Skip to main content

Kubernetes module

Deploy a certificate as a kubernetes.io/tls Secret in one or more clusters. Works with in-cluster service-account auth (when CertAutoPilot itself runs on Kubernetes) or with inline kubeconfig for external clusters. Multi-cluster is supported via multiple targets.

Overview

  • Resource produced: Secret of type kubernetes.io/tls with keys tls.crt (cert + chain) and tls.key (private key). An optional ca.crt can be included if the consumer expects it separately.
  • Auth: inline kubeconfig string (stored in a module credential), or in-cluster service-account token (when use-in-cluster is checked and the backend runs on K8s).
  • Namespace: a plain literal string per target (and per secret) — no templating. For multi-tenant reuse, use separate targets or per-distribution secret overrides.
  • Rollback: conditional. Set Store content on the distribution to capture the previous Secret bytes in LastRollbackMeta; otherwise there is nothing to restore.

Prerequisites

  • A module credential holding the kubeconfig (for external clusters) or a ServiceAccount binding (for in-cluster).
  • The credential's identity must have get, create, update, patch on secrets in the target namespace. An in-cluster install using the bundled chart grants these automatically via Role + RoleBinding.
  • The namespace must exist before distribution — the module does not create namespaces.

Create a K8s target

  1. Settings → Distribution → TargetsNew. Module type: Kubernetes.

  2. Connection mode:

    • In-cluster — available only when the backend detects it is running inside Kubernetes. Uses the mounted service-account token.
    • Kubeconfig — pick a credential that stores the kubeconfig bytes. Kubeconfigs are envelope-encrypted.
  3. Namespace: a plain literal string, e.g. ingress. The module does no namespace templating — default_namespace and each secret's namespace are used verbatim. For multi-tenant reuse, create a separate target per tenant or override the secrets per distribution.

  4. Secrets: optional. Leave the list empty and each certificate deploying through the target gets its own auto-created Secret named cap-<token>-<domain> (token: 8-char hash derived from the certificate, stable across renewals) in the Default Namespace — recommended for multi-certificate targets. Add explicit {namespace, name} entries only to pin names (e.g. ingress-nginx-tls expected by an existing Ingress). Per certificate, the secret list (and the restart-target list) can also be overridden from the certificate's Distributions → Overrides drawer; an override replaces both lists entirely. :::note Behavior change Restart-only targets (no secrets configured) now also create the auto-generated per-certificate Secret.

    Auto-created Secrets are not garbage-collected: deleting the certificate in CertAutoPilot leaves the cap-<token>-<domain> Secret in the cluster (the platform never deletes cluster resources). Remove it with kubectl delete secret when decommissioning a certificate. :::

  5. Include ca.crt: leave OFF unless your consumer explicitly reads ca.crt from the Secret.

  6. Store content for rollback: ON if you need the Rollback button to restore the previous Secret. OFF keeps the rollback metadata empty and the button greyed-out.

  7. Save → health check reads the namespace.

Execute

  1. Attach a Kubernetes distribution to a cert.
  2. Dry-run prints the diff between the live Secret and the one we would write (or would create if it does not exist).
  3. Execute writes via Update (server-side) — no patch gymnastics, the Secret is replaced. Any consumer that mounts the Secret as a Volume re-reads it on kubelet's sync interval (~60s); consumers using env vars require a pod restart.
Pod restart not automatic

CertAutoPilot writes the Secret but does not restart the consuming pods. For cert-auto-reload behaviour, use a sidecar like secretgen-controller or annotate your Deployment with a checksum/tls pattern so a kubectl rollout restart hook picks up the change. Webhook distributions can be stacked after the K8s one to fire the rollout.

Rollback

When Store content is ON, the module stashes the previous tls.crt / tls.key into the rollback metadata before overwriting. Rollback re-applies that snapshot. When the toggle is OFF, rollback is unavailable for this distribution — the UI surfaces this as a greyed-out button with a tooltip.

Multi-cluster deployments

Create one target per cluster (each with its own kubeconfig credential) and group them. Attaching a single distribution to the group fans out across every cluster. Fan-out follows the same threshold / batch-size settings as the SSH module.

Minimum RBAC for an in-cluster install

The Helm chart ships with this Role; if you run custom RBAC, mirror it:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: certautopilot-distributor
namespace: <target-ns>
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "update", "patch"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get"]

Bind it to the ServiceAccount the backend pod uses.

Troubleshooting

Fails with "forbidden: secrets cannot be updated"

ServiceAccount lacks update verb in the target namespace. Fix the RoleBinding.

"namespaces \"X\" not found"

The module does not create namespaces. Create the namespace first, then retry.

Ingress still serves the old cert

Ingress controllers cache certs. Check the controller's reload story — nginx-ingress reloads on Secret watch (fast), HAProxy Ingress reconciles on a timer. If in doubt, restart the controller pods; the Secret is already updated.

See also