Introduction to Sealed Secrets #
One of the biggest challenges in GitOps is managing secrets securely. While we can store all our Kubernetes manifests in Git repositories, we cannot store plain-text secrets there due to security concerns. This creates a gap in our “everything in Git” philosophy of GitOps.
Sealed Secrets, developed by Bitnami, solves this problem by providing a way to encrypt secrets that can be safely stored in Git repositories and automatically decrypted by your Kubernetes cluster.
The Secret Management Problem in GitOps #
In traditional GitOps workflows, you might have configuration like this:
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
data:
username: YWRtaW4= # base64 encoded 'admin'
password: UGFzc3dvcmQxMjM= # base64 encoded 'Password123'
The problem is that base64 encoding is not encryption - anyone with access to your Git repository can easily decode these values. This means you either:
- Store secrets outside Git - breaking GitOps principles
- Use a separate secret management tool - adding complexity
- Accept the security risk - not recommended
How Sealed Secrets Works #
Sealed Secrets introduces two key components:
- Controller: Runs in your cluster and holds a private key
- kubeseal CLI: Encrypts secrets using the controller’s public key
Here’s the workflow:
- You create a regular Secret manifest
- Use
kubeseal
to encrypt it into a SealedSecret - Store the SealedSecret in Git (it’s safe - only your cluster can decrypt it)
- The controller watches for SealedSecrets and automatically creates the corresponding Secret resources
The encrypted SealedSecret looks like this:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: database-credentials
spec:
encryptedData:
username: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEQAx...
password: AgAKAoiQm0XBmKUCOTfdPGdKnVL4n4OTU...
template:
metadata:
name: database-credentials
Why Sealed Secrets is Perfect for GitOps #
Sealed Secrets aligns perfectly with GitOps principles:
- Everything in Git: Both application config and secrets are version controlled
- Declarative: SealedSecrets are Kubernetes resources like any other
- Immutable: Changes require creating new SealedSecrets (proper audit trail)
- Secure: Only the target cluster can decrypt the secrets
- Simple: No external dependencies or complex PKI infrastructure
The controller automatically handles the lifecycle - when you update a SealedSecret in Git, Flux detects the change and applies it, the controller decrypts it and updates the corresponding Secret.
Key Concepts #
SealedSecret Resource #
A SealedSecret is a Kubernetes Custom Resource that contains encrypted data:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: my-secret
namespace: default
spec:
encryptedData:
key1: encrypted_value_1
key2: encrypted_value_2
template:
metadata:
name: my-secret
labels:
app: myapp
Scoping #
Sealed Secrets supports different scoping levels:
- strict (default): Secret can only be unsealed in the same namespace and with the same name
- namespace-wide: Secret can be unsealed in the same namespace with any name
- cluster-wide: Secret can be unsealed in any namespace with any name
Key Rotation #
The controller automatically handles key rotation to maintain security while ensuring existing SealedSecrets continue to work.
Exercises #
Install Sealed Secrets Controller with Flux #
Objective: Install the Sealed Secrets controller in your cluster using Flux to manage it via GitOps.
Steps:
- Create a new directory in your GitOps repository for sealed secrets:
mkdir -p clusters/c0x/sealed-secrets
- Download the sealed-secrets installation manifest from the releases page and place it in the directory
- Commit and push the files to your GitOps repository
- Verify the controller is installed:
kubectl get pods -n kube-system | grep sealed-secrets
Expected Result: The sealed-secrets-controller pod should be running in the kube-system namespace.
Create and Deploy a Sealed Secret #
Objective: Create a sealed secret for a database connection and deploy it using GitOps.
Steps:
-
If you haven’t installed the
kubeseal
tool already, head over to the essential tools article for installation instructions. -
Create a regular Secret manifest
# temp-secret.yaml apiVersion: v1 kind: Secret metadata: name: database-credentials namespace: default data: username: YWRtaW4= # admin password: UGFzc3dvcmQxMjM= # Password123
β οΈ This file should not be committed to your git repo. Never commit a
Secret
to your git repository.
- Encrypt the secret using
kubeseal
:kubeseal -f temp-secret.yaml -w clusters/c0x/database-sealed-secret.yaml
- Delete the temporary unencrypted file:
rm temp-secret.yaml
- Examine the generated
SealedSecret
:cat clusters/c0x/database-sealed-secret.yaml
- Commit and push the
SealedSecret
to your repository - Verify the secret was created in your cluster:
kubectl get sealedsecrets kubectl get secrets kubectl get secret database-credentials -o yaml
Expected Result:
- A SealedSecret should exist in your cluster
- A corresponding Secret should be automatically created
- The Secret should contain the correct decrypted values
Troubleshooting #
Common commands for debugging Sealed Secrets issues:
# Check controller status
kubectl get pods -n kube-system | grep sealed-secrets
kubectl logs -n kube-system -l name=sealed-secrets-controller
# Verify SealedSecret resources
kubectl get sealedsecrets
kubectl describe sealedsecret <name>
# Check if secrets are being created
kubectl get secrets
kubectl describe secret <name>
# Test encryption manually
kubeseal --fetch-cert > cert.pem
echo -n "test" | kubeseal --raw --from-file=/dev/stdin --name myname --namespace default