Sealed secrets

Encrypt secrets with kubeseal, commit them safely to git, and reference them from deployments.

Bitnami Sealed Secrets lets you commit encrypted secrets to git. kdef gives them a first-class block: no YAML gymnastics, no wrapping kubeseal in shell scripts.

Prerequisites

  • kubeseal installed locally
  • Sealed-secrets controller running in the cluster
# macOS
brew install kubeseal

# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/kubeseal-linux-amd64
chmod +x kubeseal-linux-amd64 && sudo mv kubeseal-linux-amd64 /usr/local/bin/kubeseal

Seal a single value

kdef seal --secret db-credentials --namespace production --key DATABASE_URL \
  --value "postgres://api:hunter2@db:5432/api"

Output (the ciphertext on stdout, the usage hint on stderr):

AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...

Usage in .kdef:
  sealedsecret "db-credentials" {
    namespace = "production"
    data = {
      DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq..."
    }
  }

Or pipe from stdin so the plaintext never touches your shell history:

echo -n "hunter2" | kdef seal --secret db-credentials --namespace production --key PASSWORD

Seal an existing Secret

If you already have a live Kubernetes Secret, seal-secret turns the whole thing into a ready-to-commit block:

kdef seal-secret --name db-credentials --namespace production

Output:

sealedsecret "db-credentials" {
  namespace = "production"
  data = {
    DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO..."
    READONLY_URL = "AgBz2j5PKTXL+QjUzTZaaB0sP..."
  }
}

Drop that into secrets.kdef and commit it.

Use it in a deployment

The secret() function works the same for plain secret blocks and sealedsecret blocks: you reference by name, not by storage mechanism.

# secrets.kdef
sealedsecret "db-credentials" {
  namespace = "production"
  data = {
    DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO..."
  }
}
# api.kdef
deployment "api" {
  namespace = "production"

  container "api" {
    image = image("api")
    env {
      DATABASE_URL = secret("db-credentials", "DATABASE_URL")
    }
  }
}

At deploy time the controller decrypts the SealedSecret into a regular Secret, and the pod reads from it via valueFrom.secretKeyRef.

Rotating a value

To rotate a key, re-seal with the new value and replace the ciphertext:

kdef seal --secret db-credentials --namespace production --key DATABASE_URL \
  --value "postgres://api:new-password@db:5432/api"

Paste the new ciphertext into secrets.kdef, commit, and let your GitOps tool apply it.

Tips

  • Different clusters have different sealing keys. A SealedSecret encrypted for staging won't decrypt in production. Re-seal per target cluster.
  • The ciphertext is also bound to the Secret's name and namespace. --namespace defaults to default, so pass the namespace your sealedsecret block lives in.
  • If you run sealed-secrets in a non-default namespace or with a non-default controller name, pass --controller-namespace and --controller-name to kdef seal (and kdef seal-secret).
  • For local development you usually want plain secret blocks: they don't need kubeseal and render as normal v1/Secret manifests.