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).