Wer mit Kubernetes arbeitet, begegnet früher oder später dem Secret-Objekt. Es sieht aus wie der richtige Ort für Passwörter, API-Tokens und Zertifikate, und der Name suggeriert Schutz. Genau das ist das Problem. Ein Kubernetes-Secret ist standardmäßig nicht verschlüsselt, sondern nur base64-kodiert. Dieser Unterschied entscheidet in der Praxis über die Sicherheit eines ganzen Clusters.

base64 ist Kodierung, keine Verschlüsselung

base64 verwandelt Binärdaten in einen druckbaren ASCII-String. Es gibt keinen Schlüssel, kein Geheimnis, nichts. Jeder, der den String hat, rechnet ihn in einer Sekunde zurück.

echo 'c3VwZXItZ2VoZWlt' | base64 -d
super-geheim

Wer ein Secret aus dem Cluster liest, bekommt genau diesen kodierten Wert und dekodiert ihn ohne weitere Hürde:

kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

Wo die Daten wirklich liegen

Secrets landen in etcd, dem zentralen Key-Value-Store von Kubernetes. Ohne zusätzliche Konfiguration liegen sie dort im Klartext, also base64-kodiert. Wer Lesezugriff auf etcd hat, etwa über ein Backup oder einen kompromittierten Control-Plane-Knoten, liest alle Secrets im Cluster. Das gleiche gilt für jeden, der per RBAC das Recht hat, Secrets in einem Namespace zu lesen.

Encryption at Rest aktivieren

Den ersten echten Schutz bringt Verschlüsselung at rest. Der API-Server verschlüsselt Secrets dann, bevor sie nach etcd geschrieben werden. Konfiguriert wird das über eine EncryptionConfiguration:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <BASE64-32-BYTE-KEY>
- identity: {}

Der API-Server bekommt die Datei über --encryption-provider-config. Wichtig: Der Schlüssel liegt damit auf dem Control-Plane-Knoten. Für höhere Anforderungen bindet man über den kms-Provider ein externes Key-Management an, damit der Schlüssel den Cluster nie im Klartext verlässt.

RBAC ist die eigentliche Tür

Verschlüsselung at rest schützt die Daten auf der Platte. Sie schützt nicht vor jemandem, der legitim per kubectl get secret zugreifen darf. Deshalb gehört der Zugriff so eng wie möglich gefasst. Eine Rolle, die nur ein einzelnes Secret braucht, sollte auch nur dieses bekommen:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app
name: read-db-secret
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"]
verbs: ["get"]

Wer list auf Secrets vergibt, gibt faktisch Lesezugriff auf alle Secrets im Namespace, weil resourceNames bei list nicht greift.

Secrets gehören nicht in die Logs

Ein häufiger Fehler ist, Secrets als Umgebungsvariablen zu injizieren und sie dann versehentlich zu protokollieren. Viele Frameworks geben beim Start die komplette Umgebung aus. Wo möglich, Secrets als Datei mounten statt als env-Variable, und sicherstellen, dass kein Debug-Output die Werte ausgibt.

Externe Secret-Manager

In größeren Setups lohnt sich ein externer Secret-Store wie HashiCorp Vault, AWS Secrets Manager oder Azure Key Vault, angebunden über den External Secrets Operator. Die Secrets bleiben dann an einer zentralen Stelle mit Audit-Log und Rotation, und Kubernetes synchronisiert sie nur bei Bedarf. Das löst auch das Problem, dass Secrets sonst gern unkontrolliert im Cluster verteilt liegen.

Wer darf eigentlich zugreifen?

Bevor man RBAC anpasst, lohnt der Blick auf den Ist-Zustand. Kubernetes beantwortet die Frage direkt:

kubectl auth can-i get secrets --as=system:serviceaccount:app:default -n app
kubectl get rolebindings,clusterrolebindings -A -o wide | grep -i secret

So sieht man, welche ServiceAccounts und Nutzer tatsächlich an Secrets kommen. Oft ist die Liste länger als erwartet, weil Default-ServiceAccounts oder breit vergebene ClusterRoles im Spiel sind. Genau diese stillen Berechtigungen sind in der Praxis das größere Risiko als die fehlende Verschlüsselung.

Und im Repository?

Damit schließt sich der Kreis zu einem Thema, das beim GitSecOps-Ansatz dauernd auftaucht: Secrets haben in einem Git-Repository nichts verloren, auch nicht base64-kodiert in einer committeten Secret-YAML. Wer Manifeste versioniert, braucht entweder verschlüsselte Secrets, etwa mit SOPS oder Sealed Secrets, oder einen externen Store. Wie sich das sauber in eine Pipeline einbaut, vertiefe ich regelmäßig auf digital-business.blog.

base64 ist bequem und lesbar, aber kein Sicherheitsmechanismus. Wer Kubernetes-Secrets ernst nimmt, kombiniert Encryption at Rest, strenge RBAC und im Zweifel einen externen Secret-Manager. Der Name allein schützt nichts.

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..