🐳 Docker

Secrets-Management mit OpenBao und External Secrets Operator

Secrets-Management mit OpenBao und External Secrets Operator
⚠️ Hinweis: Alle Guides auf smoth.me dienen ausschließlich zu Informations- und Lernzwecken. Die Umsetzung erfolgt auf eigene Gefahr. Wir übernehmen keine Haftung für Schäden, Datenverluste oder Systemausfälle, die durch die Anwendung dieser Anleitungen entstehen können. → Vollständiger Haftungsausschluss

Sicheres Secrets-Management im Heimlabor: Dein Weg zu OpenBao und ESO

Hey Leute, schön, dass ihr wieder hier auf smoth.me vorbeischaut! Als langjähriger Admin und Heimlabor-Enthusiast habe ich in meiner Karriere schon so einige Baustellen gesehen – und eine der hartnäckigsten ist immer wieder das Thema Secrets-Management. Gerade in Kubernetes, wo wir ständig neue Anwendungen deployen, ist es eine Kunst, sensible Daten wie Datenbank-Passwörter, API-Keys oder Zugangsdaten sicher zu handhaben. Wer hat nicht schon mal ein Secret in Git committed oder die Base64-Encodierung als "Sicherheit" missverstanden? Ich kenne das nur zu gut. Deswegen bin ich immer auf der Suche nach robusten Lösungen, die uns das Leben leichter und sicherer machen.

Vor Kurzem gab es spannende Neuigkeiten von Kubermatic, die mit ihrem SecureGuard eine kommerzielle Lösung für automatisiertes Secrets-Management vorgestellt haben. Das Coole daran: Unter der Haube stecken Open-Source-Projekte wie OpenBao (ein Fork von HashiCorp Vault) und der External Secrets Operator (ESO). Und genau diese Kombination ist es, die wir uns heute für unser Heimlabor genauer ansehen werden. Vergiss SecureGuard – wir bauen uns unsere eigene, hochsichere und automatisierte Secrets-Verwaltung mit OpenBao und ESO, direkt in unserem Kubernetes-Cluster!

Warum ist das so wichtig? Ganz einfach: Manuelle Kubernetes Secrets sind statisch, müssen mühsam aktualisiert werden, und die Verwaltung in Git ist ein Sicherheitsalbtraum. Mit OpenBao als zentralem Secrets-Store und ESO als Brücke zu Kubernetes können wir:

  • Secrets zentral und sicher speichern.
  • Zugriff auf Secrets fein granular steuern.
  • Secrets automatisch rotieren lassen (wenn OpenBao entsprechend konfiguriert ist).
  • Kubernetes Secrets aus dem Versionskontrollsystem fernhalten.
  • Den Lebenszyklus von Secrets automatisieren.

Das ist ein Game Changer für jedes Heimlabor, das ernsthaft mit Kubernetes arbeitet. Lass uns direkt loslegen!

Voraussetzungen: Was du mitbringen solltest

Bevor wir uns ins Getümmel stürzen, stell sicher, dass dein Heimlabor für dieses Setup bereit ist. Das hier brauchst du:

  • Ein laufender Kubernetes-Cluster: Egal ob ein kleines k3s auf einem Raspberry Pi, ein Minikube auf deinem Rechner oder ein vollwertiger Proxmox-basierter Cluster. Wichtig ist, dass du administrative Rechte hast.
  • kubectl installiert und konfiguriert: Du musst dich mit deinem Cluster verbinden und Befehle ausführen können.
  • Helm installiert: Wir nutzen Helm, um OpenBao und ESO bequem zu deployen.
  • bao CLI installiert (optional, aber empfohlen): Das Kommandozeilen-Tool für OpenBao. Du kannst es hier herunterladen. Alternativ kannst du viele Befehle auch über kubectl exec direkt im OpenBao Pod ausführen.
  • Grundlegendes Verständnis von Kubernetes: Du solltest wissen, was Pods, Deployments, Services und Namespaces sind.
  • Grundlegendes Verständnis von Secrets-Management: Ein grobes Verständnis, warum wir das überhaupt machen, schadet nicht.

Mein Tipp: Plane genügend Ressourcen für OpenBao ein. Auch wenn es im Heimlabor läuft, ist es eine kritische Komponente und sollte stabil laufen. Ein eigener Node oder zumindest ausreichend RAM ist hier Gold wert.

Warum OpenBao und ESO?

OpenBao ist ein fork von HashiCorp Vault, der die Kernfunktionalität für Secrets-Management bietet: sichere Speicherung, Zugriffskontrolle, Verschlüsselung und Audit-Logs. Es ist das "Gehirn" hinter unserem Secrets-Management.

Der External Secrets Operator (ESO) ist die "Brücke". Er sitzt in deinem Kubernetes-Cluster und ist dafür zuständig, Secrets aus externen Quellen (wie OpenBao) abzurufen und sie als native Kubernetes Secrets im Cluster bereitzustellen. Der Clou: ESO überwacht diese externen Quellen und aktualisiert die Kubernetes Secrets automatisch, wenn sich etwas ändert.

Zusammen bilden sie ein unschlagbares Team für ein modernes, sicheres und automatisiertes Secrets-Management, das auch im Heimlabor professionellen Ansprüchen genügt.

Schritt-für-Schritt-Anleitung: Dein Secrets-Management-Setup

1. OpenBao im Kubernetes-Cluster installieren

Wir starten damit, OpenBao in unserem Cluster zu deployen. Ich empfehle, einen eigenen Namespace dafür zu verwenden, um die Ressourcen sauber zu trennen.

Zuerst fügen wir das OpenBao Helm Repository hinzu und aktualisieren es:

helm repo add openbao https://openbao.github.io/helm-charts
helm repo update

Jetzt installieren wir OpenBao. Für ein Heimlabor ist eine Standalone-Installation meist ausreichend. Wir deaktivieren den Dev-Modus und HA, und nutzen einen ClusterIP Service, da ESO später intern darauf zugreifen wird. Für die initialen Schritte werden wir Port-Forwarding nutzen.

helm install openbao openbao/openbao --namespace openbao --create-namespace \
  --set server.dev.enabled=false \
  --set server.ha.enabled=false \
  --set server.standalone.enabled=true \
  --set server.service.type=ClusterIP \
  --set server.service.port=8200 \
  --set server.extraEnvironmentVars[0].name="VAULT_API_ADDR" \
  --set server.extraEnvironmentVars[0].value="http://openbao-0.openbao.openbao.svc.cluster.local:8200" \
  --set server.extraEnvironmentVars[1].name="VAULT_CLUSTER_ADDR" \
  --set server.extraEnvironmentVars[1].value="http://openbao-0.openbao.openbao.svc.cluster.local:8201"

Wichtig zu wissen: Der Helm Chart erstellt einen StatefulSet mit einem Pod namens openbao-0. Die VAULT_API_ADDR und VAULT_CLUSTER_ADDR sorgen dafür, dass OpenBao seine eigene Adresse im Cluster richtig erkennt. Warte, bis der Pod läuft:

kubectl get pods -n openbao
# Warte, bis openbao-0 Running und Ready ist.

2. OpenBao initialisieren und entsiegeln (Unseal)

OpenBao ist standardmäßig versiegelt (sealed) und muss initialisiert und entsiegelt werden, bevor es verwendet werden kann. Das ist ein wichtiger Sicherheitsmechanismus.

Wir initiieren OpenBao direkt im Pod. Ich nutze hier eine einfache Konfiguration mit 1 Key-Share und 1 Key-Threshold, was für ein Heimlabor in Ordnung ist, aber für Produktion nicht empfohlen wird (dort wären es 3/5 oder mehr).

kubectl exec -it openbao-0 -n openbao -- bao operator init -key-shares=1 -key-threshold=1

Dieser Befehl gibt dir zwei sehr wichtige Informationen aus: den Unseal Key und den Root Token. Speichere diese Informationen SOFORT an einem SEHR sicheren Ort! Ohne den Unseal Key kannst du OpenBao nach einem Neustart nicht wieder entsiegeln, und ohne den Root Token kannst du dich nicht als Administrator anmelden.

Jetzt entsiegeln wir OpenBao mit dem erhaltenen Unseal Key:

kubectl exec -it openbao-0 -n openbao -- bao operator unseal <DEIN_UNSEAL_KEY>

Ersetze <DEIN_UNSEAL_KEY> durch den Key, den du gerade erhalten und gesichert hast.

3. OpenBao konfigurieren: KV Secrets Engine und Kubernetes Auth

Bevor wir ESO ins Spiel bringen, müssen wir OpenBao so konfigurieren, dass es Secrets speichern kann und ESO sich authentifizieren darf.

a) Zugriff auf OpenBao via Port-Forwarding

Um die weiteren Konfigurationen bequem von deinem lokalen Rechner aus vorzunehmen, leiten wir den OpenBao Service-Port weiter:

kubectl port-forward svc/openbao -n openbao 8200:8200 &
export BAO_ADDR='http://127.0.0.1:8200'

Der export-Befehl setzt die Umgebungsvariable, die das bao CLI benötigt. Das & am Ende des port-forward-Befehls lässt ihn im Hintergrund laufen.

b) Login mit dem Root Token

Jetzt meldest du dich mit dem Root Token an, den du bei der Initialisierung erhalten hast:

bao login <DEIN_ROOT_TOKEN>

c) KV Secrets Engine aktivieren und ein Secret ablegen

Wir aktivieren die Key/Value (KV) Secrets Engine der Version 2. Hier werden wir unsere Test-Secrets ablegen.

bao secrets enable kv-v2
bao kv put kv/my-app/db username=admin password=supersecure

Du kannst das Secret überprüfen mit: bao kv get kv/my-app/db

d) Kubernetes Auth Method für ESO konfigurieren

Das ist ein entscheidender Schritt für die Sicherheit. Wir wollen, dass sich der External Secrets Operator über seine Kubernetes Service Account Identität bei OpenBao authentifiziert, anstatt einen statischen Token zu verwenden. Das ist viel sicherer.

Zuerst aktivieren wir die Kubernetes Auth Method in OpenBao:

bao auth enable kubernetes

Jetzt konfigurieren wir die Kubernetes Auth Method. OpenBao muss wissen, wie es die Identität von Kubernetes Service Accounts überprüfen kann. Dafür benötigt es den Host des Kubernetes API Servers, ein Token zur Überprüfung von Service Account Tokens und das CA-Zertifikat des Clusters. Mein Tipp: Hol dir diese Werte direkt aus dem Cluster.

KUBE_HOST="https://kubernetes.default.svc"
KUBE_TOKEN_REVIEWER_JWT=$(kubectl get secret $(kubectl get sa external-secrets -n external-secrets -o jsonpath='{.secrets[0].name}') -n external-secrets -o jsonpath='{.data.token}' | base64 -d)
KUBE_CA_CERT=$(kubectl get secret $(kubectl get sa external-secrets -n external-secrets -o jsonpath='{.secrets[0].name}') -n external-secrets -o jsonpath='{.data."ca.crt"}' | base64 -d)

bao write auth/kubernetes/config \
  kubernetes_host="$KUBE_HOST" \
  token_reviewer_jwt="$KUBE_TOKEN_REVIEWER_JWT" \
  kubernetes_ca_cert="$KUBE_CA_CERT"

Wichtig: Hier nutzen wir den Standard-Service-Account von ESO (external-secrets im Namespace external-secrets), um an das Token und CA-Zertifikat zu kommen. Stell sicher, dass dieser Service Account existiert (wird von der ESO Helm Installation erstellt) und die nötigen Berechtigungen hat, um Tokens zu überprüfen (was er standardmäßig haben sollte).

e) OpenBao Policy und Role für ESO erstellen

Wir erstellen eine Policy, die dem External Secrets Operator nur Lesezugriff auf die benötigten Secrets gibt. Danach verknüpfen wir diese Policy mit einer Kubernetes-Rolle, die an den Service Account von ESO gebunden ist.

bao policy write external-secrets-operator-policy - <<EOF
path "kv/data/my-app/*" {
  capabilities = ["read"]
}
EOF

bao write auth/kubernetes/role/external-secrets-role \
  bound_service_account_names=external-secrets \
  bound_service_account_namespaces=external-secrets \
  policies=external-secrets-operator-policy \
  ttl=1h

Hier definieren wir, dass der Service Account external-secrets im Namespace external-secrets, wenn er sich über die Kubernetes Auth Method authentifiziert, die Policy external-secrets-operator-policy erhält. Diese Policy erlaubt nur das Lesen von Secrets unter dem Pfad kv/data/my-app/*. Perfekte Granularität!

4. External Secrets Operator (ESO) installieren

Jetzt installieren wir den ESO in unserem Cluster. Auch hier nutzen wir Helm.

helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace

Überprüfe, ob die ESO Pods laufen:

kubectl get pods -n external-secrets
# Warte, bis die Pods Running und Ready sind.

5. OpenBao als SecretStore für ESO konfigurieren

ESO benötigt einen SecretStore oder ClusterSecretStore, um zu wissen, woher es die Secrets beziehen soll. Da wir OpenBao clusterweit nutzen wollen, erstellen wir einen ClusterSecretStore.

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: openbao-store
spec:
  provider:
    openBao:
      server: http://openbao.openbao.svc.cluster.local:8200 # Der interne Cluster-Service von OpenBao
      auth:
        kubernetes:
          mountPath: kubernetes # Der Pfad, unter dem die Kubernetes Auth Methode in OpenBao aktiviert ist
          # ESO nutzt automatisch seinen eigenen Service Account Token, wenn kein serviceAccountRef angegeben ist.
          # Wir haben OpenBao so konfiguriert, dass dieser Service Account (external-secrets) sich authentifizieren darf.

Speichere dies als clustersecretstore.yaml und wende es an:

kubectl apply -f clustersecretstore.yaml

Prüfe den Status deines ClusterSecretStore:

kubectl get clustersecretstore openbao-store

Der Status sollte Ready sein.

6. Secrets mit ESO in Kubernetes synchronisieren

Endlich kommen wir zum Kernstück! Wir definieren ein ExternalSecret-Objekt, das ESO anweist, welche Secrets aus OpenBao abgerufen und als natives Kubernetes Secret erstellt werden sollen.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-db-secrets
  namespace: default # Oder der Namespace deiner Anwendung
spec:
  refreshInterval: 1m # Wie oft ESO OpenBao auf Updates prüfen soll
  secretStoreRef:
    name: openbao-store
    kind: ClusterSecretStore
  target:
    name: my-app-db-creds # Der Name des Kubernetes Secrets, das erstellt wird
    creationPolicy: Owner # ESO verwaltet den Lebenszyklus des Secrets
  data:
    - secretKey: DB_USERNAME
      remoteRef:
        key: kv/my-app/db # Pfad in OpenBao
        property: username # Key innerhalb des OpenBao Secrets
    - secretKey: DB_PASSWORD
      remoteRef:
        key: kv/my-app/db
        property: password

Speichere dies als external-secret.yaml und wende es an:

kubectl apply -f external-secret.yaml

Innerhalb weniger Sekunden sollte ESO das Kubernetes Secret my-app-db-creds im Namespace default (oder deinem gewählten Namespace) erstellt haben. Überprüfe es:

kubectl get secret my-app-db-creds -n default -o yaml

Du solltest sehen, dass die Felder DB_USERNAME und DB_PASSWORD korrekt aus OpenBao übernommen wurden. Die Werte sind natürlich base64-encodiert, wie es bei Kubernetes Secrets üblich ist.

7. Anwendung des Secrets in einem Deployment

Jetzt kannst du dieses Kubernetes Secret wie jedes andere Secret in deinen Pods oder Deployments verwenden.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app-container
          image: nginx:latest # Beispiel-Image
          env:
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: my-app-db-creds
                  key: DB_USERNAME
            - name: DB_PASS
              valueFrom:
                secretKeyRef:
                  name: my-app-db-creds
                  key: DB_PASSWORD

Speichere dies als my-app-deployment.yaml und wende es an: