- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
Sealed Secrets lösen das Problem, dass Kubernetes Secrets nur base64-kodiert und nicht verschlüsselt sind. Mit dem Controller im Cluster und dem kubeseal-CLI verschlüsselst du Secrets lokal — nur der Controller kann sie entschlüsseln. So landen Secrets sicher in Git.
Sealed Secrets: Secrets sicher in Git speichern
Wer GitOps macht, steht vor einem Problem: Kubernetes Secrets sind nur base64-kodiert. Wer base64 -d kennt, kann sie lesen. Secrets einfach ins Git-Repo committen? Keine Option.
Sealed Secrets von Bitnami lösen genau das. Der Ablauf:
┌─────────────┐ kubeseal ┌──────────────┐ Git Push ┌─────────┐
│ Secret YAML │───────────────▶│ SealedSecret │───────────────▶│ Git │
│ (Klartext) │ verschlüsselt│ (verschlüss.)│ │ Repo │
└─────────────┘ └──────────────┘ └────┬────┘
│
┌──────────────┐ entschlüsselt ┌──────────▼──┐
│ Secret │◀────────────────────│ Controller │
│ (im Cluster) │ │ (Cluster) │
└──────────────┘ └─────────────┘
Du verschlüsselst lokal mit kubeseal, pushst die SealedSecret-YAML ins Repo, und der Controller im Cluster entschlüsselt sie automatisch zu einem normalen Secret.
Installation
Zwei Teile: Der Controller im Cluster und das CLI-Tool auf deinem Rechner.
Controller installieren
# Per Helm (empfohlen)
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
kubectl create namespace sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace sealed-secrets \
--set fullnameOverride=sealed-secrets-controller
# Prüfen, ob der Controller läuft
kubectl get pods -n sealed-secrets
kubectl get svc -n sealed-secrets
kubeseal CLI installieren
# macOS
brew install kubeseal
# Linux
KUBESEAL_VERSION=0.27.3
curl -LO "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz"
tar -xzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz
sudo mv kubeseal /usr/local/bin/
# Version prüfen
kubeseal --version
Erstes Secret verschlüsseln
Angenommen, du brauchst ein Datenbank-Passwort als Secret:
Schritt 1: Secret YAML erstellen (nicht committen!)
kubectl create secret generic db-credentials \
--from-literal=username=dbadmin \
--from-literal=password=S3cur3-P4ssw0rd \
--namespace production \
--dry-run=client -o yaml > /tmp/db-secret.yaml
Die Datei unter /tmp/ erstellen — sie enthält das Klartext-Secret und darf nie in Git landen.
Schritt 2: Mit kubeseal verschlüsseln
kubeseal --format yaml \
--controller-name sealed-secrets-controller \
--controller-namespace sealed-secrets \
< /tmp/db-secret.yaml > sealed-db-credentials.yaml
Schritt 3: SealedSecret prüfen und committen
# Inhalt ansehen — komplett verschlüsselt
cat sealed-db-credentials.yaml
Die Ausgabe sieht so aus:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
password: AgBy8h3kF...langer-verschlüsselter-String...
username: AgCtr9s2P...langer-verschlüsselter-String...
template:
metadata:
name: db-credentials
namespace: production
Diese Datei kannst du bedenkenlos committen:
git add sealed-db-credentials.yaml
git commit -m "Add sealed database credentials"
git push
Schritt 4: Im Cluster anwenden
kubectl apply -f sealed-db-credentials.yaml
# Der Controller erstellt automatisch das echte Secret
kubectl get secret db-credentials -n production
kubectl get secret db-credentials -n production -o jsonpath='{.data.password}' | base64 -d
Verschlüsselungs-Scopes
Sealed Secrets unterstützen drei Scopes, die bestimmen, wo ein Secret entschlüsselt werden darf:
| Scope | Flag | Beschreibung |
|---|---|---|
strict (Standard) | --scope strict | Secret ist an Name UND Namespace gebunden |
namespace-wide | --scope namespace-wide | Secret kann im Namespace umbenannt werden |
cluster-wide | --scope cluster-wide | Secret funktioniert in jedem Namespace |
Für die meisten Fälle ist strict richtig. Nur wenn du Secrets dynamisch benennen musst:
# Namespace-wide Scope
kubeseal --format yaml --scope namespace-wide \
--controller-name sealed-secrets-controller \
--controller-namespace sealed-secrets \
< /tmp/db-secret.yaml > sealed-db-credentials.yaml
Key-Management und Rotation
Der Controller generiert beim Start ein Schlüsselpaar. Diesen Schlüssel musst du sichern — ohne ihn sind alle SealedSecrets verloren.
Schlüssel sichern
# Alle Sealing-Keys exportieren
kubectl get secret -n sealed-secrets \
-l sealedsecrets.bitnami.com/sealed-secrets-key \
-o yaml > /safe/location/sealed-secrets-master-key.yaml
Diese Datei gehört an einen sicheren Ort (Vault, verschlüsselter USB-Stick, KMS) — niemals in Git.
Schlüssel wiederherstellen (Disaster Recovery)
# Vor der Neuinstallation: Key wiederherstellen
kubectl apply -f /safe/location/sealed-secrets-master-key.yaml
# Dann Controller neu installieren
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace sealed-secrets \
--set fullnameOverride=sealed-secrets-controller
Key Rotation
Der Controller erstellt standardmäßig alle 30 Tage einen neuen Key. Alte Keys bleiben erhalten, damit bestehende SealedSecrets weiter entschlüsselt werden können.
Um SealedSecrets mit dem neuesten Key neu zu verschlüsseln:
# Alle SealedSecrets im Repo neu verschlüsseln
kubeseal --re-encrypt \
--controller-name sealed-secrets-controller \
--controller-namespace sealed-secrets \
< sealed-db-credentials.yaml > sealed-db-credentials-rotated.yaml
mv sealed-db-credentials-rotated.yaml sealed-db-credentials.yaml
GitOps-Integration mit ArgoCD
Wenn du ArgoCD nutzt, funktionieren SealedSecrets nahtlos. ArgoCD sieht die SealedSecret-Ressource im Git-Repo, wendet sie an, und der Controller erstellt das Secret:
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/dein-org/k8s-manifests.git
path: apps/production
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
selfHeal: true
Dein Repository-Layout könnte so aussehen:
k8s-manifests/
├── apps/
│ └── production/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── sealed-db-credentials.yaml # verschlüsselt, sicher in Git
Troubleshooting
SealedSecret wird nicht entschlüsselt:
# Events des SealedSecret prüfen
kubectl describe sealedsecret db-credentials -n production
# Controller-Logs
kubectl logs -n sealed-secrets deployment/sealed-secrets-controller
Häufigste Ursache: Der Namespace in der SealedSecret stimmt nicht mit dem Namespace überein, in dem sie applied wird (bei strict Scope).
"certificate signed by unknown authority":
# Zertifikat explizit vom Controller holen
kubeseal --fetch-cert \
--controller-name sealed-secrets-controller \
--controller-namespace sealed-secrets > /tmp/sealed-secrets-cert.pem
# Dann mit Zertifikat verschlüsseln
kubeseal --format yaml --cert /tmp/sealed-secrets-cert.pem \
< /tmp/db-secret.yaml > sealed-db-credentials.yaml
FAQ
Was passiert, wenn der Controller gelöscht wird?
Bestehende Secrets im Cluster bleiben erhalten — sie sind normale Kubernetes Secrets. Aber neue SealedSecrets werden nicht mehr entschlüsselt. Deshalb: Sealing-Keys immer sichern.
Kann ich SealedSecrets offline erstellen?
Ja. Exportiere das Public-Key-Zertifikat mit kubeseal --fetch-cert und nutze es mit --cert Flag. So brauchst du keinen direkten Cluster-Zugriff zum Verschlüsseln.
Wie unterscheiden sich Sealed Secrets von SOPS oder External Secrets?
Sealed Secrets verschlüsseln direkt in Kubernetes-Ressourcen und brauchen keinen externen Key-Store. SOPS verschlüsselt beliebige Dateien und braucht KMS/AGE-Keys. External Secrets Operator synchronisiert aus Vault/AWS SecretsManager. Für reine GitOps-Workflows ohne externe Infrastruktur sind Sealed Secrets am einfachsten.
Muss ich alle Secrets neu verschlüsseln, wenn der Key rotiert?
Nein, alte Keys werden behalten. Aber es ist Best Practice, SealedSecrets periodisch mit kubeseal --re-encrypt zu aktualisieren und die ältesten Keys dann zu entfernen.
Nächster Schritt: Kombiniere Sealed Secrets mit ArgoCD für einen vollständigen GitOps-Workflow, bei dem auch Secrets automatisch deployed werden.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
GitOps Secrets: SOPS und Sealed Secrets im Vergleich
Secrets sicher in Git verwalten mit Sealed Secrets, Mozilla SOPS und External Secrets Operator. Praxisvergleich für GitOps-Workflows.
External Secrets: Kubernetes-Secrets sicher verwalten
Der External Secrets Operator synchronisiert Secrets aus AWS, Vault und Azure Key Vault nach Kubernetes. Anleitung mit Praxisbeispielen.
Kubernetes Immutable Infrastructure: Read-Only Container
Immutable Infrastructure in Kubernetes umsetzen: Read-Only Root Filesystem, Distroless Images und GitOps-only Changes für maximale Container-Sicherheit.
Kubernetes Secrets mit HashiCorp Vault verwalten
HashiCorp Vault als zentralen Secrets Store für Kubernetes einrichten mit External Secrets Operator, Vault Agent und praxisnahen YAML-Beispielen.
GitOps Security: Supply Chain mit ArgoCD und Cosign sichern
GitOps-Pipelines für Kubernetes absichern: Image Signing mit Cosign, Policy Enforcement mit OPA Gatekeeper und Drift Detection in ArgoCD einrichten.