Secret Management

Von Boris Sander9. November 2025
Secret Management

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:

  1. Zentrale, sichere Speicherung
  2. Kontrollierter Zugriff
  3. Automatische Rotation
  4. 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

FeatureVaultAWS SMAzure KVGCP SM
Dynamic SecretsTeilweise--
Auto-Rotation-
Multi-Cloud---
Self-Hosted---
KomplexitätHochNiedrigNiedrigNiedrig
PreisSelf-Host/EnterprisePay-per-usePay-per-usePay-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:

  1. Dokumentierter Prozess
  2. Separate, sichere Credentials
  3. Audit Trail
  4. 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

  1. Identifizieren: Welches Secret? Welcher Scope?
  2. Revoken: Secret sofort ungültig machen
  3. Rotieren: Neues Secret generieren
  4. Analysieren: Wie ist es passiert?
  5. 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: