Skip to content

Alles sicher – Kubernetes Secrets verschlüsselt speichern mit “Sealed Secrets”

Kubernetes Secrets sind ein einfacher Weg um Konfigurationen oder Parameter in Kubernetes zu speichern um sie in den Pods / Containern wiederzuverwenden. Allerdings täuscht der Name Secret, ein Secret wird nicht verschlüsselt und sicher Kubernetes abgelegt sondern lediglich kodiert. Eine dekodierung ist jederzeit möglich. Das gilt für das Manifest als auch für die Secret Ressource im Cluster. Mit Sealed Secrets können wir zumindest die Informationen im Manifest verschlüsseln um sie z.B. in einem Git Repository sicher zu speichern.

Sensible Informationen in einem Git Repository abzulegen ist nie eine gute Idee. Kubernetes Secrets bzw. das zugehörige Manifest können aber nicht mit Bordmitteln verschlüsselt werden. Hier hilft Sealed Secrets. Dabei handelt es sich um einen sogenannten Controller den wir in unseren Cluster installieren. Mit Hilfe von einem Public Key können wir unsere sensiblen Informationen in einem Manifest verschlüsselt. Bei der Anwendung des Secret Manifests in den Cluster, werden die Daten durch Sealed Secrets entschlüsselt und als reguläres Secret im Cluster gespeichert. Unser Manifest bleibt dabei aber immer verschlüsselt und kann somit in einem zentralen (Git) Repository abgelegt werden. Detailierte Informationen über Sealed Secrets könnt ihr hier finden.

Update 06.02.2022: Alle hier beschriebenen Manifeste könnt ihr auch in meinem Github Repository finden.

Sealed Secrets installieren und verwenden

Sealed Secrets besteht aus zwei Komponenten. Dem oben angesprochenen Controller und einem Command Line Tool, dass uns Benutzer bei der Erstellung von Sealed Secrets unterstützt. Den Controller können wir mit diesem Befehl in den Cluster installieren.

kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/controller.yaml

Achtung: Sealed Secrets in der Version 0.16.0 nutzt die API Version v1beta1 zur Erstellung von Rollen, Role Bindungs usw. Diese API Version ist mit der Version 1.22 in Kubernetes nicht mehr enthalten. Solltet ihr also Version 1.22 von Kubernetes oder höher einsetzen, müsst ihr das controller.yaml anpassen und aus dem Eintrag rbac.authorization.k8s.io/v1beta1 diesen hier machen authorization.k8s.io/v1. Ob dieses Problem in aktuelleren Sealed Secret Versionen gelöst ist, kann ich nicht sagen.

Das kubeseal CLI kann unter Linux folgendermaßen installiert werden.

wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/kubeseal-linux-amd64 -O kubeseal
sudo install -m 755 kubeseal /usr/local/bin/kubeseal

Von hier an gibt es mehrere Möglichkeiten mit Sealed Secrets zu arbeiten über das kubeseal CLI. Zur Verschlüsselung unserer Secrets benötigt kubeseal den Public Key den der Sealed Secrets Controller beim Deployment erstellt hat (sollte er noch nicht vorhanden sein, siehe unten). Hierzu existiert in kubeseal die Option --fetch-cert um sich den Public Key aus dem Cluster herunterzuladen. Solltet ihr Sealed Secrets in einem anderen Namespace deployed haben, müsst ihr bei –fetch-cert ebenfalls den Parameter –controller-namespace angeben.

kubeseal [--controller-namespace <NAMESPACE>] --fetch-cert > public-sealed-secrets.pem

Ihr könnt den Key auch aus den Logs des Controller Containers anzeigen lassen mit folgendem Befehl:

kubectl logs -n sealed-secrets sealed-secrets-controller-<podID>

Eine weitere Alternative ist natürlich, direkt in das Secret sealed-secrets-key zu schauen und dort den Wert für den Key tls.crt auszulesen.

Den Public Key speichern wir uns in einer Datei mit dem Namen public-sealed-secrets.pem ab. Er wird wie oben erwähnt dazu verwendet, die Werte in einem Kubernetes Secret zu verschlüsseln. kubeseal nimmt hierzu ein vorhandenes Secret Manifest und wandelt es in eine Custom Ressource (CRD) um vom Typ SealedSecret. Auf diesen Ressource Typ wartet des Sealed Secrets Controller in unserem Cluster und entschlüsselt (unter Verwendung des Private Keys) die Daten wieder und legt diese wiederum als reguläres Secret in unserer Cluster Datenbank ab. Das verschlüsselte Manifest vom Typ SealedSecret verbleibt auf dem Client und kann bedenkenlos in ein Git Repository hochgeladen werden.

Hier ein Beispiel. Ich habe ein Secret, dass ich gerne verschlüsselt in meinem Git Repo ablegen möchte. Die Base64 enkodierten Werte für username und password entsprechen admin und supersecret.

apiVersion: v1
data:
  password: c3VwZXJzZWNyZXQK
  username: YWRtaW4=
kind: Secret
metadata:
  name: sealed-secret-test-credentials
  namespace: default
type: Opaque

Nun führen wir kubeseal unter Angabe unseres Public Keys aus und verschlüsseln das Secret. Wir speichern das SealedSecret unter dem Dateinamen sealed_secret.yaml.

kubeseal --format=yaml --cert=public-sealed-secrets.pem < secret.yaml > sealed_secret.yaml

Die ausgegebene Datei sieht dann ungefähr so aus.

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: sealed-secret-test-credentials
  namespace: default
spec:
  encryptedData:
    password: AgBJLZK7+TJHYTzcEmBnuOCnEyHi9aQk27zXjSIs33CHExp3IjvqFggaE/o4OM+pL4FjCvjBCj5vx1mIEoBNza8jx69dvGupxnzqMkMYlOvHekIQj5n5+9MCRD66JWm3+JxAaWRcqCXPQF2Zxu1eOF/Ugjfjwgd5loyOGj7TbGtST+pLPwEj+TidSFVjcSbIJ6xYWusyTZe06u0c/HcO7lFfNo5KHcn5nCIzlbqevUXGEz0/tsu4LN61Msd7R6n94w2c2Z91pTf/oatdQ9kI1UE+MgaWx7WS0ZQLKZ5tkhi/CeDpwPLRNiWRpCWFHF2lWL1+WpWf3fFRC8PrnfMJjFK5oXKys35rUms9LXqTNlzB1lmAN2WsoyVHFVl84UP/EMwtUUYqL6iKZ6z2Hf2MjPc2TL1trwVYC4ha1CaNFbBsBjgowTlBhU/bvRuI9F0b1/izwhHZYAlbEyqPQRlnP2X6nqK+XeZLHU2Vi1lKMcxrT36h1pgCZqhXfyJVcp+VkI+3N2t701OVNgAFtT7oAdVtwRA7PTfWqBGeAWUfvWPwb6uOFY/gw0Xi4jik1dlm+EQ/+3fAngGjzziJjxO7FrcsKitVEemocGrZ63/qkTP/ErjcMzlouLQF+PMOARjor2b2BDHjZV72ZqDcKjIQA++T8WK0kHhUsCHfxk7ChPvRxKEZCi8b7ewe1IlT3UNoLEkd5YNPpHTLHqLPT/E=
    username: AgBDxPDTvj6t7NDrnXkIwCIMBYzycSepqOybdgI3HjASAqTmaVyRacVrIXvVeL/KwiNsyBxOfPimQPM054qJM4PmBwJcGt7finc0nsmeZFAsVhBMlct5Vt3L2gY0DblAppF5mcoJGieL0zoEC7bjEwHsugpiRaqCOh5nk5z06Lm0tncyutMl62XncN0c7zweWd3oT+cQrgQ0hULjUPzj1kW5LC5gm36DV56FAYJyY2jds2TxMU3TNbQONe53R0xbVA5VeoLpUhQwEH88EcwJqNhQEORkAMt2+uGHOEkBtlK5nk0GHjfS7eUn13IWQQmtqxVLcXGMrArakGCES0AN3tb9eVpRQSmNLWTlZjuS7Uige+9oEF4VQ7Xh4VeXG32Ca0wAPAe+E4IT16D4/YaGCT0WxO2dg6l+CRTMprivuftlmSoXzTD26mZxoqo4Avc4fOwLEHIBsyIlShWl0D6o4rqKNtY9u3Pu6PPV2Vfu/mhEPjwoDG2t5EjgvRvfOVdPZfGLq06SPp67ZppeFhUaj1JA/hfybNhWqsyyRxh9xk20Qe3xfbRoFoAdVENibJ42UZpz3AHuLQbDp8H7T20ZPtayjhmUk0OBVTLynEqzyTYVAzC0BkIzW2sJjj2dJoudKB9wdoo3nMlS+HdlubKQ+fwezDExHerX19OFoxNOedeodsYYt8abM3GjnsivOaBZDBIM4lBmTQ==
  template:
    data: null
    metadata:
      creationTimestamp: null
      name: sealed-secret-test-credentials
      namespace: default
    type: Opaque

Wir sehen, dass wir nicht länger eine Ressource vom Typ Secret im Manifest beschreiben, sondern eine Ressource vom Typ SealedSecret. Die Werte von username und password sind verschlüsselt.

Jetzt wenden wir das Manifest sealed_secret.yaml gegen unseren Cluster an und werden sehen, dass direkt ein reguläres Secret mit unserem angegebenen Namen sealed-secret-test-credentials erscheint. Es wurde also von Sealed Secret wieder entschlüsselt.

kubectl apply -f sealed_secret.yaml
sealedsecret.bitnami.com/sealed-secret-test-credentials created

kubectl get secrets sealed-secret-test-credentials -o yaml
apiVersion: v1
data:
  password: c3VwZXJzZWNyZXQK
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2021-11-27T21:26:39Z"
  name: sealed-secret-test-credentials
  namespace: default
  ownerReferences:
  - apiVersion: bitnami.com/v1alpha1
    controller: true
    kind: SealedSecret
    name: sealed-secret-test-credentials
    uid: 7ad3c6b3-fe63-448c-9330-7f4a3c160628
  resourceVersion: "2819330"
  uid: 9f52c544-b94b-4752-89d5-538c691d8c4e
type: Opaque

Achtung: Ich empfehle euch, nicht direkt in einem geklonten Git Repository mit kubeseal zu arbeiten. Es passiert sonst unglaublich schnell, dass ihr ausversehen doch wieder das unverschlüsselte Secret in einen Commit aufnehmt und dann war der ganze Aufwand für die Katz.

Private Key sichern und wiederherstellen

Es macht durchaus Sinn den Private Key der im Secret sealed-secrets-key unter tls.key gespeichert ist zu sichern. Solltet ihr euren Kubernetes Cluster neu aufsetzen oder aus welchem Grund auch immer Sealed Secrets entfernen und neu deployen, könnt ihr mit dem alten Public Key verschlüsselte Secrets nicht mehr mit dem neuen Private Key entschlüsseln. Sealed Secrets wechselt in regelmäßigen Abständen den Private Key und legt einen neuen Private Key an. Die alten Keys bleiben bestehen. Es muss also sichergestellt sein, dass wir alle vorhandenen und in Verwendung befindlichen Private Keys regelmäßig sichern. Das erreichen wir z.B. so.

kubectl get secrets sealed-secrets-<keyID> -o yaml

Das YAML speichern wir uns ab und legen es für den schlimmsten Fall an einen sicheren Ort (NICHT das Git Repo!).

Wenn wir jetzt also auf einem neuen Cluster unsere alten Private Keys verwenden wollen, dann legen wir diese einfach vor der Installation aus dem gesicherten YAML an. Sealed Secrets wird beim Deployment feststellen, dass es bereits einen oder mehrere Private Keys gibt und diese durchprobieren, sollte ein SealedSecret geladen werden.

Philip

Leave a Reply

Your email address will not be published. Required fields are marked *