Kubernetes Secrets mit SeleadSecrets verschlüsseln

Sealed Secrets ist eine Lösung zum Speichern verschlüsselter Kubernetes- Geheimnisse in der Versionskontrolle.

In diesem kompakten Tutorial wollen wir schaune, wie wir es installieren und verwenden können.

Vergleich mit Helm-Secrets und Sops

Eine beliebte Alternative zu Sealed Secrets ist Helm-Secrets, das Sops als Backend verwendet.

Der Hauptunterschied ist:

  • Sealed Secrets entschlüsselt das Geheimnis serverseitig.
  • Helm-secrets entschlüsselt das Geheimnis clientseitig.

Die clientseitige Entschlüsselung mit Helm-Geheimnissen kann ein Sicherheitsrisiko darstellen, da der Client z. B. ein CI/CD-System Zugriff auf den Verschlüsselungsschlüssel haben muss, um die Bereitstellung durchzuführen. Dies stellt kein Problem dar, wenn wir dem GitOps Ansatz mit Tools, wie Argo CD oder Flux folgen. Mit Sealed Secrets und serverseitiger Entschlüsselung können wir dieses Sicherheitsrisiko vermeiden. Der Verschlüsselungskey existiert nur im Kubernetes-Cluster und wird niemals offengelegt.

Sealed Secrets ist nicht in der Lage, Cloud-KMS-Lösungen wie AWS KMS zu nutzen. Wenn dies eine Anforderung ist, dann müssen wir uns Sops/Helm-Secrets genauer anschauen.

Installation über Helm-Chart

Sealed Secrets besteht aus zwei Komponenten:

  • Clientseitiges CLI-Tool zum verschlüsseln von Geheimnissen und zum Erstellen der Sealed Secrets.
  • Serverseitiger Controller, der zum Entschlüsseln der Sealed Secrets und zum Erstellen von Secrets verwendet wird.

Um den Controller in unserem Kubernetes-Cluster zu installieren, verwenden wir das offizielle Helm-Chart aus dem Sealed-Secrets-Repository.

Wir fügen das Repository hinzu und installieren es im kube-system Namespace:

helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets

helm install sealed-secrets --namespace kube-system --version 2.13.0 sealed-secrets/sealed-secrets

Installation des CLI-Tools

Secrets werden clientseitig mit dem kubeseal CLI-Tool verschlüsselt.

Auf macOS können kubeseal mittels brew insrallieren. Für Linux können wir die Binärdatei von der GitHub-Release-Seite herunterladen.

macos

brew install kubeseal

linux

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

Die kubeseal CLI verwendet den aktuellen kubectl Kontext, um auf den Cluster zuzugreifen. Bevor Sie fortfahren, stellen Sie sicher, dass kubectl eine Verbindung zu dem Cluster besteht, in dem Sealed Secrets installiert werden soll.

Ein Sealed Secret erstellen

Die kubeseal CLI nimmt ein Kubernetes Secret Manifest als Eingabe, verschlüsselt es und gibt ein Sealed Secret Manifest aus.

In diesem Tutorial verwenden wir dieses Manifest als Eingabe:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: secret
data:
  password: cGFzc3dvcmQ=

Nun speichern wir das Manifest in einer Datei mit dem Namen secret.yaml und verschlüsseln es:

HINWEIS! Falls die Fehlermeldung auftaucht “error: error unmarshaling JSON: while decoding JSON: illegal base64 data at input byte 4” Das Password bzw. der Benutzername in dem Secretfile muss base64 encoded sein. z.B.
echo -n “password” | base64 cGFzc3dvcmQ=

Update

Die neueren Versionen von Kubernetes unterstützen die optionale stringData Eigenschaft, mit der der Wert für jeden Schlüssel ohne Decodierung bereitgestellt werden kann.

apiVersion: v1
kind: Secret
metadata:
  name: dummy-secret
type: Opaque
stringData:
  API_KEY: mega_secret_key
  API_SECRET: really_secret_value1

cat secret.yaml | kubeseal \
    --controller-namespace kube-system \
    --controller-name sealed-secrets \
    --format yaml \
    > sealed-secret.yaml

Der Inhalt der sealed-secret.yaml Datei sollte so aussehen:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: secret
  namespace: default
spec:
  encryptedData:
    password: AgA...
  template:
    metadata:
      creationTimestamp: null
      name: secret
      namespace: default

Um das Sealed Secret bereitzustellen, wenden wir das Manifest mit kubectl an:

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

Nun können wir das Secret abrufen, um sicherzustellen, dass der Controller es erfolgreich entsiegelt hat.

kubectl get secret secret -o yaml                                                                                                                                                                
apiVersion: v1
data:
  password: cGFzc3dvcmQ=
kind: Secret
metadata:
  creationTimestamp: "2023-09-21T14:32:23Z"
  name: secret
  namespace: default
  ownerReferences:
  - apiVersion: bitnami.com/v1alpha1
    controller: true
    kind: SealedSecret
    name: secret
    uid: 0476c9c9-3184-4ca1-a730-fc9ddd851bee
  resourceVersion: "1428"
  uid: aa627405-2e28-4f31-8de7-82781592131d

Ein Sealed Secret aktualisieren

Um einen Wert in einem Sealed Secret zu aktualisieren, müssen wir lokal ein neues Manifest erstellen und es mit der --merge-into Option in ein vorhandenes einbinden.

Im folgenden Beispiel aktualisieren wir den Wert des Passwortschlüssels -from-file=password auf new-password.

echo -n "new-password" | kubectl create secret generic xxx --dry-run=client --from-file=password=/dev/stdin -o yaml | kubeseal --controller-namespace=kube-system --controller-name=sealed-secrets --format yaml --merge-into sealed-secret.yaml

Einem Sealed Secret einen neuen Wert hinzufügen

Der Unterschied zwischen dem Aktualisieren eines Werts und dem Hinzufügen eines neuen Werts ist der Name des Schlüssels. Wenn ein benannter Schlüssel password bereits vorhanden ist, wird er aktualisiert. Wenn es nicht existiert, wird es hinzugefügt.

api_key zUm beispielsweise einen neuen Schlüssel --from-file=api_keyzu unserem Geheimnis hinzuzufügen, führen wir Folgendes aus:

echo -n "my secret api key" \
    | kubectl create secret generic xxx --dry-run=client --from-file=api_key=/dev/stdin -o json \
    | kubeseal --controller-namespace=kube-system --controller-name=sealed-secrets --format yaml --merge-into sealed-secret.yaml

kubectl apply -f sealed-secret.yaml

Einen Wert aus einem Sealed Secret löschen

# BSD sed (macOS)
sed -i '' '/api_key:/d' sealed-secret.yaml

# GNU sed
sed -i '/api_key:/d' sealed-secret.yaml

kubectl apply -f sealed-secret.yaml

Nach dem Anwenden der Datei aktualisiert der Controller die Datei Secret automatisch und entfernt den Wert api_key.

Löschen Sie des Sealed Secrets

Um das Geheimnis zu löschen, verwenden wir kubectl, um die Ressource zu löschen:

kubectl delete -f sealed-secret.yaml

Dadurch wird die Ressource SealedSecret sowie die entsprechende Ressource Secret aus dem Cluster gelöscht.

Fazit:

Sealed Secrets ist eine sichere Möglichkeit, Kubernetes-Geheimnisse in der Versionskontrolle zu verwalten. Der Verschlüsselungsschlüssel wird gespeichert und Geheimnisse werden im Cluster entschlüsselt. Der Client hat keinen Zugriff auf den Verschlüsselungsschlüssel. Der Client verwendet das kubeseal CLI-Tool, um Sealed Secret Manifeste zu generieren, die verschlüsselte Daten enthalten. Nach dem Anwenden der Datei erkennt der serverseitige Controller eine neue Sealed Secrets Ressource und entschlüsselt sie, um eine SecretRessource zu erstellen.