Kubernetes Secrets verschlüsseln: Base64 schützt etcd nicht

|Autor: QUASA-Redaktion|5 Min. Lesezeit
Kubernetes Secrets verschlüsseln: Base64 schützt etcd nicht

Kubernetes Secrets liegen standardmäßig unverschlüsselt in etcd. Die Base64-Kodierung ihrer Datenfelder bietet keine Vertraulichkeit: Wer den gespeicherten Wert lesen kann, kann die Daten wiederherstellen. Die Kubernetes-Dokumentation zu Secrets beschreibt sowohl diesen Speicherzustand als auch die Grenzen der Kodierung.

Damit der API-Server Secrets verschlüsselt in etcd schreibt, benötigt er eine EncryptionConfiguration mit einem verschlüsselnden Provider an erster Stelle. Anschließend müssen Administratoren bestehende Secrets erneut schreiben und die gespeicherten Werte direkt in etcd prüfen. Das Aktivieren der Konfiguration allein ändert bereits abgelegte Objekte nicht.

Ausgangszustand und Provider-Reihenfolge prüfen

Die Startkonfiguration des Kubernetes-API-Servers und die Provider-Reihenfolge bestimmen, ob ein Secret unverschlüsselt in etcd landet.

Prüfen Sie auf jedem Control-Plane-Host die Startargumente von kube-apiserver. Fehlt --encryption-provider-config, ist keine Verschlüsselung von API-Daten im Ruhezustand konfiguriert. Ist das Argument gesetzt, öffnen Sie die angegebene Datei und prüfen Sie, ob unter resources die Ressource secrets steht und welcher Eintrag unter providers zuerst kommt.

Steht identity an erster Stelle, werden neue Schreibvorgänge für diese Ressource unverschlüsselt gespeichert. Ein verschlüsselnder Provider an erster Stelle sagt dagegen noch nichts über ältere Secrets aus: Deren Inhalt ändert sich erst bei einem erneuten Schreibvorgang. Erfassen Sie vor der Umstellung auch alle API-Server, die dieselbe Konfiguration und die nötigen Entschlüsselungsschlüssel erhalten müssen.

Lokalen Schlüssel oder KMS wählen

Base64 formatiert die Werte im Secret, schützt aber den Speicherinhalt nicht. Ein lokaler Provider wie secretbox verschlüsselt die von kube-apiserver geschriebenen Daten; sein Schlüssel liegt in der EncryptionConfiguration auf dem Control-Plane-Host. Das begrenzt die Folgen eines isolierten Zugriffs auf etcd, schützt jedoch nicht vor einem Angreifer, der zugleich an die Konfigurationsdatei auf diesem Host gelangt.

Bei KMS v2 werden Datenschlüssel durch einen extern verwalteten Schlüssel geschützt. Laut der Kubernetes-Anleitung zum KMS-Provider spricht der API-Server dafür über einen Unix-Socket mit einem Plugin auf dem Control-Plane-Host; das Plugin verbindet sich mit dem externen Schlüsselverwaltungssystem. Diese Trennung setzt voraus, dass Plugin und KMS für Verschlüsselung und Entschlüsselung erreichbar bleiben. Sie ersetzt keine restriktiven RBAC-Regeln: Berechtigte API-Clients können Secrets weiterhin lesen, und der Zugriff durch Container sollte auf das Nötige begrenzt werden.

EncryptionConfiguration kontrolliert ausrollen

In der EncryptionConfiguration verschlüsselt der erste Provider neue Secrets, während identity vorübergehend alte Klartextobjekte lesbar hält.

Die Kubernetes-Vorgaben zur Verschlüsselung ruhender Daten legen die Reihenfolge fest: Der erste passende Provider verarbeitet neue Schreibvorgänge; beim Lesen werden passende Provider der Reihe nach versucht. Die YAML-Datei verwendet apiVersion apiserver.config.k8s.io/v1 und kind EncryptionConfiguration. Unter resources führen Sie secrets auf, unter providers zuerst den gewählten verschlüsselnden Provider.

Für secretbox tragen Sie unter keys einen Schlüsselnamen und einen zufällig erzeugten, Base64-kodierten Schlüssel ein. Diese Kodierung ist nur das Format des Schlüsselwerts in der Konfiguration; die Verschlüsselung der Secret-Daten übernimmt der Provider. Bei KMS v2 gehören unter kms die Plugin-Version v2, der Plugin-Name und der Unix-Socket-Endpunkt in die Konfiguration. Während der Erstmigration bleibt identity am Ende der Provider-Liste, damit zuvor unverschlüsselt gespeicherte Secrets lesbar sind.

Beschränken Sie die Leserechte für die Konfigurationsdatei, denn ein lokaler Schlüssel darin kann etcd-Daten entschlüsseln. Läuft kube-apiserver als statischer Pod, muss die Datei in den Pod eingebunden und ihr Pfad mit --encryption-provider-config übergeben werden. Aktualisieren Sie mehrere Control-Plane-Hosts nacheinander. Während des Rollouts muss jeder laufende API-Server auch die Daten entschlüsseln können, die ein bereits aktualisierter Server schreibt; die Konfiguration einschließlich der Schlüssel muss deshalb auf allen Hosts zusammenpassen.

Bestehende Secrets neu schreiben und etcd verifizieren

Prüfen Sie zunächst einen neuen Schreibvorgang mit einem harmlosen Testwert, etwa über kubectl create secret generic etcd-test -n default --from-literal=probe=testwert. Lesen Sie danach den Eintrag mit etcdctl aus dem zugrunde liegenden Datenspeicher. Beim üblichen Speicherpfad lässt sich der Wert mit ETCDCTL_API=3 etcdctl get /registry/secrets/default/etcd-test | hexdump -C ansehen; ergänzen Sie die für Ihren etcd-Zugang erforderlichen Endpunkt- und TLS-Argumente.

Ein verschlüsselter Eintrag beginnt nach dem ausgegebenen etcd-Schlüssel mit einem Präfix wie k8s:enc:secretbox:v1: oder k8s:enc:kms:v2:. Prüfen Sie zusätzlich mit kubectl get secret etcd-test -n default, ob der API-Server das Secret weiterhin lesen kann. Die API-Abfrage allein beweist keine Verschlüsselung in etcd, weil der Server Daten vor der Ausgabe entschlüsselt. Entfernen Sie das Test-Secret nach der Prüfung.

Schreiben Sie nun die vorhandenen Objekte mit kubectl get secrets --all-namespaces -o json | kubectl replace -f - erneut. Dafür sind Lese- und Schreibrechte für alle betroffenen Secrets nötig. Falls ein paralleler Schreibvorgang einen Konflikt auslöst, wiederholen Sie die betroffenen Aktualisierungen; in großen Clustern lässt sich die Arbeit nach Namespaces aufteilen. Prüfen Sie anschließend auch zuvor vorhandene Einträge direkt in etcd. Erst wenn alle betroffenen Secrets verschlüsselt gespeichert sind, können Sie identity entfernen. Sonst würde der API-Server verbliebene Klartextobjekte nicht mehr lesen können.

Schlüssel ohne Ausfall rotieren

Bei der Rotation bleibt der alte Schlüssel lesbar, bis Kubernetes Secrets mit dem neuen Schlüssel erneut gespeichert wurden.

Bei einem lokalen Provider beginnt die Rotation damit, den neuen Schlüssel zusätzlich zum alten auf allen Control-Plane-Hosts einzutragen. Übernehmen Sie diese Konfiguration auf allen API-Servern, damit jeder Server mit beiden Schlüsseln gespeicherte Daten lesen kann, und bewahren Sie eine geschützte Kopie des neuen Schlüssels auf. Danach setzen Sie ihn an die erste Stelle der keys-Liste und übernehmen auch diese Änderung auf allen Hosts. Erst jetzt verschlüsseln neue Schreibvorgänge mit dem neuen Schlüssel.

Schreiben Sie die vorhandenen Secrets erneut und kontrollieren Sie bei lokal verschlüsselten Einträgen den Schlüsselnamen im etcd-Präfix. Entfernen Sie den alten Schlüssel erst, wenn die Migration abgeschlossen ist und die Leseprüfung gelingt. Bei einer hochverfügbaren Control Plane können nacheinander aktualisierte API-Server den Dienst während dieser Schritte aufrechterhalten; ein einzelner neu gestarteter API-Server bietet diese Verfügbarkeit nicht.

Bei KMS v2 signalisiert eine Änderung der vom Plugin gemeldeten key_id dem API-Server die Rotation des externen Schlüssels. Diese Änderung wird nicht zwangsläufig sofort von allen API-Servern übernommen. Schreiben Sie die Secrets erst nach Übernahme des neuen Schlüsselzustands erneut und halten Sie den bisherigen Schlüssel für die Entschlüsselung älterer Daten verfügbar, bis die Migration abgeschlossen ist.

Lesen Sie auch:

Teilen:

Newsletter abonnieren

Erhalten Sie die neuesten Nachrichten zu Web3, KI und Krypto direkt in Ihren Posteingang.

0