Secret Management
„Das Passwort ist 'admin'. Steht auch im README."
Sinn & Zweck
Secrets sind überall: API-Keys, Datenbank-Passwörter, TLS-Zertifikate, SSH-Keys, Tokens. Sie sind die Schlüssel zu deinen Systemen – und sie werden ständig falsch behandelt.
Das Problem:
- Secrets in Git-Repositories (für immer in der History)
- Secrets in Environment Variables (sichtbar in Prozesslisten)
- Secrets in Config-Dateien (auf jedem Server)
- Secrets in Slack/Teams (Screenshots, anyone?)
- Secrets auf Post-its (ja, wirklich)
Secret Management löst das Problem durch:
- Zentrale, sichere Speicherung
- Kontrollierter Zugriff
- Automatische Rotation
- Audit-Logging
Der Secret Lifecycle
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Creation │────→│ Storage │────→│ Usage │
│ │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
│
↓
┌─────────────┐
│ Rotation │
│ │
└─────────────┘
│
↓
┌─────────────┐
│ Revocation │
│ │
└─────────────┘
Anti-Patterns (Was man nicht tun sollte)
1. Secrets im Code
# So NICHT:
database_url = "postgres://admin:SuperSecret123@prod-db:5432/app"
api_key = "sk-1234567890abcdef"
# Für immer in Git-History. Für immer.
2. Secrets in Environment Variables (ungeschützt)
# So NICHT:
export DB_PASSWORD="SuperSecret123"
# Sichtbar in:
# - /proc/<pid>/environ
# - ps eww
# - Container Inspect
# - CI/CD Logs
3. Secrets in Config-Dateien
# config.yaml
database:
host: prod-db
user: admin
password: SuperSecret123 # Committed to Git ✗
4. Secrets teilen via Slack/E-Mail
Dev: "Hey, kannst du mir das Prod-DB-Passwort schicken?"
Ops: "Klar, ist SuperSecret123"
Security: *schreit innerlich*
Secret Management Tools
HashiCorp Vault
Der Industriestandard. Kann alles, braucht aber Expertise.
Features:
- Dynamic Secrets (generiert on-demand)
- Secret Rotation
- Encryption as a Service
- PKI/Certificate Management
- Multiple Auth Methods
# Secret schreiben
vault kv put secret/database password="SuperSecret123"
# Secret lesen
vault kv get secret/database
# Dynamic Database Credentials
vault read database/creds/my-role
# Output: username=v-token-my-role-1234 password=A1a-xyz...
# Credentials expire after TTL!
AWS Secrets Manager
Native AWS-Integration, einfach zu nutzen.
import boto3
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='prod/database')
secret = json.loads(response['SecretString'])
db_password = secret['password']
Azure Key Vault
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://my-vault.vault.azure.net/", credential=credential)
secret = client.get_secret("db-password")
password = secret.value
Google Secret Manager
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient()
name = "projects/my-project/secrets/db-password/versions/latest"
response = client.access_secret_version(request={"name": name})
password = response.payload.data.decode("UTF-8")
Vergleich
| Feature | Vault | AWS SM | Azure KV | GCP SM |
|---|---|---|---|---|
| Dynamic Secrets | ✓ | Teilweise | - | - |
| Auto-Rotation | ✓ | ✓ | ✓ | - |
| Multi-Cloud | ✓ | - | - | - |
| Self-Hosted | ✓ | - | - | - |
| Komplexität | Hoch | Niedrig | Niedrig | Niedrig |
| Preis | Self-Host/Enterprise | Pay-per-use | Pay-per-use | Pay-per-use |
Secret Detection
Warum?
Weil Entwickler trotzdem Secrets committen werden. Menschen sind so.
Tools
GitLeaks:
# Repo scannen
gitleaks detect --source . --verbose
# Pre-commit Hook
gitleaks protect --staged
TruffleHog:
# Git History scannen
trufflehog git file://. --since-commit HEAD~100
# GitHub Org scannen
trufflehog github --org=mycompany
Pre-commit Config:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
Was sie finden
Finding: AWS Access Key
Rule: aws-access-key-id
File: config/aws.py:42
Secret: AKIA1234567890ABCDEF
Finding: Private Key
Rule: private-key
File: deploy/key.pem:1
Match: -----BEGIN RSA PRIVATE KEY-----
Kubernetes Secrets
Das Problem
# Kubernetes Secret (Base64 encoded, NOT encrypted!)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
password: U3VwZXJTZWNyZXQxMjM= # echo "SuperSecret123" | base64
# Jeder mit kubectl get secret kann das lesen
# Base64 ist KEINE Verschlüsselung!
Die Lösung: External Secrets Operator
# ExternalSecret - holt Secrets aus Vault/AWS SM/etc.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: database/production
property: password
Sealed Secrets
# Secret verschlüsseln (nur Cluster kann entschlüsseln)
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# Kann sicher in Git committed werden!
Best Practices
1. Secrets niemals im Code
# SCHLECHT
api_key = "sk-1234567890"
# GUT
import os
api_key = os.environ.get("API_KEY")
# BESSER
from vault import get_secret
api_key = get_secret("api/key")
2. Rotation automatisieren
┌─────────────┐
│ Vault │
│ │
└──────┬──────┘
│ Generate new password
↓
┌──────────────────┬──────────────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ DB │ │ App │ │ Old pwd │
│ Update │ │ Update │ │ Revoke │
└─────────┘ └─────────┘ └─────────┘
3. Least Privilege
# Vault Policy: Nur lesen, nur bestimmte Secrets
path "secret/data/app/*" {
capabilities = ["read"]
}
# NICHT:
path "secret/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
4. Audit-Logging aktivieren
# Vault Audit Log
vault audit enable file file_path=/var/log/vault/audit.log
# Loggt jede Operation:
# - Wer hat was angefragt?
# - Wann?
# - Erfolgreich oder nicht?
5. Expiration setzen
# Vault: Secret mit TTL
vault kv put -mount=secret database password="..." ttl=30d
# AWS: Rotation Schedule
aws secretsmanager rotate-secret \
--secret-id prod/database \
--rotation-lambda-arn arn:aws:lambda:...
6. Break Glass Procedures
Für Notfälle:
- Dokumentierter Prozess
- Separate, sichere Credentials
- Audit Trail
- Sofortige Rotation nach Nutzung
Secret in verschiedenen Kontexten
CI/CD
GitHub Actions:
jobs:
deploy:
steps:
- name: Deploy
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./deploy.sh
GitLab CI:
deploy:
script:
- echo "$DB_PASSWORD" # Masked in logs
variables:
DB_PASSWORD: $DB_PASSWORD # From CI/CD Settings
Container
# Kubernetes: Secret als Environment Variable
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# Besser: Als Volume (kein ENV)
volumes:
- name: secrets
secret:
secretName: db-credentials
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
Terraform
# SCHLECHT: Hardcoded
resource "aws_db_instance" "db" {
password = "SuperSecret123"
}
# GUT: Aus Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
secret_id = "prod/db/password"
}
resource "aws_db_instance" "db" {
password = data.aws_secretsmanager_secret_version.db.secret_string
}
Risiken & Grenzen
Zentralisierung = Single Point of Failure
Vault down = Alle Apps können keine Secrets mehr lesen
= Alles down
Lösung: HA-Setup, Caching, Fallback-Mechanismen
Komplexität
"Früher war das Passwort in der Config-Datei."
"Jetzt brauchen wir: Vault + Consul + PKI + Policies + Audit + HA + Backup"
Developer Experience
# Früher:
password = "admin"
# Jetzt:
import hvac
client = hvac.Client(url='http://vault:8200')
client.auth.kubernetes.login(role='my-role')
secret = client.secrets.kv.v2.read_secret_version(
path='database/production',
mount_point='secret'
)
password = secret['data']['data']['password']
Secret Sprawl
"Wir haben 5.000 Secrets in Vault. Wer nutzt was?"
Key Management
Wer verwaltet die Keys, die die Secrets verschlüsseln?
Vorteile
Zentrale Kontrolle
Ein Ort für alle Secrets = ein Ort zum Überwachen, Rotieren, Auditen.
Audit Trail
2024-01-15 10:23:45 | user=deploy-bot | action=read | path=secret/database
2024-01-15 10:23:46 | user=admin | action=update | path=secret/api-key
Automatische Rotation
Keine manuellen Passwort-Änderungen mehr. Keine "Das Passwort ist seit 5 Jahren dasselbe"-Situationen.
Dynamic Secrets
App: "Ich brauche DB-Credentials"
Vault: "Hier, Username: v-app-xyz, Password: abc123, gültig für 1 Stunde"
*nach 1 Stunde: automatisch gelöscht*
Encryption as a Service
App: "Verschlüssel das für mich"
Vault: *verschlüsselt mit HSM-backed Key*
App: "Entschlüssel das für mich"
Vault: *entschlüsselt, loggt Zugriff*
Incident Response: Secret Leaked!
Sofortmaßnahmen
- Identifizieren: Welches Secret? Welcher Scope?
- Revoken: Secret sofort ungültig machen
- Rotieren: Neues Secret generieren
- Analysieren: Wie ist es passiert?
- Monitoring: Wurde es benutzt?
Git-History bereinigen
# BFG Repo Cleaner
bfg --replace-text passwords.txt my-repo.git
# git filter-branch (langsamer)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch path/to/secret.txt' \
HEAD
# ACHTUNG: Alle Clones sind jetzt ungültig!
Post-Incident
- Root Cause Analysis
- Process Improvement
- Training
- Monitoring verbessern
Fazit
Secret Management ist nicht sexy, aber essentiell. Jedes geleakte Passwort, jeder exponierte API-Key ist ein Einfallstor für Angreifer. Die Tools existieren, die Best Practices sind dokumentiert – es braucht nur den Willen, sie umzusetzen.
Das sicherste Passwort ist das, das niemand kennt – nicht mal der Entwickler.
Weiterführende Ressourcen: