Kubernetes Secrets ohne Verschlüsselung in Git-Repositories zu speichern ist einer der häufigsten Sicherheitsfehler bei Cloud-nativen Deployments. Obwohl Kubernetes Secrets Base64-Kodierung verwenden, bietet dies keinerlei kryptographischen Schutz — jeder mit Repository-Zugriff kann sie sofort dekodieren. SOPS (Secrets OPerationS) in Kombination mit Age-Verschlüsselung löst dieses Problem elegant und ermöglicht es, verschlüsselte Secrets direkt in Git zu speichern, bei voller Kompatibilität mit GitOps-Workflows wie ArgoCD und FluxCD.

Voraussetzungen

  • Ein laufender Kubernetes-Cluster (v1.24+) mit konfiguriertem kubectl
  • sops v3.8+ installiert
  • age v1.1+ installiert
  • Grundkenntnisse über Kubernetes Secrets und YAML-Manifeste
  • Git-Repository für Ihre Kubernetes-Manifeste
  • Optional: ArgoCD oder FluxCD für GitOps-Integration

Risiken von Kubernetes Secrets Verstehen

Kubernetes Secrets werden oft als sicherer Speichermechanismus missverstanden. In Wirklichkeit haben sie erhebliche Einschränkungen, die Sie verstehen müssen, bevor Sie Ihre Secrets-Strategie entwerfen.

Base64 Ist Keine Verschlüsselung

Ein Standard-Kubernetes-Secret speichert Werte als Base64-kodierte Strings:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=
  password: cDRzc3cwcmQxMjM=

Das Dekodieren dieser Werte ist trivial:

echo "cDRzc3cwcmQxMjM=" | base64 -d
# Ausgabe: p4ssw0rd123

Jeder, der Ihr Repository klont oder Lesezugriff auf etcd erhält, kann jedes Secret in Ihrem Cluster extrahieren.

Bedenken zur etcd-Speicherung

Standardmäßig speichert Kubernetes Secrets unverschlüsselt in etcd. Obwohl Sie die Verschlüsselung im Ruhezustand mit EncryptionConfiguration aktivieren können, schützt dies nur die etcd-Datendateien — nicht die API-Server-Antworten oder die in Git gespeicherten Manifeste. Sie brauchen eine Lösung, die Secrets verschlüsselt, bevor sie in Ihr Versionskontrollsystem gelangen.

Das Git-Problem

GitOps erfordert, dass der gewünschte Cluster-Zustand in Git lebt. Aber Secrets im Klartext in Git zu committen bedeutet:

  • Jeder Entwickler mit Repository-Zugriff sieht Produktions-Credentials
  • Die Secret-Historie bleibt für immer in Git bestehen, auch nach dem Löschen
  • Geleakte Repositories legen jedes jemals committete Secret offen
  • Compliance-Frameworks (SOC 2, PCI-DSS) verbieten Klartext-Credentials in der Versionskontrolle

SOPS und Age Installieren

Linux

# Age installieren
sudo apt-get install age
# Oder von GitHub-Releases
curl -LO https://github.com/FiloSottile/age/releases/download/v1.2.0/age-v1.2.0-linux-amd64.tar.gz
tar xzf age-v1.2.0-linux-amd64.tar.gz
sudo mv age/age age/age-keygen /usr/local/bin/

# SOPS installieren
curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
sudo mv sops-v3.9.4.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops

macOS

brew install sops age

Überprüfen Sie beide Installationen:

sops --version
# sops 3.9.4
age --version
# v1.2.0

Secrets mit SOPS und Age Verschlüsseln

Age-Schlüsselpaar Generieren

age-keygen -o age-key.txt
# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Speichern Sie den privaten Schlüssel sicher. Der öffentliche Schlüssel kann bedenkenlos geteilt und in Ihr Repository committet werden:

# Schlüssel dort speichern, wo SOPS ihn finden kann
mkdir -p ~/.config/sops/age
mv age-key.txt ~/.config/sops/age/keys.txt

# Oder die Umgebungsvariable setzen
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt

.sops.yaml-Konfiguration Erstellen

Erstellen Sie eine .sops.yaml-Datei im Root Ihres Repositories, um Verschlüsselungsregeln zu definieren:

creation_rules:
  # Alle Dateien im secrets/-Verzeichnis verschlüsseln
  - path_regex: secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

  # Staging-Secrets mit anderem Schlüssel verschlüsseln
  - path_regex: envs/staging/secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,
      age1lzd99uca0lqtmgahfmxj4gvr2fcswcaxmxnz30fwcmm22hjvrzrqsnqxsl

  # Anderer Schlüssel für Produktion
  - path_regex: envs/production/secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,
      age1an5quvgyl8uny5mkmgkzpnpe9wuufrl2vvmqsht4xp3k96s608q0eamcl

Mehrere Empfänger (kommagetrennt) ermöglichen Team-Zugriff — jeder aufgelistete Schlüssel kann die Datei entschlüsseln.

Ein Kubernetes Secret Verschlüsseln

Beginnen Sie mit Ihrem Klartext-Secret-Manifest:

# secrets/db-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
stringData:
  username: admin
  password: "s3cur3-pr0d-p@ssw0rd"
  connection-string: "postgresql://admin:s3cur3-pr0d-p@ssw0rd@db.internal:5432/myapp"

Verschlüsseln Sie es mit SOPS:

sops --encrypt --in-place secrets/db-credentials.yaml

Die verschlüsselte Datei behält die YAML-Struktur bei, verschlüsselt aber die Werte:

apiVersion: v1
kind: Secret
metadata:
    name: db-credentials
    namespace: production
type: Opaque
stringData:
    username: ENC[AES256_GCM,data:k8mN3w==,iv:abc...,tag:def...,type:str]
    password: ENC[AES256_GCM,data:dGhpcyBpcyBl...,iv:ghi...,tag:jkl...,type:str]
    connection-string: ENC[AES256_GCM,data:bG9uZ2VyIHN0cmluZw==...,iv:mno...,tag:pqr...,type:str]
sops:
    age:
        - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            ...
            -----END AGE ENCRYPTED FILE-----
    lastmodified: "2026-02-17T10:00:00Z"
    version: 3.9.4

Schlüssel (apiVersion, kind, metadata) bleiben lesbar, während Werte verschlüsselt sind. Das bedeutet, dass git diff zeigt, welche Secrets sich geändert haben, ohne die tatsächlichen Werte zu verraten.

Entschlüsseln und Anwenden

# Entschlüsseln und in einem Befehl anwenden
sops --decrypt secrets/db-credentials.yaml | kubectl apply -f -

# Oder in eine temporäre Datei entschlüsseln
sops --decrypt secrets/db-credentials.yaml > /tmp/secret.yaml
kubectl apply -f /tmp/secret.yaml
rm -f /tmp/secret.yaml

Integration mit GitOps-Workflows

ArgoCD mit KSOPS

KSOPS ist ein Kustomize-Plugin, das SOPS-verschlüsselte Dateien während ArgoCD-Sync-Operationen entschlüsselt.

Installieren Sie KSOPS in Ihrem ArgoCD-Repo-Server:

# argocd-repo-server Patch
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
  namespace: argocd
spec:
  template:
    spec:
      containers:
        - name: argocd-repo-server
          env:
            - name: SOPS_AGE_KEY
              valueFrom:
                secretKeyRef:
                  name: sops-age-key
                  key: age-key.txt
            - name: XDG_CONFIG_HOME
              value: /.config
          volumeMounts:
            - mountPath: /.config/kustomize/plugin/viaduct.ai/v1/ksops
              name: custom-tools
      initContainers:
        - name: install-ksops
          image: viaductoss/ksops:v4.3.2
          command: ["/bin/sh", "-c"]
          args:
            - cp /usr/local/bin/ksops /.config/kustomize/plugin/viaduct.ai/v1/ksops/ksops
          volumeMounts:
            - mountPath: /.config/kustomize/plugin/viaduct.ai/v1/ksops
              name: custom-tools
      volumes:
        - name: custom-tools
          emptyDir: {}

Erstellen Sie einen KSOPS-Generator in Ihrem Kustomize-Overlay:

# secret-generator.yaml
apiVersion: viaduct.ai/v1
kind: ksops
metadata:
  name: secret-generator
files:
  - secrets/db-credentials.yaml
  - secrets/api-keys.yaml

Native FluxCD-Integration

FluxCD hat eingebaute SOPS-Unterstützung. Erstellen Sie ein Entschlüsselungs-Secret und konfigurieren Sie Ihre Kustomization:

# Age-Schlüssel-Secret im flux-system Namespace erstellen
kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=~/.config/sops/age/keys.txt
# flux-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  path: ./envs/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: my-app
  decryption:
    provider: sops
    secretRef:
      name: sops-age

FluxCD entschlüsselt automatisch alle SOPS-verschlüsselten Dateien im angegebenen Pfad während der Reconciliation.

SOPS+Age vs Sealed Secrets vs Vault vs External Secrets

EigenschaftSOPS + AgeSealed SecretsHashiCorp VaultExternal Secrets
VerschlüsselungsortClient-seitigController-seitigServer-seitigServer-seitig
Git-kompatibelJa (verschlüsseltes YAML)Ja (Custom Resource)Nein (nur Referenzen)Nein (nur Referenzen)
Infrastruktur nötigKeineCluster-ControllerVault-ServerOperator + Backend
SchlüsselverwaltungAge-SchlüsseldateienCluster-ZertifikatVault-PoliciesBackend-abhängig
Multi-Umgebung.sops.yaml-RegelnZertifikate pro ClusterNamespaces/PoliciesMehrere Stores
GitOps-IntegrationArgoCD/Flux-PluginsNatives K8sCSI-Driver/InjectorOperator-Sync
RotationNeu verschlüsseln mit SOPSNeu versiegeln nötigDynamische SecretsBackend-abhängig
KomplexitätNiedrigNiedrigHochMittel
Offline-fähigJaNeinNeinNein
Ideal fürKleine-mittlere TeamsEinzelne ClusterEnterprise/ComplianceMulti-Cloud

SOPS+Age glänzt, wenn Sie Einfachheit, Offline-Fähigkeit und echtes GitOps ohne zusätzliche Infrastruktur wollen.

Praxisszenario

Sie verwalten ein Multi-Umgebungs-Kubernetes-Deployment über Entwicklungs-, Staging- und Produktions-Cluster. Ihr Team von acht Ingenieuren nutzt ArgoCD für GitOps, und Sie brauchen eine Secrets-Lösung, die:

  • Entwicklern ermöglicht, Secrets ohne Cluster-Zugriff zu erstellen und zu aktualisieren
  • Verschlüsselte Secrets in Git für die Nachvollziehbarkeit aufbewahrt
  • Verschiedene Verschlüsselungsschlüssel pro Umgebung verwendet
  • Schlüsselrotation ohne Neuverteilung jedes Secrets erlaubt

So würden Sie Ihr Repository strukturieren:

k8s-manifests/
├── .sops.yaml
├── base/
│   ├── deployment.yaml
│   └── service.yaml
├── envs/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   ├── secret-generator.yaml
│   │   └── secrets/
│   │       └── app-secrets.yaml    # mit Dev-Schlüssel verschlüsselt
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   ├── secret-generator.yaml
│   │   └── secrets/
│   │       └── app-secrets.yaml    # mit Staging-Schlüssel verschlüsselt
│   └── production/
│       ├── kustomization.yaml
│       ├── secret-generator.yaml
│       └── secrets/
│           └── app-secrets.yaml    # mit Prod-Schlüssel verschlüsselt

Jede Umgebung hat ihr eigenes Age-Schlüsselpaar. Entwickler halten den Dev-Schlüssel, Teamleiter halten Dev+Staging, und nur die CI/CD-Pipeline hält den Produktionsschlüssel. Die .sops.yaml-Datei leitet die Verschlüsselung basierend auf dem Dateipfad zum richtigen Schlüssel.

Fallstricke und Grenzfälle

Schlüsselrotation erfordert Neuverschlüsselung. Wenn Sie einen Age-Schlüssel rotieren, müssen Sie jede Datei mit dem alten Schlüssel entschlüsseln und mit dem neuen neu verschlüsseln. SOPS bietet sops updatekeys dafür, aber testen Sie es zuerst:

# Schlüssel für eine einzelne Datei aktualisieren (nutzt .sops.yaml-Regeln)
sops updatekeys secrets/db-credentials.yaml

# Alle Secrets im Batch neu verschlüsseln
find . -name "*.yaml" -path "*/secrets/*" -exec sops updatekeys {} \;

Teilweise Verschlüsselung mit encrypted_regex. Standardmäßig verschlüsselt SOPS alle Werte. Verwenden Sie encrypted_regex in .sops.yaml, um nur bestimmte Schlüssel zu verschlüsseln:

creation_rules:
  - path_regex: secrets/.*\.yaml$
    encrypted_regex: "^(data|stringData)$"
    age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Binäre Secrets benötigen zuerst Base64-Kodierung. SOPS arbeitet mit Textdateien. Für binäre Secrets (TLS-Zertifikate, Keystores) kodieren Sie sie vor der Verschlüsselung in Base64:

# Zertifikat kodieren, dann SOPS den Base64-String verschlüsseln lassen
cat tls.crt | base64 -w0 > tls.crt.b64

Committen Sie niemals den privaten Age-Schlüssel. Fügen Sie ihn sofort zur .gitignore hinzu:

echo "age-key.txt" >> .gitignore
echo "*.agekey" >> .gitignore

Der SOPS-Metadatenbereich wächst mit den Empfängern. Jeder zusätzliche Age-Empfänger fügt ~300 Bytes Metadaten hinzu. Bei vielen Empfängern erwägen Sie, einen gemeinsamen Team-Schlüssel über einen sicheren Kanal zu verteilen, anstatt individuelle Schlüssel zu verwenden.

Fehlerbehebung

Fehler: could not find common decryption key Der private Age-Schlüssel ist nicht verfügbar. Überprüfen Sie den Speicherort der Schlüsseldatei:

# Prüfen ob die Schlüsseldatei existiert
ls -la ~/.config/sops/age/keys.txt

# Oder die Umgebungsvariable prüfen
echo $SOPS_AGE_KEY_FILE

# Prüfen ob der Schlüssel zum Empfänger passt
grep "public key" ~/.config/sops/age/keys.txt

Fehler: failed to decrypt Die Datei wurde mit einem anderen Age-Schlüssel verschlüsselt. Prüfen Sie, welcher Schlüssel verwendet wurde:

sops --decrypt --verbose secrets/db-credentials.yaml 2>&1 | grep "recipient"

ArgoCD-Sync schlägt mit KSOPS-Fehlern fehl Stellen Sie sicher, dass der KSOPS-Init-Container erfolgreich abgeschlossen wurde und das Age-Schlüssel-Secret existiert:

kubectl logs deployment/argocd-repo-server -n argocd -c install-ksops
kubectl get secret sops-age-key -n argocd

FluxCD Kustomization bleibt bei Not Ready hängen Prüfen Sie die Logs des Kustomize-Controllers:

kubectl logs deployment/kustomize-controller -n flux-system | grep -i sops

Häufige Ursache: Das sops-age Secret ist im falschen Namespace oder hat den falschen Schlüsselnamen (muss age.agekey sein).

Verschlüsselte Datei zeigt mac mismatch-Fehler Die Datei wurde nach der Verschlüsselung ohne sops --set oder sops edit modifiziert. Verschlüsseln Sie erneut aus der Klartextquelle:

sops --decrypt secrets/db-credentials.yaml > /tmp/plain.yaml
sops --encrypt /tmp/plain.yaml > secrets/db-credentials.yaml
rm -f /tmp/plain.yaml

Zusammenfassung

  • Kubernetes Secrets verwenden Base64-Kodierung, keine Verschlüsselung — sie sind standardmäßig nicht sicher
  • SOPS verschlüsselt YAML-Werte und hält Schlüssel lesbar, was aussagekräftige Git-Diffs ermöglicht
  • Age bietet einfacheres Schlüsselmanagement als PGP ohne Konfigurationsaufwand
  • Die .sops.yaml-Datei definiert pfadbasierte Verschlüsselungsregeln für Multi-Umgebungs-Setups
  • ArgoCD integriert sich über das KSOPS-Plugin; FluxCD hat native SOPS-Entschlüsselungs-Unterstützung
  • Schlüsselrotation erfordert die Neuverschlüsselung aller betroffenen Dateien mit sops updatekeys
  • Speichern Sie private Age-Schlüssel immer außerhalb des Repositories und in der .gitignore
  • Für Enterprise-Umgebungen mit dynamischen Secrets erwägen Sie HashiCorp Vault

Verwandte Artikel