- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes Secrets Management mit HashiCorp Vault: Praxisanleitung
TL;DR
- Native Kubernetes Secrets sind Base64-kodiert, nicht verschluesselt -- jeder mit etcd-Zugriff kann sie lesen.
- HashiCorp Vault bietet Verschluesselung, Zugriffskontrolle, Audit-Logging und automatische Rotation aus einer Hand.
- Der External Secrets Operator (ESO) synchronisiert Vault-Secrets als native Kubernetes Secrets -- die einfachste Integrationsmethode fuer bestehende Workloads.
- Alternativ mountet der Secrets Store CSI Driver Secrets direkt ins Pod-Filesystem, ohne sie in etcd zu persistieren.
- Dynamische Secrets (z.B. kurzlebige Datenbank-Credentials) reduzieren die Angriffsoberflaeche drastisch.
Warum native Kubernetes Secrets nicht ausreichen
Kubernetes Secrets sehen auf den ersten Blick nach einer Loesung aus. Sie sind in der API integriert, lassen sich mit kubectl verwalten und koennen als Environment-Variablen oder Volumes in Pods gemountet werden.
Das Problem: Die Daten sind nur Base64-kodiert. Das ist keine Verschluesselung. Ein einfaches echo "cGFzc3dvcmQ=" | base64 -d liefert das Klartext-Passwort. Jeder mit Lesezugriff auf das etcd-Backend oder die Kubernetes API (und den richtigen RBAC-Berechtigungen) sieht alle Secrets.
Dazu fehlen native Secrets wesentliche Features:
| Feature | Kubernetes Secrets | HashiCorp Vault |
|---|---|---|
| Verschluesselung at rest | Nur mit EncryptionConfig | Standardmaessig (AES-256-GCM) |
| Automatische Rotation | Nein | Ja (Leases, dynamische Secrets) |
| Audit-Logging | Nur ueber K8s Audit Logs | Detailliertes internes Audit Log |
| Dynamische Secrets | Nein | Ja (DB, AWS, Azure, GCP) |
| Feingranulare Policies | RBAC (Namespace/Resource-Level) | Pfad-basierte Policies pro Identity |
| Zentrale Verwaltung | Pro Cluster | Cluster-uebergreifend |
Architektur: Vault mit Kubernetes verbinden
Es gibt drei gaengige Wege, Vault-Secrets in Kubernetes-Pods zu bringen. Jeder hat seine Staerken:
Option 1: External Secrets Operator (ESO)
Der ESO ist ein Kubernetes-Controller, der Secrets aus Vault abruft und als native Kubernetes Secrets im Cluster erstellt. Bestehende Workloads muessen nicht angepasst werden -- sie lesen weiterhin ein normales Kubernetes Secret.
Vorteile: Einfachste Migration, keine Aenderung an Deployments noetig. Nachteile: Secrets werden in etcd persistiert (wenn auch verschluesselt durch Vault).
Option 2: Secrets Store CSI Driver
Der CSI Driver mountet Secrets direkt als Dateien ins Pod-Filesystem. Die Secrets werden nie als Kubernetes-Objekt in etcd gespeichert.
Vorteile: Secrets existieren nur im Pod-Filesystem. Nachteile: Jedes Deployment braucht Volume-Konfiguration.
Option 3: Vault Agent Injector
Ein Mutating Webhook injiziert einen Vault Agent Sidecar in jeden Pod. Der Agent authentifiziert sich bei Vault und schreibt Secrets in ein Shared Volume.
Vorteile: Unterstuetzt Template-Rendering fuer komplexe Formate. Nachteile: Sidecar erhoet den Ressourcenverbrauch.
Vergleich der drei Optionen
| Kriterium | ESO | CSI Driver | Vault Agent |
|---|---|---|---|
| Secrets in etcd | Ja | Nein | Nein |
| Deployment-Aenderung noetig | Nein (nur ExternalSecret CR) | Ja (Volume Mount) | Nein (Annotations) |
| Ressourcen-Overhead | Controller (1 Pod) | DaemonSet | Sidecar pro Pod |
| Template-Rendering | Nein | Nein | Ja |
| Rotation | Ueber refreshInterval | Ueber rotation config | Ueber Agent |
| Komplexitaet | Niedrig | Mittel | Mittel-Hoch |
Fuer die meisten Teams ist der ESO der beste Einstieg. Er erfordert die wenigsten Aenderungen an bestehenden Workloads.
Schritt fuer Schritt: Vault mit ESO einrichten
1. Vault deployen
Vault laesst sich per Helm im Cluster oder auf einer dedizierten VM betreiben. Fuer Produktion empfiehlt sich ein separater Cluster oder dedizierte VMs mit Raft Storage.
# Vault Helm Chart installieren (Dev-Modus fuer Tests)
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install vault hashicorp/vault \
--namespace vault \
--create-namespace \
--set "server.dev.enabled=true" \
--set "injector.enabled=false"
# Fuer Produktion statt dev mode:
# --set "server.ha.enabled=true"
# --set "server.ha.replicas=3"
# --set "server.ha.raft.enabled=true"
2. Kubernetes Auth Method konfigurieren
Die Kubernetes Auth Method erlaubt es Pods, sich mit ihrem ServiceAccount-Token bei Vault zu authentifizieren.
# Vault Shell oeffnen
kubectl exec -it vault-0 -n vault -- /bin/sh
# Auth Method aktivieren
vault auth enable kubernetes
# Kubernetes API Verbindung konfigurieren
vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
# KV Secrets Engine aktivieren
vault secrets enable -path=kv kv-v2
# Test-Secret anlegen
vault kv put kv/myapp/db \
username="app_user" \
password="s3cur3-p4ssw0rd"
# Policy erstellen
vault policy write myapp-policy - <<EOF
path "kv/data/myapp/*" {
capabilities = ["read"]
}
EOF
# Kubernetes Role erstellen
vault write auth/kubernetes/role/myapp-role \
bound_service_account_names=myapp-sa \
bound_service_account_namespaces=default \
policies=myapp-policy \
ttl=1h
3. External Secrets Operator installieren
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
4. SecretStore und ExternalSecret erstellen
Zuerst der SecretStore, der die Verbindung zu Vault definiert:
# secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-store
namespace: default
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
path: "kv"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "myapp-role"
serviceAccountRef:
name: "myapp-sa"
Dann das ExternalSecret, das ein konkretes Secret aus Vault abruft:
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-db-credentials
namespace: default
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-store
kind: SecretStore
target:
name: myapp-db-secret
creationPolicy: Owner
data:
- secretKey: DB_USERNAME
remoteRef:
key: kv/data/myapp/db
property: username
- secretKey: DB_PASSWORD
remoteRef:
key: kv/data/myapp/db
property: password
Das Deployment referenziert dann das generierte Kubernetes Secret:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
serviceAccountName: myapp-sa
containers:
- name: myapp
image: myapp:latest
envFrom:
- secretRef:
name: myapp-db-secret
5. Pruefen, ob alles funktioniert
# ExternalSecret-Status pruefen
kubectl get externalsecret myapp-db-credentials
# Das generierte Kubernetes Secret ansehen
kubectl get secret myapp-db-secret -o jsonpath='{.data.DB_USERNAME}' | base64 -d
# ESO-Logs bei Problemen
kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets
Dynamische Secrets: Kurzlebige Datenbank-Credentials
Statische Secrets (Username + Passwort in Vault gespeichert) sind besser als Klartext in etcd, aber der eigentliche Vorteil von Vault liegt in dynamischen Secrets.
Dynamische Secrets werden bei jeder Anfrage neu generiert und haben eine begrenzte Lebensdauer. Wenn ein Pod ein Datenbank-Credential anfordert, erstellt Vault einen temporaeren User in der Datenbank, der nach Ablauf der Lease automatisch geloescht wird.
# PostgreSQL Secrets Engine aktivieren
vault secrets enable database
# Datenbankverbindung konfigurieren
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
allowed_roles="myapp-db-role" \
connection_url="postgresql://{{username}}:{{password}}@postgres.default.svc:5432/mydb?sslmode=disable" \
username="vault_admin" \
password="vault_admin_password"
# Role fuer dynamische Credentials
vault write database/roles/myapp-db-role \
db_name=mydb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
Jetzt erstellt Vault bei jedem Abruf einen neuen Datenbank-User mit einer Stunde Gueltigkeitsdauer. Selbst wenn ein Credential leakt, ist es nach einer Stunde wertlos.
Haeufige Fehler und wie man sie vermeidet
Vault im Dev-Modus in Produktion. Dev-Modus speichert alles im RAM, keine Persistenz. Nach einem Restart sind alle Secrets weg. Fuer Produktion immer Raft oder Consul als Storage Backend nutzen.
Zu breite Vault Policies. path "secret/*" { capabilities = ["read"] } gibt jedem authentifizierten Pod Zugriff auf alle Secrets. Policies sollten so eng wie moeglich sein: ein Pfad pro Anwendung.
Unseal-Keys nicht sicher verwahrt. Bei einer produktiven Vault-Installation muessen die Unseal-Keys getrennt und sicher aufbewahrt werden. Am besten: Auto-Unseal ueber einen Cloud KMS (AWS KMS, Azure Key Vault, GCP Cloud KMS).
refreshInterval zu kurz gesetzt. Ein refreshInterval: 10s im ExternalSecret erzeugt extrem viele Vault-Anfragen. Fuer statische Secrets reicht 1h. Fuer dynamische Secrets orientiert man sich an der Lease-Dauer.
ServiceAccount-Token nicht korrekt konfiguriert. Die haeufigste Fehlerquelle bei der Kubernetes Auth Method. Seit Kubernetes 1.24 werden keine langlebigen SA-Tokens mehr automatisch erstellt. Der ESO erstellt kurzlebige TokenRequests -- dafuer muss der ServiceAccount existieren und die Vault-Role korrekt gebunden sein.
Migration bestehender Secrets
Eine Migration von nativen Kubernetes Secrets zu Vault sollte schrittweise erfolgen:
- Inventar erstellen: Alle Secrets im Cluster auflisten (
kubectl get secrets --all-namespaces -o json) - Priorisieren: Datenbank-Credentials und API-Keys zuerst, weniger kritische Secrets spaeter
- In Vault anlegen: Secrets in die KV Engine schreiben
- ExternalSecret erstellen: CR anlegen, der das gleiche Kubernetes Secret erzeugt (gleicher Name, gleiche Keys)
- Altes Secret loeschen: Erst wenn das ExternalSecret den Status
SecretSyncedhat - Testen: Pods neu starten und pruefen, ob die Anwendung korrekt funktioniert
Weiterfuehrende Themen
- Kubernetes Security Scanning automatisieren -- Secrets in Code oder Images finden, bevor sie deployed werden
- Docker und Kubernetes: Grundlagen -- Container-Grundlagen, auf denen Secrets Management aufbaut
- Kubernetes Cluster produktionsreif aufsetzen -- Vault als Teil der Production-Baseline
- Kubernetes Monitoring mit Open Source -- Vault-Metriken in Prometheus/Grafana integrieren
- Kubernetes Hosting: Cloud vs. On-Premise -- Hosting-Optionen fuer Vault und Kubernetes
Vault richtig aufzusetzen erfordert Erfahrung -- besonders bei HA-Konfigurationen, Auto-Unseal und der Migration bestehender Secrets. Wenn Sie dabei Unterstuetzung brauchen, erreichen Sie uns unter /kontakt.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
External Secrets: Kubernetes-Secrets sicher verwalten
Der External Secrets Operator synchronisiert Secrets aus AWS, Vault und Azure Key Vault nach Kubernetes. Anleitung mit Praxisbeispielen.
Sealed Secrets Anleitung: Kubernetes Secrets in Git
Sealed Secrets verschlüsseln Kubernetes Secrets für Git-Repos. Installation, kubeseal-Nutzung und GitOps-Integration Schritt für Schritt erklärt.
Config Management: ConfigMaps, Secrets und External Secrets
Kubernetes Config Management mit ConfigMaps, Secrets und External Secrets Operator: Vault-Integration, Helm-Templating und Encryption at Rest einrichten.
Kubernetes Insider Threat Detection: Strategien für umfassende Clustersicherheit
Entdecke effektive Strategien für die umfassende Kubernetes Insider Threat Detection. Lerne, wie du Innentäter durch lückenloses Monitoring, Behavioral Analytics und fortschrittliche Runtime Security in deinen Kubernetes-Clustern frühzeitig erkennst und die Sicherheit sowie Compliance nachhaltig stärkst.
OPA Gatekeeper: Admission Controller für Kubernetes
OPA Gatekeeper setzt Policies im Kubernetes-Cluster durch und blockiert fehlerhafte Deployments vor dem Rollout. Praxisguide mit Rego-Beispielen.