Skip to main content

Sealed Secrets

Purpose: For platform engineers, shows how to set up Sealed Secrets controller and use seal/unseal workflow as an alternative to SOPS.

What Sealed Secrets Does

Sealed Secrets encrypts Kubernetes Secret manifests so they can be safely stored in Git. It uses asymmetric encryption: a public key (available to anyone) encrypts secrets into SealedSecret resources, and a private key (held only by the controller running in the cluster) decrypts them. This is an alternative to SOPS for teams that prefer a Kubernetes-native encryption workflow.

openCenter uses SOPS as the primary secrets encryption method. Sealed Secrets is available as an optional alternative for clusters where SOPS is not suitable.

How It's Deployed

Sealed Secrets is deployed via FluxCD from openCenter-gitops-base:

openCenter-gitops-base/applications/base/services/sealed-secrets/
├── namespace.yaml
├── source.yaml
├── helmrelease.yaml
└── helm-values/
└── hardened-values.yaml

Customer overlay:

applications/overlays/<cluster>/services/sealed-secrets/
├── kustomization.yaml
└── override-values.yaml

How It Works

  1. The Sealed Secrets controller generates an RSA key pair on first startup. The private key stays in the cluster; the public key is exported for use by developers.
  2. Developers use the kubeseal CLI to encrypt a standard Kubernetes Secret into a SealedSecret using the public key.
  3. The SealedSecret manifest is committed to Git (safe — it can only be decrypted by the cluster's private key).
  4. FluxCD applies the SealedSecret to the cluster.
  5. The controller decrypts it and creates the corresponding Kubernetes Secret.

Key Configuration

Fetching the Public Key

After the controller is deployed, fetch the public key for your cluster:

kubeseal --fetch-cert \
--controller-name=sealed-secrets \
--controller-namespace=sealed-secrets \
> pub-cert.pem

Distribute this certificate to developers who need to seal secrets for this cluster.

Sealing a Secret

Create a standard Kubernetes Secret manifest, then seal it:

# Create a regular secret (do NOT commit this)
kubectl create secret generic db-creds \
--from-literal=username=admin \
--from-literal=password=s3cret \
--dry-run=client -o yaml > secret.yaml

# Seal it with the cluster's public key
kubeseal --format=yaml --cert=pub-cert.pem < secret.yaml > sealed-secret.yaml

# Commit the sealed version (safe for Git)
rm secret.yaml
git add sealed-secret.yaml

The resulting SealedSecret looks like:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-creds
namespace: default
spec:
encryptedData:
username: AgBy3i... # Encrypted with cluster's public key
password: AgCtr8...

Scope

SealedSecrets are scoped by default — a sealed secret for namespace production with name db-creds can only be decrypted into that exact namespace and name. This prevents someone from copying a SealedSecret to a different namespace.

SOPS vs. Sealed Secrets

AspectSOPS (openCenter default)Sealed Secrets
EncryptionSymmetric (Age keys)Asymmetric (RSA)
Key managementAge keys in secrets/age/Controller manages keys
Toolingsops CLIkubeseal CLI
FluxCD integrationNative (decryption.provider: sops)Via SealedSecret CRD
Key rotationManual via opencenter cluster rotate-keysController auto-rotates (30 days default)

Use SOPS when you want unified key management across the GitOps workflow. Use Sealed Secrets when you prefer a Kubernetes-native approach or need to delegate secret creation to teams without access to SOPS keys.

Verification

# Check Sealed Secrets controller
kubectl get pods -n sealed-secrets

# Verify a SealedSecret was decrypted
kubectl get secret db-creds -n default

# Check controller logs for decryption errors
kubectl logs -n sealed-secrets -l app.kubernetes.io/name=sealed-secrets

Common Customizations

  • Key rotation interval: The controller rotates its key pair every 30 days by default. Adjust with --key-renew-period flag.
  • Backup sealing keys: Export the controller's private key for disaster recovery: kubectl get secret -n sealed-secrets -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > sealed-secrets-key-backup.yaml. Store this securely outside the cluster.
  • Cluster-wide scope: Use kubeseal --scope cluster-wide to create secrets that can be decrypted in any namespace (less restrictive).