Fix-Plan — Security-Audit 2026-05-15
24 Findings, sortiert nach Risk-Reduction-pro-Minute. Reihenfolge ist optimiert auf “groesster Schmerz zuerst weg, billige Fixes mitnehmen, Blocker fuer Friseur-Live raus”.
Status-Codes: OPEN / IN_PROGRESS / DONE / WONTFIX. Bei Erledigung in baseline.json fixed: true setzen.
Heute Abend — 90 Min, raeumt 3 von 5 CRITs ab
Block A — Token-Rotation (15 Min)
F003 — GitHub-PAT + ElevenLabs-Key aus ~/.claude.json rotieren
# 1. GitHub PAT revoke (Browser)
open https://github.com/settings/tokens
# alten gho_... finden, "Delete" — sofort tot
# 2. Neuen PAT anlegen mit Minimal-Scopes (repo, read:org)
# Wert in 1Password ablegen unter: "GitHub PAT — Claude Code dev (2026-05)"
# 3. ElevenLabs revoke (Browser)
open https://elevenlabs.io/app/settings/api-keys
# alten sk_... revoke, neuen anlegen, in 1Password ablegen
# 4. Beide neu in ~/.claude.json eintragen — ABER:
# Mittelfristig auf op-Wrapper migrieren (analog Hetzner-MCP).
# Heute Quick-Fix: neue Werte rein, Migration als F003b separat.Verifikation: grep -E "gho_|sk_" ~/.claude.json zeigt neue Token-Praefixe. Alte Tokens (alt-Werte) ueber gh api user bzw ElevenLabs-Console testen → 401 erwartet.
Status: OPEN → setze auf DONE in baseline.json sobald beide neu.
Block B — mcp-whatsapp absperren (60 Min)
Reihenfolge: erst Notbremse via Cloudflare, dann sauberer Fix im Code.
F001 — /mcp ohne Auth → Cloudflare-Access davor (15 Min Notbremse)
1. Cloudflare Dashboard → Zero Trust → Access → Applications
2. "Add an application" → Self-hosted
- Name: mcp-whatsapp /mcp
- Subdomain: mcp-whatsapp.agenticventures.de
- Path: /mcp/*
3. Policy: "Marvin only"
- Action: Allow
- Include: Email = hello@marvinkuehlmann.com (oder Google OAuth)
4. Save → testen: curl https://mcp-whatsapp.agenticventures.de/mcp ohne Cookie → 302 Redirect
Damit ist die Tuer zu, bevor Code-Fix kommt. Friseur-Bot bleibt benutzbar (Webhook geht weiter durch — siehe F002).
F002 — /webhook HMAC-SHA256-Validation (45 Min)
cd ~/source/mcps/mcp-whatsapp
# 1. APP_SECRET aus Meta Developer Console kopieren
# https://developers.facebook.com/apps/<app-id>/settings/basic/
# "App Secret" → "Show"
# 2. In AWS Secrets Manager ablegen
aws secretsmanager create-secret \
--name mcp-whatsapp/META_APP_SECRET \
--secret-string "<wert>" \
--profile av-prod
# 3. CDK-Stack: secrets-Mapping erweitern (cdk/stack.ts)
# Pattern siehe vf-hosted-Stack (ContainerImage.fromEcrRepository mit secrets:{...})
# Variable: META_APP_SECRET
# 4. server.py — Webhook-Handler:
# pip install (oder uv add) — hmac ist stdlib, kein neuer depServer-Patch (server.py:569 Bereich):
import hmac, hashlib, os
def verify_meta_signature(body: bytes, signature_header: str | None) -> bool:
if not signature_header or not signature_header.startswith("sha256="):
return False
secret = os.environ["META_APP_SECRET"].encode()
expected = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)
@app.post("/webhook")
async def webhook(request: Request):
body = await request.body()
sig = request.headers.get("X-Hub-Signature-256")
if not verify_meta_signature(body, sig):
raise HTTPException(401, "invalid signature")
# ... bisheriger CodeZusaetzlich (F-Hardcode-Fallback):
# raus:
VERIFY_TOKEN = os.environ.get("WHATSAPP_WEBHOOK_VERIFY_TOKEN", "agentic-friseur-verify-2026")
# rein:
VERIFY_TOKEN = os.environ["WHATSAPP_WEBHOOK_VERIFY_TOKEN"]Deploy: cdk deploy McpWhatsappStack --profile av-prod. Smoke-Test: Meta-Webhook-Tester schickt signed Payload → 200. curl mit falscher/keiner Signatur → 401.
Status nach Heute Abend: F001, F002, F003 → DONE. 2 CRITs offen (beide AWS-Auth).
Morgen — 60 Min, raeumt die anderen 2 CRITs ab
Block C — AWS Mgmt-Account haerten (45 Min)
F004 — Mgmt-Account Root-Keys + mkuehlmann MFA
# 1. Login Mgmt-Account Root via Console
# https://signin.aws.amazon.com/console — Email + Pwd + (noch keine MFA)
# -- Bei Login: MFA-Device direkt anlegen (Hardware-Key bevorzugt, sonst Authy/1Password)
# 2. Root Access Keys deaktivieren + loeschen
# IAM → "Security credentials" (im Root-Menue oben)
# Access Keys → falls vorhanden: "Make inactive" → "Delete"
# 3. IAM-User mkuehlmann MFA aktivieren
aws iam list-mfa-devices --user-name mkuehlmann --profile av-mgmt
# Wenn leer:
# Console: IAM → Users → mkuehlmann → "Security credentials" → "Assign MFA device"
# 4. Alte Access-Keys von mkuehlmann pruefen + rotieren
aws iam list-access-keys --user-name mkuehlmann --profile av-mgmt
# Wenn > 90 Tage alt: neuen Key anlegen, alten deaktivieren, nach 7 Tagen loeschen.
# Idealerweise: User-Keys ganz raus, ueber Identity-Center-Role arbeiten.F005 — Root-MFA fuer av-production, av-becker, mk-privat (15 Min)
Pro Account: Console-Login als Root → IAM → MFA-Device → Authenticator-Code scannen.
1. av-production: https://491085415349.signin.aws.amazon.com/console
2. av-becker: https://<account-id>.signin.aws.amazon.com/console
3. mk-privat: <id>.signin.aws.amazon.com/console
(Account-IDs aus intern/capabilities/aws/accounts.md.)
Status nach Morgen: 5/5 CRITs DONE.
Diese Woche — 4h, raeumt 7 HIGHs ab
Block D — Hetzner-Spur (45 Min)
F007 — DNSSEC + CAA aktivieren
1. agenticventures.de (Cloudflare-NS):
- Cloudflare Dashboard → DNS → Settings → "Enable DNSSEC"
- Cloudflare gibt DS-Record aus → bei Domain-Registrar (Hetzner-Domain-Robot
oder wo agenticventures.de registriert ist) DS-Record eintragen
- DNS → Records → CAA hinzufuegen:
Name: @
Tag: issue
Value: letsencrypt.org
Plus: issue: amazonaws.com (fuer ACM-Cloudfront)
Plus: iodef: mailto:hello@marvinkuehlmann.com
2. marvinkuehlmann.com (Route 53):
aws route53 enable-hosted-zone-dnssec --hosted-zone-id <id> --profile mk-privat
# → KMS-Key wird auto-erstellt, DS-Record kommt zurueck
# DS-Record bei Domain-Registrar (Hetzner?) eintragen
# Plus CAA-Records analog
Verifikation: dig +dnssec agenticventures.de SOA → AD-Flag gesetzt. dig CAA agenticventures.de → Records sichtbar.
F008 — pdf.agenticventures.de absichern
1. Stirling-Default-Login resetten:
ssh root@av-tools-stirling-01.av (IP aus Hetzner-Console)
docker exec stirling /bin/sh -c 'rm /configs/settings.yml' # Reset Force-Login
# oder neue settings.yml mit `security.loginAttempts.maxAttempts: 5` + neuem User
2. Cloudflare Access-App anlegen (analog F001):
- Subdomain: pdf.agenticventures.de
- Policy: Marvin-Email + ggf. Team-Mitglieder
- Damit: kein direkter Stirling-Login mehr noetig, Cloudflare ist die Auth-Schicht
F009 — SSH-Firewall av-tools-stirling-01
# Aktuelle Firewall finden
# (via mcp__hetzner__list_firewalls + get_firewall)
# Ziel: SSH (22) nur von:
# - Marvins fixer IP (falls vorhanden) oder
# - Tailscale-CGNAT (100.64.0.0/10) — wenn Tailscale eingerichtet
# - Notfall-Bastion (falls vorhanden)
# Quick-Fix wenn keine fixe IP: SSH-Port von 22 auf hohen non-standard Port
# (security-by-obscurity, aber reduziert Bot-Scans um 99%) + fail2ban schaerferBlock E — agents-platform + Vault (1h)
F006 — npm audit fix
cd ~/source/agents-platform
npm audit fix
npm run synth # CDK noch buildbar?
git add package-lock.json
git commit -m "sec: npm audit fix — fast-uri CVE 7.5 via aws-cdk-lib"F010 — .claude/settings.local.json Allowlist aufraeumen
cd ~/source/agentic-ventures
cat .claude/settings.local.json
# pruefen: Wildcards wie Bash(aws s3 *), Bash(aws lambda *), Bash(op item *) raus
# stattdessen: enge Patterns Bash(aws s3 ls), Bash(aws s3 cp) etc.
# globale skipDangerousModePermissionPrompt: true → falseSkill /fewer-permission-prompts kann hier helfen — laeuft analytisch ueber Transcripts und schlaegt enge Patterns vor.
Block F — Icking-Rebuild (2h)
F011 — /chat Rate-Limit verdrahten
# fastapi_app.py
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/chat")
@limiter.limit("20/minute")
async def chat(request: Request, ...):
...Settings: RATE_LIMIT_CHAT="20/minute" aus env, default fallback. Test: 21 Calls von gleicher IP innerhalb 60s → 429.
F012 — cloudflared essential=true
// infra/icking-stack.ts — taskDef.addContainer fuer cloudflared
{
essential: true, // war false
...
}Deploy. Smoke: kill cloudflared-Container → App-Container faellt mit, ECS startet Task neu, kein orphan reachable.
Naechste 2 Wochen — MEDs (5h Gesamt)
Reihenfolge nach Blast-Radius:
| # | Finding | Aufwand | Hinweis |
|---|---|---|---|
| F022 | Icking Prompt-Injection: User-Content + RAG-Hits | 2h | Anti-Exfil-Klausel im System-Prompt, max_tokens-Cap 1024 |
| F020 | GuardDuty Org-weit aktivieren | 30 Min | Delegated Admin auf av-security oder av-prod |
| F021 | Access Analyzer pro Account | 30 Min | Org-Analyzer reicht — einmal anlegen, deckt alle Sub-Accounts |
| F019 | Secrets-Rotation in av-prod (25 Stueck) | 1h | Pro Secret pruefen ob Rotation moeglich (Lambda-Rotator vs manuell) |
| F014 | mcp-papierkram read-only-Variante | 1h | Zweiter MCP-Mount-Path /papierkram-readonly/mcp mit subset der Tools |
| F015 | mcp-gsuite Migration oauth2client → google-auth | 2h | Code-Patch + Re-OAuth-Flow |
| F016 | mcp-whatsapp Digest-Pinning | 15 Min | Image-SHA in CDK statt :latest |
| F017 | mcp-whatsapp CI/Lint/gitleaks | 1h | Workflow analog mcp-vf-hosted |
| F018 | Vault pre-commit Visibility-Guard | 30 Min | Hook der bei visibility: internal Frontmatter git push origin main blockt — oder zumindest warnt |
| F013 | agents-platform dashboard-presignup Message generischer | 10 Min | ”Diese Email ist nicht autorisiert” → “Anmeldung fehlgeschlagen” |
| F023 | Icking Custom-VPC | nicht jetzt | beim naechsten Stack-Bump |
| F024 | Hetzner age-Key + Volume-LUKS-Pattern ADR | 1h | Vor erstem Industriekunden-Hetzner-Project |
Nicht-Security-Doku-Drift mitnehmen (5 Min)
- Buckets-Map ergaenzen:
av-dashboard-data-*,av-dashboard-frontend-*,av-production-terraform-state,openwebui-vf-uploads-*→intern/capabilities/aws/buckets.md - Apps-Inventar ergaenzen:
inference-service-prod,presenton-vf→intern/capabilities/apps/ - mcp-vf-hosted DNS-Cutover-Status pruefen + im MCPs-Index korrigieren
Tracking
Wenn ein Finding erledigt:
baseline.jsoneditieren:"fixed": truesetzen- Commit-Message Format:
sec(<scope>): fix <F-ID> — <kurztitel> - Beim naechsten
security-audit-biweekly-Lauf (1. Juni) wird das automatisch im Diff sichtbar
Eskalations-Pfade
- Mgmt-Account-Aussperrung waehrend MFA-Setup: AWS-Support, Tier-Premium-Ticket (kann 24h dauern) — vor MFA-Setup Recovery-Email + Hardware-Key griffbereit.
- mcp-whatsapp-Outage waehrend Cloudflare-Access-Setup: Friseur-Bot pausiert temporaer. Falls Pilot-Kunde aktiv testet: kurz Bescheid geben („sicherheits-update, ca. 30 Min”).
- GitHub-PAT-Revoke bricht laufenden Skill: unwahrscheinlich aber moeglich. Neue PAT direkt nach Revoke anlegen, ~5 Min Lucke.
Verwandt
- report — der konsolidierte Audit-Report
- baseline.json — Status-Tracking
- active-work — wo Tasks reinfliessen
- SKILL — Methodik fuer naechste Laeufe