Kubernetes nennt es ein Secret. Als ich eines auf Azure Kubernetes Service (AKS) mit kubectl describe angesehen habe, zeigte es nur eine Größe: 21 Bytes. Einen Befehl später hatte ich den Wert im Klartext. Kein Passwort, kein Schlüssel, nichts zu knacken.
Dieser Beitrag zeigt, was ein Kubernetes-Secret wirklich schützt, wer es lesen kann und was es sicher macht. Alles stammt aus der Übung des Microsoft-Learn-Moduls, ausgeführt in meinem eigenen Cluster.
Das Wichtigste
- Ein Secret ist kodiert, nicht verschlüsselt. Kubernetes speichert die Werte eines Secrets als Base64. Base64 hat keinen Schlüssel: Wer das Secret lesen darf, dreht es mit einem Befehl zurück. Auch das Auflisten von Secrets liefert ihre Werte.
- Im Pod steht es im Klartext. Das Kubelet übergibt den dekodierten Wert an den Container. Eine App kann ihn in ihrer eigenen Ausgabe maskieren, aber wer
kubectl execauf dem Pod ausführen darf, liest den vollen Wert. - RBAC ist das Schloss, nicht die Identität des Pods. Der eigene Service Account des Pods durfte kein einziges Secret lesen, und das muss er auch nicht: Das Kubelet holt die Secrets für die Pods auf seinem Node. Deshalb kann jeder, der in einem Namespace Pods anlegen darf, jedes Secret dort einhängen.
- Auf AKS sind die Festplatten verschlüsselt, jedes einzelne Secret in etcd nur mit KMS. Azure Storage verschlüsselt ruhende Daten immer. Jedes Secret vor dem Schreiben in etcd zu verschlüsseln ist eine optionale Schicht, standardmäßig aus. Der Azure-Key-Vault-Provider für den Secrets Store CSI Driver hält den Wert ganz aus dem Cluster heraus.
Kodiert, nicht verschlüsselt
Ein Secret ist ein Kubernetes-Objekt für sensible Werte wie Passwörter und Schlüssel. Es funktioniert wie eine ConfigMap, das Objekt für normale Einstellungen, hat aber einen eigenen Typ und eigene Berechtigungen. Die Übung im Microsoft-Learn-Modul schreibt eines in ein YAML-Manifest, mit den Werten im Klartext unter stringData:
apiVersion: v1kind: Secretmetadata:name: api-secretstype: OpaquestringData:secret-endpoint: "SecretEndpointValue"secret-access-key: "SecretAccessKey123456"
Das sind die Demo-Werte der Übung, kein echter Schlüssel. Ein solches Manifest ist das erste Leck: Es gehört nie in die Versionskontrolle. Kubernetes übernimmt stringData in das Feld data, und data enthält Base64.
Das Modul prüft ein Secret mit kubectl describe. Das zeigt die Schlüssel und ihre Größe, nicht die Werte:
> kubectl describe secret api-secretsType: OpaqueData====secret-access-key: 21 bytessecret-endpoint: 19 bytes
Das sieht geschützt aus. Ist es nicht. Fragt man nach dem rohen Feld data, kommt der gespeicherte Wert zurück:
> kubectl get secret api-secrets -o jsonpath="{.data.secret-access-key}"U2VjcmV0QWNjZXNzS2V5MTIzNDU2
Base64 ist eine Kodierung, keine Verschlüsselung. Es macht aus beliebigen Bytes einfache Buchstaben, damit sie in YAML und JSON passen: Je vier Buchstaben stehen für drei Bytes (U2Vj ist Sec, cmV0 ist ret und so weiter). Einen Schlüssel gibt es nicht. Die Go-Template-Ausgabe von kubectl dekodiert den Wert im selben Befehl:
> kubectl get secret api-secrets -o go-template='{{index .data "secret-access-key" | base64decode}}'SecretAccessKey123456
Ein Secret ist ein beschrifteter Umschlag, kein Tresor. kubectl describe öffnet ihn nur nicht für Sie.
Klartext im Pod
In der Übung startet ein Deployment die App in einem Pod. Sein Template ordnet jeden Schlüssel mit secretKeyRef einer Umgebungsvariablen zu:
env:- name: SECRET_ACCESS_KEYvalueFrom:secretKeyRef:name: api-secretskey: secret-access-key
Wenn der Container startet, holt das Kubelet, der Kubernetes-Agent auf dem Node, das Secret und übergibt den dekodierten Wert an den Container. Wie bei einer ConfigMap, die als Umgebungsvariablen gelesen wird, ist das eine Kopie vom Start: Ein rotiertes Secret erreicht die App erst nach einem Rollout.
Die App der Übung geht vorsichtig damit um. Ihr Endpunkt /secrets maskiert den Wert:
> python cfg.py get /secretsGET /secrets -> 200… "secret_access_key":{…,"value":"***3456","length":21} …
Aber das Maskieren macht die App. Der Container selbst hält den echten Wert:
> kubectl exec deploy/aks-config-api -- printenv SECRET_ACCESS_KEYSecretAccessKey123456
Im Pod ist ein Secret Klartext. Wer kubectl exec auf diesem Pod ausführen darf, kann es lesen.
Wer es lesen darf
Kubernetes regelt den Zugriff mit RBAC, der rollenbasierten Zugriffssteuerung: welcher Benutzer oder welche Identität welche Art von Objekt mit get, list oder watch abfragen darf. list zählt als Lesen: Das Auflisten von Secrets liefert ihre Werte. kubectl auth can-i stellt dem Cluster genau diese Frage:
> kubectl auth can-i get secretsyes> kubectl auth can-i get secrets --as system:serviceaccount:default:defaultno
Die erste Antwort gilt mir. Meine kubeconfig kam von az aks get-credentials, auf einem Cluster ohne Anmeldung über Microsoft Entra; dort wirkt diese Anmeldeinformation wie ein Cluster-Admin. Die zweite Antwort gilt dem Pod: Die Übung setzt keinen Service Account, also laufen ihre Pods als default, und diese Identität darf kein einziges Secret lesen.
Das muss sie auch nicht. Der Pod fragt die API nie nach seinem Secret; das Kubelet tut es, und der Cluster lässt jedes Kubelet nur die Secrets der Pods auf seinem eigenen Node lesen. Die Identität des Pods ist also nicht das Schloss. Das Schloss ist, wer Secrets lesen darf und wer in die Pods kommt, die sie nutzen.
| Wer | Liest den Wert wie |
|---|---|
Wer get, list oder watch auf Secrets hat | kubectl get secret … -o jsonpath + Base64-Dekodierung |
Wer exec in den Pod ausführen darf | printenv oder die eingehängte Datei |
| Wer im Namespace Pods (oder Deployments) anlegen darf | ein neuer Pod, der das Secret einhängt |
| Der eigene Service Account des Pods (default) | braucht keinen API-Zugriff: Das Kubelet liefert es |
Wo verschlüsselt?
Kubernetes speichert jedes Objekt, auch Secrets, in etcd, seiner Datenbank. Auf AKS läuft etcd in der Control Plane, dem Teil des Clusters, den Microsoft für Sie betreibt. Ist dort jedes Secret standardmäßig verschlüsselt? Nein, nur wenn Sie es einschalten.
- Azure-Plattformverschlüsselung: Azure Storage verschlüsselt ruhende Daten immer. Das schützt die Festplatte, nicht jedes einzelne Secret.
- KMS-Verschlüsselung: eine optionale Schicht, die jedes Secret vor dem Schreiben in etcd verschlüsselt, mit einem Schlüssel in Azure Key Vault (von der Plattform verwaltete oder eigene Schlüssel).
Das Sicherheitsprofil meines Clusters zeigte diese Schicht ausgeschaltet, wie es der Standard ist:
> az aks show -g rg-demo -n aks-demo --query securityProfile -o json{"azureKeyVaultKms": null,"customCaTrustCertificates": null,"defender": null,"imageCleaner": null,"workloadIdentity": null}
Auch mit KMS bekommt kubectl den Klartext: Der API-Server entschlüsselt ihn für jeden, den RBAC durchlässt. Verschlüsselung im Ruhezustand schützt die gespeicherte Kopie, nicht die API.
Der stärkere Schritt hält den Wert aus dem Cluster heraus: der Azure-Key-Vault-Provider für den Secrets Store CSI Driver, ein Add-on, das Secrets aus Key Vault in Pods einhängt. Der Wert liegt in Key Vault; beim Start des Pods holt der Treiber ihn mit einer verwalteten Identität und hängt ihn als Datei ein. In etcd liegt er nie, es sei denn, Sie lassen den Treiber zusätzlich ein Kubernetes-Secret daraus erzeugen. Key Vault kann jeden Lesezugriff protokollieren, und mit eingeschalteter Rotation (standardmäßig aus) prüft der Treiber alle zwei Minuten auf einen neuen Wert. Im Pod ist es weiterhin eine Klartextdatei, die Regeln oben gelten also weiter.
Die Berechtigung, die jedes Secret liest
Das Kubelet übergibt ein Secret an jeden Pod, der es nennt. Wer in einem Namespace einen Pod oder ein Deployment anlegen darf, kann also jedes Secret dort einhängen und ausgeben, ohne je Secrets lesen zu dürfen. In meinem Lauf durfte der Service Account der App das nicht:
> kubectl auth can-i create pods --as system:serviceaccount:default:defaultno
Wenn Sie Rollen vergeben: Behandeln Sie „Pods anlegen“ wie „Secrets lesen“.
Was beim Ausführen passiert ist
Getestet am 28. Oktober 2026 in meinem eigenen Azure-Abonnement (AKS 1.36 in westeurope, Azure CLI 2.88.0, kubectl 1.36); danach wurde alles gelöscht. Der Cluster hatte einen Node vom Typ Standard_D2s_v5 und keine Microsoft-Entra-Integration (lokale Konten), wie in der Übung. Jeder Befehl oben lief in unter einer Sekunde, außer az aks show (3,1 s). Die Prüfungen für den Service Account default endeten mit Exit-Code 1, so sagt kubectl auth can-i „nein“. Das Secret enthielt die Demo-Werte der Übung, die dekodierte Ausgabe auf dieser Seite ist also unbedenklich.
Was das Video weglässt
- Weitere Secret-Typen:
kubernetes.io/dockerconfigjsonfür Registry-Zugangsdaten undkubernetes.io/tlsfür Zertifikate, dazukubectl create secret generic --from-literal. - Azure App Configuration mit Key-Vault-Verweisen: Ihr Kubernetes-Provider löst die Verweise auf und erzeugt Kubernetes-Secrets, die dann wieder in etcd liegen.
- Microsoft-Entra-Integration und das Abschalten lokaler Konten, damit der Zugriff auf den Cluster über Entra ID läuft statt über ein Cluster-Admin-Zertifikat.
- Workload Identity für den CSI-Treiber statt der verwalteten Identität des Add-ons, und unveränderliche Secrets.
- Persistenter Speicher, der dritte Teil des Moduls (ein eigener Beitrag).
Weiterführende Links
- Microsoft-Learn-Modul: Configure applications on Azure Kubernetes Service
- Secrets und Good practices for Kubernetes Secrets (Kubernetes-Dokumentation)
- Data encryption at rest concepts for AKS
- Use the Azure Key Vault provider for Secrets Store CSI Driver in AKS



