- Install k3d
- Create the cluster for this tutorial using
k3d cluster create python-flask --port "8080:5000@loadbalancer"
- Build the docker image:
docker build -t flask-app:latest .
- Import the image into k3d
k3d image import flask-app:latest -c python-flask
- Install the helm chart
helm install flask-app ./flask-app
- Test the app is serving
curl http://localhost:8080/hello
- Install the sealed secret operator
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system
- Create your regular secret
kubectl create secret generic flask-secret \
--from-literal=SUPER_SECRET_KEY=your-secret-value \
--dry-run=client -o yaml > secret.yaml
- Use kubeseal to encrypt it (noting the namespace)
kubeseal --format yaml \
--controller-namespace kube-system \
--controller-name sealed-secrets \
< secret.yaml > sealed-secret.yaml
- Apply the sealed secret
kubectl apply -f sealed-secret.yaml
- Reference it in your deployment
env:
- name: SUPER_SECRET_KEY
valueFrom:
secretKeyRef:
name: flask-secret
key: SUPER_SECRET_KEY
- The sealed secret is safe to commit while the secret.yml should be excluded or deleted
This guide explains how to set up and use SOPS (Secrets OPerationS) for managing encrypted secrets in a Kubernetes cluster.
- A running Kubernetes cluster (k3d in this example)
- helm
- kubectl
# Install age encryption tool
brew install age
# Install sops
brew install sops
# Generate a key pair
age-keygen -o key.txt
Create a .sops.yaml
file in your project root:
# Get your public key
PUBLIC_KEY=$(cat key.txt | grep "public key:" | cut -d " " -f 4)
# Create .sops.yaml configuration
cat > .sops.yaml << EOF
creation_rules:
- path_regex: .*\.yaml
age: ${PUBLIC_KEY}
EOF
# Add the helm repository
helm repo add sops https://isindir.github.io/sops-secrets-operator/
helm repo update
# Install the operator
helm install sops sops/sops-secrets-operator -n sops --create-namespace
# Create a secret with the age key
kubectl create secret generic sops-age \
--namespace sops \
--from-file=key.txt
# Edit the operator deployment to mount the key
kubectl edit deployment sops-sops-secrets-operator -n sops
Add these sections to the deployment:
Under spec.template.spec.containers[0]
, add:
volumeMounts:
- name: age-key
mountPath: /etc/sops/keys
readOnly: true
Under spec.template.spec
, add:
volumes:
- name: age-key
secret:
secretName: sops-age
Create a file named test-sops-secret.yaml
:
cat > test-sops-secret.yaml << EOF
apiVersion: isindir.github.com/v1alpha3
kind: SopsSecret
metadata:
name: test-sops-secret
spec:
secretTemplates:
- name: my-secret
data:
ANOTHER_SECRET_KEY: $(echo -n "testvalue" | base64)
EOF
Encrypt the secret:
sops --encrypt --in-place --encrypted-regex '^(data)$' test-sops-secret.yaml
Apply the encrypted secret:
kubectl apply -f test-sops-secret.yaml
# Check the SopsSecret status
kubectl get sopssecret test-sops-secret
# Check the created Kubernetes secret
kubectl get secret my-secret
Reference the secret in your deployment:
spec:
template:
spec:
containers:
- name: your-container
env:
- name: ANOTHER_SECRET_KEY
valueFrom:
secretKeyRef:
name: my-secret
key: ANOTHER_SECRET_KEY
.sops.yaml
- Contains only your public key configuration- Encrypted secret files (after running
sops --encrypt
) - Your Kubernetes manifests and other code
key.txt
- Contains your age private key- Any unencrypted secret files
- Any files containing raw secret values
Do not delete key.txt
! Instead:
- Add it to your
.gitignore
:echo "key.txt" >> .gitignore
- Store it securely (like in a password manager)
- Keep a secure backup
- You'll need this same key file to:
- Decrypt secrets locally
- Set up SOPS in other clusters
- Rotate or modify secrets
- SOPS secrets can be shared across clusters that share the same age key
- Always verify files are encrypted before committing
- Consider using git pre-commit hooks to prevent accidental secret commits