Zum Inhalt springen
Infrastruktur & Technologie

Claude Code als Sicherheitskontrolle im DevOps-Workflow

DevOps-Runbook für automatisierte Security Reviews mit Claude Code: Installation, Custom Commands, Audit-Scripts und CI-Integration.

Mansoor Ahmed
Mansoor Ahmed
Head of Engineering 16 Min. Lesezeit

Moderne self-hosted App-Stacks bestehen aus vielen Komponenten: Supabase Platform, Next.js App Layer, Edge Functions, Background Jobs und externe APIs. Mit jeder Komponente steigt die Wahrscheinlichkeit für Konfigurationsfehler, Secret Leaks, fehlende Policies und ungesicherte Endpunkte.

Traditionelle Security-Scanner prüfen statische Regeln. Sie finden service_role in einer .env Datei, aber sie erkennen nicht, dass eine neue Server Action denselben Ownership-Check vermisst, der in allen anderen Server Actions vorhanden ist. Sie melden offene Ports, aber sie erkennen nicht, dass eine Firewall-Änderung letzte Woche zusammen mit einem neuen Container einen unbeabsichtigten Zugriffspfad eröffnet hat.

Claude Code schliesst diese Lücke als kontextuelle Analyse-Schicht über dem gesamten Stack. Untersuchungen zeigen, dass kontextuelle Code-Reviews bis zu 31% mehr Sicherheitsprobleme identifizieren als rein regelbasierte Scanner (GitHub Security Lab 2024).

Claude Code ersetzt keine deterministischen Checks. Es ergänzt sie, indem es die Ergebnisse interpretiert, Zusammenhänge erkennt und Empfehlungen priorisiert.

Dieses Runbook beschreibt, wie Claude Code konkret installiert, konfiguriert und in den DevOps-Workflow integriert wird.

Auf einen Blick - Artikel 5 von 6 der DevOps-Runbook-Serie

  • Claude Code läuft ausschließlich auf dem Audit-Server (nicht auf Produktion)
  • Drei-Ebenen-Modell: deterministische Checks, Runbook-Regeln, Claude Kontextanalyse
  • Custom Commands in .claude/commands/ definieren den Prüfumfang
  • Headless Mode mit --allowedTools auf Read,Grep,Glob einschränken (kein Bash)
  • Wöchentlicher Cron-Audit plus Ad-hoc-Reviews bei Pull Requests

Serien-Inhaltsverzeichnis

Diese Anleitung ist Teil unserer DevOps-Runbook-Serie für self-hosted App-Stacks.

  1. Supabase Self-Hosting Runbook
  2. Next.js über Supabase sicher betreiben
  3. Supabase Edge Functions sicher einsetzen
  4. Trigger.dev Background Jobs sicher betreiben
  5. Claude Code als Sicherheitskontrolle im DevOps-Workflow - dieser Artikel
  6. Security Baseline für den gesamten Stack

Die ersten vier Artikel beschreiben die einzelnen Komponenten - von der Supabase-Infrastruktur über die Next.js Sicherheitschecks bis zu den Trigger.dev Task-Konfigurationen. Dieser Artikel beschreibt die Security-Kontrollschicht darüber.

Architekturüberblick

Claude Code arbeitet nicht auf dem Produktionsserver, sondern auf dem Audit-Server (siehe Artikel 1). Es hat Read-Only-Zugang zu den Daten, die es analysiert.

Produktionsserver (supabase-prod)
   |
   +-- Supabase Stack
   +-- Next.js App
   +-- Edge Functions
   +-- Trigger.dev
   |
   +---- SSH (read-only) ----> Audit-Server (audit-runner)
                                   |
                                   +-- Deterministische Checks
                                   |   +-- Port Scans (nmap)
                                   |   +-- Firewall Diff (iptables-save)
                                   |   +-- Container Versionen (docker images)
                                   |   +-- RLS Status (psql)
                                   |   +-- npm audit
                                   |   +-- grep-basierte Code Checks
                                   |
                                   +-- Claude Code (headless)
                                   |   +-- Analysiert Check-Ergebnisse
                                   |   +-- Liest Git Diffs
                                   |   +-- Prüft Configs gegen Runbooks
                                   |   +-- Erstellt priorisierten Report
                                   |
                                   +-- Report -> DevOps Team (Mensch entscheidet)

Claude Code führt keine Änderungen am Produktionssystem aus. Keine Deployments, keine Secret-Rotation, keine Container-Stops.

Grundprinzip: Drei Ebenen der Sicherheitsprüfung

Ebene 1: Deterministische Checks (Scripts)
   -> Objektive, wiederholbare Ergebnisse
   -> Beispiel: "Port 5432 ist von aussen erreichbar" = Fakt

Ebene 2: Runbook-Regeln (Markdown-Dateien)
   -> Definierter Soll-Zustand des Stacks
   -> Beispiel: "Postgres darf nur auf 10.0.1.10 lauschen"

Ebene 3: Claude Code Kontextanalyse (headless)
   -> Interpretiert Ergebnisse, erkennt Zusammenhänge
   -> Beispiel: "Port 5432 ist offen UND der neue Container
     hat eine direkte DB-Connection. Das ist ein Problem."

Die Ebenen bauen aufeinander auf. Claude bekommt die Ergebnisse von Ebene 1 und die Regeln von Ebene 2 als Input und erstellt daraus seinen kontextuellen Review.

Vergleich der drei Prüfebenen

EigenschaftDeterministische ChecksRunbook-RegelnClaude Code Analyse
AusführungAutomatisch (Scripts)ReferenzdokumentHeadless Mode (CLI)
ErgebnisFakt (ja/nein)Soll-ZustandInterpretation + Priorität
FrequenzTäglichStatisch (bei Änderung)Wöchentlich / bei PR
StärkeZuverlässig bei bekannten MusternDefinierte StandardsErkennt unbekannte Muster
SchwächeSieht keine ZusammenhängeNicht selbstprüfendNicht deterministisch
Beispiel”Port 5432 offen""Postgres nur auf 10.0.1.10""Port offen UND neuer Container = Problem”

Teil A - Claude Code einrichten

A1 - Installation und Konfiguration auf dem Audit-Server

Umsetzung

Claude Code wird auf dem Audit-Server installiert, nicht auf dem Produktionsserver.

# Auf dem audit-runner Server

# Node.js installieren (falls nicht vorhanden)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
apt-get install -y nodejs

# Claude Code installieren
npm install -g @anthropic-ai/claude-code

# API Key konfigurieren
# Eigener API Key mit Budget-Limit für CI/CD
export ANTHROPIC_API_KEY="sk-ant-..."

# In .bashrc oder .env für den audit-user persistieren
echo 'export ANTHROPIC_API_KEY="sk-ant-..."' >> /home/audit/.bashrc

Headless Mode testen:

# Einfacher Test: funktioniert Claude Code non-interaktiv?
echo "Hello" | claude -p "Antworte mit OK wenn du das lesen kannst" \
  --output-format text

# Erwartung: "OK" oder ähnliche Bestätigung

Prüfbare Bedingung

# Claude Code installiert?
claude --version
# Erwartung: Versionsnummer

# API Key gesetzt?
test -n "$ANTHROPIC_API_KEY" && echo "OK" || echo "FEHLT"

# Headless Mode funktioniert?
claude -p "Sage nur: OK" --output-format text --max-turns 1 2>/dev/null
# Erwartung: "OK"

Failure Scenario

Wenn Claude Code auf dem Produktionsserver läuft und dort Bash-Zugriff hat, könnte ein fehlerhafter Prompt theoretisch Befehle auf dem Produktionssystem ausführen. Auf dem Audit-Server hat Claude Code nur Zugriff auf die dort gesammelten Reports und Config-Kopien, nicht auf das Live-System.

A2 - Custom Security Review Command einrichten

Umsetzung

Claude Code unterstützt Custom Commands über Markdown-Dateien in .claude/commands/. Diese Commands definieren den Kontext und die Prüfregeln für den Security Review.

# Im Infrastruktur-Repository
mkdir -p .claude/commands
# .claude/commands/security-review.md

Du bist Security Reviewer für einen self-hosted Stack bestehend aus:
- Supabase (PostgreSQL, PostgREST, GoTrue, Kong, Edge Functions)
- Next.js (App-Schicht, Server Actions, Route Handler)
- Trigger.dev v3 (Background Jobs, self-hosted)
- Alles auf Hetzner Cloud (EU, eigene Server)

Du erhältst den Output der deterministischen Security Checks und die
aktuellen Konfigurationsdateien.

Prüfe folgendes:

## Infrastruktur (Artikel 1)
- Sind unerwartete Ports von aussen erreichbar?
- Hat sich die Firewall gegenüber der Baseline geändert?
- Sind Container-Versionen gepinnt und aktuell?
- Ist das TLS-Zertifikat noch mindestens 14 Tage gültig?
- Liegt das letzte Backup weniger als 26 Stunden zurück?

## Next.js (Artikel 2)
- Taucht service_role in NEXT_PUBLIC Variablen oder im Client-Bundle auf?
- Haben alle Server Actions einen getUser() Auth Check?
- Haben alle Route Handler einen getUser() Auth Check?
- Existiert middleware.ts mit getUser() (nicht getSession())?
- Sind Security Headers in next.config.js gesetzt?

## Edge Functions (Artikel 3)
- Haben alle Webhook-Functions eine Signaturprüfung?
- Gibt es Functions mit Business-Logik statt Integrations-Logik?
- Ist CORS korrekt konfiguriert (kein Wildcard in Produktion)?
- Gibt es hardcoded Secrets im Function-Code?

## Trigger.dev (Artikel 4)
- Haben alle Tasks maxDuration und concurrencyLimit?
- Haben Tasks mit externen API-Calls Idempotency Keys?
- Nutzen Tasks nur die DB-Felder die sie brauchen (kein SELECT *)?
- Gibt es console.log statt dem Trigger.dev logger?

## Stackübergreifend
- Gibt es Architektur-Drift? (Business-Logik in Edge Functions,
  Langläufer in Server Actions, etc.)
- Sind die Änderungen der letzten Woche konsistent mit der
  bestehenden Architektur?
- Gibt es neue Tabellen ohne RLS?
- Gibt es Muster die auf systematische Probleme hindeuten?

Erstelle einen priorisierten Bericht:
- KRITISCH: sofort handeln (Daten-Leak möglich, Port offen, Secret exponiert)
- WARNUNG: diese Woche lösen (fehlende Checks, Drift, veraltete Packages)
- INFO: bei Gelegenheit verbessern (Code-Qualität, fehlende Tests)

Für jedes Finding: Was ist das Problem, warum ist es ein Risiko,
was ist die konkrete Lösung.

Prüfbare Bedingung

# Custom Command existiert?
test -f .claude/commands/security-review.md && echo "OK" || echo "FEHLT"

# Command wird von Claude Code erkannt?
claude -p "/security-review" --max-turns 1 2>/dev/null | head -5
# Erwartung: Claude beginnt mit der Analyse

A3 - CLAUDE.md als Projekt-Kontext

Umsetzung

Claude Code liest automatisch die CLAUDE.md Datei im Projektverzeichnis. Diese Datei gibt Claude den permanenten Kontext über euren Stack.

# CLAUDE.md (im Root des Infrastruktur-Repos)

## Stack-Überblick
Self-hosted Supabase + Next.js + Edge Functions + Trigger.dev v3
auf Hetzner Cloud (EU, eigene Server).

## Server
- supabase-prod: 10.0.1.10 (Supabase, Next.js, Edge Functions)
- audit-runner: 10.0.1.11 (Security Checks, Claude Code, Monitoring)
- trigger-server: separater Docker-Stack für Trigger.dev v3

## Sicherheitsregeln
- service_role Key darf NUR in: lib/supabase/admin.ts und trigger/lib/supabase.ts
- NEXT_PUBLIC_* darf NUR enthalten: SUPABASE_URL und SUPABASE_ANON_KEY
- Alle public-Tabellen müssen RLS aktiviert haben
- Alle Server Actions und Route Handler müssen getUser() aufrufen
- Alle Webhook Edge Functions müssen Signaturen prüfen
- Alle Trigger.dev Tasks müssen maxDuration und concurrencyLimit haben
- PostgreSQL nur auf 10.0.1.10:5432 (internes Interface)
- Supabase Studio nicht von aussen erreichbar
- Trigger.dev Dashboard nicht von aussen erreichbar

## Deployment
- Infrastruktur wird nur über Git deployed (kein manuelles SSH-Editing)
- Edge Functions werden als Dateien in volumes/functions/ abgelegt
- Trigger.dev Tasks werden über npx trigger.dev deploy --self-hosted deployed

## Verzeichnisstruktur
- app/ = Next.js App (Server Actions, Route Handler, Pages)
- lib/supabase/ = Supabase Client Konfiguration
- middleware.ts = Auth Middleware
- volumes/functions/ = Supabase Edge Functions
- trigger/tasks/ = Trigger.dev Tasks
- infra/ = docker-compose, nginx/caddy, Firewall Configs
- scripts/ = Audit Scripts, Backup, Restore
- runbooks/ = Sicherheits-Runbooks

Wer den CLAUDE.md-Ansatz mit den Sicherheitsregeln aus Artikel 2 kombiniert, bekommt eine lückenlose Kontextbasis für automatisierte Reviews.

Teil B - Der Gesamt-Audit-Workflow

Hier kommt zusammen, was die Artikel 1 bis 4 vorbereitet haben. Jeder Artikel hat eigene Check-Scripts. Artikel 5 führt sie zusammen und gibt den Output an Claude Code.

B1 - Das Gesamt-Audit-Script

Umsetzung

Dieses Script sammelt alle Ergebnisse der deterministischen Checks und übergibt sie an Claude Code im Headless Mode.

#!/bin/bash
# scripts/full-security-audit.sh
# Läuft auf dem audit-runner Server

set -euo pipefail

PROD_HOST="10.0.1.10"
REPORT_DIR="/opt/audit/reports"
DATE=$(date +%Y-%m-%d)
REPORT_FILE="${REPORT_DIR}/audit-input-${DATE}.md"

mkdir -p "$REPORT_DIR"

echo "=== Gesamt-Security-Audit ${DATE} ===" | tee "$REPORT_FILE"

# -------------------------------------------
# EBENE 1: Deterministische Checks
# -------------------------------------------

echo -e "\n# Deterministische Check-Ergebnisse\n" >> "$REPORT_FILE"

# --- Infrastruktur (Artikel 1) ---
echo "## Infrastruktur" >> "$REPORT_FILE"

echo "### Offene Ports (extern)" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
nmap -p 22,80,443,3000,3040,5432,5433,8000,9000 app.example.com \
  --open -oG - 2>/dev/null >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### Firewall Diff gegen Baseline" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "iptables-save" | \
  diff /opt/baselines/firewall-baseline.txt - >> "$REPORT_FILE" 2>&1 || true
echo '```' >> "$REPORT_FILE"

echo "### Container Status" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Image}}'" \
  >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

echo "### TLS Zertifikat" >> "$REPORT_FILE"
CERT_EXPIRY=$(echo | openssl s_client -connect app.example.com:443 2>/dev/null | \
  openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
CERT_EPOCH=$(date -d "$CERT_EXPIRY" +%s 2>/dev/null || echo 0)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (CERT_EPOCH - NOW_EPOCH) / 86400 ))
echo "Ablauf: ${CERT_EXPIRY} (${DAYS_LEFT} Tage)" >> "$REPORT_FILE"

echo "### Backup Status" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "ls -lh /opt/backups/*.gpg 2>/dev/null | tail -3" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### RLS Status" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "docker compose exec -T postgres psql -U postgres -c \
  \"SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename;\"" \
  >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

echo "### Disk Usage" >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "df -h / | tail -1" >> "$REPORT_FILE"

# --- Next.js (Artikel 2) ---
echo -e "\n## Next.js App-Schicht" >> "$REPORT_FILE"

echo "### service_role im Client Code" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && grep -rn 'SERVICE_ROLE\|service_role' app/ \
  --include='*.ts' --include='*.tsx' | grep -v 'lib/supabase/admin.ts' | grep -v node_modules" \
  >> "$REPORT_FILE" 2>&1 || echo "Keine Treffer" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### NEXT_PUBLIC mit Secrets" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && grep 'NEXT_PUBLIC_' .env* 2>/dev/null | \
  grep -iE 'service_role|secret|private|database'" \
  >> "$REPORT_FILE" 2>&1 || echo "Keine Treffer" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### Server Actions ohne Auth" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && for file in \$(grep -rl \"'use server'\" app/ --include='*.ts'); do
  if ! grep -q 'getUser' \"\$file\"; then
    echo \"WARNUNG: \$file\"
  fi
done" >> "$REPORT_FILE" 2>&1 || echo "Keine Treffer" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### Route Handler ohne Auth" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && for file in \$(find app/api -name 'route.ts' 2>/dev/null); do
  if ! grep -q 'getUser' \"\$file\"; then
    echo \"WARNUNG: \$file\"
  fi
done" >> "$REPORT_FILE" 2>&1 || echo "Keine Treffer" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

echo "### npm audit" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && npm audit --audit-level=high 2>&1 | tail -20" \
  >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

# --- Edge Functions (Artikel 3) ---
echo -e "\n## Edge Functions" >> "$REPORT_FILE"

echo "### Webhook Functions ohne Signaturprüfung" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "for dir in /opt/supabase/volumes/functions/*-webhook/; do
  [ -d \"\$dir\" ] || continue
  name=\$(basename \"\$dir\")
  if ! grep -qE 'signature|verify|hmac|crypto' \"\$dir/index.ts\" 2>/dev/null; then
    echo \"KRITISCH: \$name hat keine Signaturprüfung\"
  else
    echo \"OK: \$name\"
  fi
done" >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

echo "### Hardcoded Secrets in Functions" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "grep -rn 'sk_live\|sk_test\|whsec_\|Bearer ey' \
  /opt/supabase/volumes/functions/ --include='*.ts' 2>/dev/null" \
  >> "$REPORT_FILE" 2>&1 || echo "Keine Treffer" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"

# --- Trigger.dev (Artikel 4) ---
echo -e "\n## Trigger.dev Tasks" >> "$REPORT_FILE"

echo "### Tasks ohne maxDuration" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && for file in trigger/tasks/*.ts; do
  name=\$(basename \"\$file\" .ts)
  if ! grep -q 'maxDuration' \"\$file\" 2>/dev/null; then
    echo \"WARNUNG: \$name\"
  fi
done" >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

echo "### Tasks ohne Concurrency Limit" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && for file in trigger/tasks/*.ts; do
  name=\$(basename \"\$file\" .ts)
  if ! grep -qE 'concurrencyLimit|queue:' \"\$file\" 2>/dev/null; then
    echo \"WARNUNG: \$name\"
  fi
done" >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

# --- Git Änderungen ---
echo -e "\n## Code-Änderungen (letzte 7 Tage)" >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
ssh deploy@${PROD_HOST} "cd /opt/app && git log --oneline --since='7 days ago'" \
  >> "$REPORT_FILE" 2>&1
ssh deploy@${PROD_HOST} "cd /opt/app && git diff HEAD~10 --stat 2>/dev/null" \
  >> "$REPORT_FILE" 2>&1
echo '```' >> "$REPORT_FILE"

# -------------------------------------------
# EBENE 2: Runbook-Regeln (als Kontext)
# -------------------------------------------

echo -e "\n# Aktive Runbook-Regeln\n" >> "$REPORT_FILE"
cat runbooks/security-baseline.md >> "$REPORT_FILE" 2>/dev/null || \
  echo "security-baseline.md nicht gefunden" >> "$REPORT_FILE"

# -------------------------------------------
# EBENE 3: Claude Code Analyse
# -------------------------------------------

echo ""
echo "=== Deterministische Checks abgeschlossen ==="
echo "=== Starte Claude Code Analyse ==="
echo ""

# Claude Code im Headless Mode aufrufen
# --allowedTools einschränken: nur Lesen, kein Bash, kein Schreiben
CLAUDE_OUTPUT=$(cat "$REPORT_FILE" | claude -p \
  "Du erhältst den vollständigen Security-Audit-Report unseres self-hosted Stacks.
Analysiere ihn gemäss den Regeln in .claude/commands/security-review.md.
Erstelle einen priorisierten Bericht mit KRITISCH / WARNUNG / INFO.
Für jedes Finding: Problem, Risiko, konkrete Lösung." \
  --allowedTools "Read,Grep,Glob" \
  --append-system-prompt "Du bist ein Senior Security Engineer. Antworte auf Deutsch. Sei konkret und priorisiere strikt." \
  --output-format text \
  --max-turns 5 \
  2>/dev/null)

# Report speichern
CLAUDE_REPORT="${REPORT_DIR}/claude-review-${DATE}.md"
echo "$CLAUDE_OUTPUT" > "$CLAUDE_REPORT"

echo ""
echo "=== Claude Code Review abgeschlossen ==="
echo "Report: $CLAUDE_REPORT"

# Bei kritischen Findings: Alert
if echo "$CLAUDE_OUTPUT" | grep -qi "KRITISCH"; then
  echo "$CLAUDE_OUTPUT" | mail -s "KRITISCH: Security Review ${DATE}" ops@example.com
  echo "Alert gesendet an ops@example.com"
fi

Prüfbare Bedingung

# Script ausführbar?
test -x scripts/full-security-audit.sh && echo "OK" || echo "NICHT AUSFÜHRBAR"

# Letzter Audit-Report vorhanden?
ls -la /opt/audit/reports/claude-review-$(date +%Y-%m-%d).md 2>/dev/null
# Erwartung: Datei von heute

# Report enthält die erwarteten Sektionen?
grep -c "KRITISCH\|WARNUNG\|INFO" /opt/audit/reports/claude-review-$(date +%Y-%m-%d).md
# Erwartung: mindestens 1

B2 - Wann der Audit läuft

Drei Trigger-Punkte

1. Wöchentlicher Cron (Sonntag 6:00 Uhr)
   -> Vollständiger Audit über den gesamten Stack
   -> Ergebnis: Priorisierter Report für die Woche

2. Bei Pull Requests (CI/CD)
   -> Nur die geänderten Dateien
   -> Ergebnis: Review-Kommentar am PR

3. Ad-hoc (manuell)
   -> Nach Incidents, größeren Deployments, Infrastruktur-Änderungen
   -> Ergebnis: Sofort-Analyse

Wöchentlicher Cron:

# crontab auf dem audit-runner
0 6 * * 0 /opt/audit/scripts/full-security-audit.sh >> /var/log/security-audit.log 2>&1

PR Review (Git Diff an Claude pipen):

#!/bin/bash
# scripts/pr-security-review.sh
# Wird vom CI bei jedem PR aufgerufen

DIFF=$(git diff origin/main...HEAD)

if [ -z "$DIFF" ]; then
  echo "Kein Diff, kein Review nötig"
  exit 0
fi

echo "$DIFF" | claude -p \
  "Review diesen Git Diff auf Sicherheitsprobleme.
Prüfe insbesondere:
- service_role Nutzung in Client-Code
- Server Actions ohne Auth Check
- Edge Functions ohne Signaturprüfung
- Trigger.dev Tasks ohne maxDuration
- Hardcoded Secrets
- Neue API Endpoints ohne Rate Limiting

Antworte NUR wenn du ein konkretes Problem findest.
Wenn alles in Ordnung ist, sage: Keine Sicherheitsprobleme gefunden." \
  --allowedTools "Read,Grep,Glob" \
  --output-format text \
  --max-turns 3

Ad-hoc Review:

# Manuell nach einem Incident
cd /opt/infra-repo
claude -p "Prüfe den aktuellen Zustand des Repositories auf Sicherheitsprobleme. \
Fokus auf die letzten 5 Commits." \
  --allowedTools "Read,Grep,Glob,Bash(git log*),Bash(git diff*)" \
  --max-turns 10

Prüfbare Bedingung

# Cron Job aktiv?
crontab -l | grep "full-security-audit"
# Erwartung: Eintrag vorhanden

# Letzter Audit weniger als 8 Tage her?
LAST_REPORT=$(ls -t /opt/audit/reports/claude-review-*.md 2>/dev/null | head -1)
if [ -n "$LAST_REPORT" ]; then
  AGE=$(( ($(date +%s) - $(stat -c %Y "$LAST_REPORT")) / 86400 ))
  echo "Letzter Report: $AGE Tage alt"
  [ "$AGE" -gt 8 ] && echo "WARNUNG: Audit überfällig"
fi

Teil C - Was Claude kann und was nicht

C1 - Wo Claude Code besonders stark ist

Claude erkennt Zusammenhänge, die deterministische Checks nicht sehen können:

Architektur-Drift erkennen: Die deterministischen Checks finden, dass eine neue Edge Function existiert. Claude erkennt, dass diese Function Business-Logik enthält, die nach Next.js gehört, weil sie User-Input verarbeitet und Daten transformiert statt einen externen Event zu empfangen.

Muster über Dateigrenzen hinweg: Ein grep findet, dass getUser() in einer Server Action fehlt. Claude erkennt, dass diese Action die einzige von 15 Actions ist, der der Check fehlt, und dass sie letzte Woche in einem Commit hinzugefügt wurde, der auch drei andere Dateien änderte, die alle korrekt sind. Das deutet auf ein Versehen hin, nicht auf ein systematisches Problem.

Priorisierung: Die deterministischen Checks produzieren eine flache Liste von 30 Findings. Claude gruppiert sie: “Die 5 Service-Role-Findings sind alle in der Admin-Library und korrekt. Die 2 fehlenden Auth-Checks in den Route Handlern sind das eigentliche Risiko, weil sie öffentliche API-Endpoints betreffen.”

Konfigurationskontext: Ein Port Scan zeigt, dass Port 8000 offen ist. Claude weiss aus dem Stack-Kontext (CLAUDE.md), dass Port 8000 der interne Kong-Port ist, und prüft ob er nur auf localhost lauschen sollte.

C2 - Was Claude Code nicht kann und nicht tun darf

Claude kann nicht aktiv scannen. Es kann keine Netzwerk-Scans durchführen, keine laufenden Prozesse prüfen und keine Ports von aussen testen. Das machen die deterministischen Tools (nmap, ss, docker ps). Claude analysiert deren Output.

Claude darf nicht auf Produktion schreiben. Die --allowedTools Einschränkung im Headless Mode stellt sicher, dass Claude nur lesen kann:

# RICHTIG: Nur Lese-Tools erlaubt
claude -p "..." --allowedTools "Read,Grep,Glob"

# FALSCH: Bash erlaubt = Claude kann beliebige Befehle ausführen
claude -p "..." --allowedTools "Read,Grep,Glob,Bash"

Wenn Bash nötig ist (z.B. für git log), nur spezifische Befehle erlauben:

--allowedTools "Read,Grep,Glob,Bash(git log*),Bash(git diff*)"

Claude ist nicht deterministisch. Derselbe Input kann zu leicht unterschiedlichen Reports führen. Deshalb ersetzt Claude keine deterministischen Checks. Es interpretiert deren Ergebnisse.

Claude kennt keine Echtzeit-Daten. Es arbeitet mit den Snapshots, die ihm zum Zeitpunkt des Audits übergeben werden. Zwischen Audit und Review-Lektüre kann sich der Zustand geändert haben.

C3 - Sicherheitsregeln für Claude Code im CI

# Auf dem Audit-Server: Claude Code Konfiguration

# 1. Eigener API Key mit Budget-Limit
#    Separater Key, nicht der Entwickler-Key
#    Budget: max. 50 USD/Monat für CI Reviews

# 2. --allowedTools immer einschränken
#    Niemals unbeschränkten Bash-Zugriff in automatisierten Scripts

# 3. --max-turns begrenzen
#    Verhindert Endlosschleifen bei verwirrenden Inputs
#    Empfehlung: 3-5 für PR Reviews, 5-10 für Full Audits

# 4. Output immer in Datei speichern
#    Nicht nur auf stdout, damit der Report nachvollziehbar bleibt

# 5. Claude Code hat keinen SSH-Zugriff auf Produktion
#    Die deterministischen Scripts sammeln die Daten
#    Claude bekommt nur den Text-Output zur Analyse

Deployment-Checkliste

Installation
  [ ] Claude Code auf audit-runner installiert
  [ ] API Key konfiguriert (separater CI Key mit Budget-Limit)
  [ ] Headless Mode funktioniert (Test mit claude -p)

Konfiguration
  [ ] .claude/commands/security-review.md angelegt
  [ ] CLAUDE.md im Infrastruktur-Repo vorhanden
  [ ] --allowedTools auf Read,Grep,Glob eingeschränkt
  [ ] --max-turns auf 5-10 begrenzt

Automation
  [ ] Gesamt-Audit-Script vorhanden und ausführbar
  [ ] Wöchentlicher Cron Job aktiv
  [ ] PR Review Script im CI eingebunden
  [ ] Alert bei kritischen Findings konfiguriert

Sicherheit
  [ ] Claude Code läuft NUR auf dem audit-runner
  [ ] Claude Code hat KEINEN SSH-Zugriff auf Produktion
  [ ] Kein unbeschränkter Bash-Zugriff in --allowedTools
  [ ] Reports werden gespeichert und archiviert
  [ ] Budget-Limit auf dem API Key gesetzt

Fazit

Claude Code ist kein automatischer Security Scanner und kein Ersatz für deterministische Checks. Es ist ein kontextueller Analyst, der die Ergebnisse von Scannern und Grep-Checks interpretiert, Zusammenhänge erkennt und Empfehlungen priorisiert.

Der Workflow ist immer derselbe: Deterministische Tools sammeln Fakten, Claude Code interpretiert sie, ein Mensch entscheidet. Das funktioniert, weil jede Ebene das tut, was sie am besten kann. Scripts sind zuverlässig bei bekannten Mustern. Claude erkennt unbekannte Muster. Menschen treffen Entscheidungen.

Die Kombination aus den Check-Scripts der Artikel 1-4, dem Custom Security Review Command und dem wöchentlichen Audit-Cron ergibt eine Sicherheitskontrolle, die sowohl bekannte als auch unerwartete Risiken aufdeckt.

Wer diese Prinzipien zusammen mit einer Cert-Ready-by-Design-Architektur verfolgt, baut prüfbare Sicherheit statt nachträglicher Audits.

Audit-Checklisten der Serie

Vorbereitete Prompts für Claude Code. Jede Checkliste prüft automatisch die Sicherheitspunkte des jeweiligen Runbooks und meldet BESTANDEN, WARNUNG oder KRITISCH.

Serien-Inhaltsverzeichnis

  1. Supabase Self-Hosting Runbook
  2. Next.js über Supabase sicher betreiben
  3. Supabase Edge Functions sicher einsetzen
  4. Trigger.dev Background Jobs sicher betreiben
  5. Claude Code als Sicherheitskontrolle im DevOps-Workflow - dieser Artikel
  6. Security Baseline für den gesamten Stack

Der nächste und letzte Artikel beschreibt die Security Baseline als maschinenlesbare YAML-Datei, die alle Regeln aus den Artikeln 1-5 in einer einzigen prüfbaren Datei zusammenführt.

Bert Gogolin

Bert Gogolin

Geschäftsführer, Gosign

AI Governance Briefing

Enterprise AI, Regulierung und Infrastruktur - einmal im Monat, direkt von mir.

Kein Spam. Jederzeit abbestellbar. Datenschutzerklärung

Claude Code Security DevOps CI/CD Automation
Artikel teilen

Häufige Fragen

Ersetzt Claude Code deterministische Security-Scanner?

Nein. Claude Code ergänzt deterministische Checks, indem es deren Ergebnisse interpretiert, Zusammenhänge erkennt und Empfehlungen priorisiert. Scripts finden bekannte Muster zuverlässig, Claude erkennt unbekannte Muster und Architektur-Drift.

Darf Claude Code auf dem Produktionsserver laufen?

Nein. Claude Code läuft ausschließlich auf dem Audit-Server mit Read-Only-Zugang. Die deterministischen Scripts sammeln Daten vom Produktionsserver, Claude analysiert nur den Text-Output.

Warum darf Claude Code keinen Bash-Zugriff im CI haben?

Mit unbeschränktem Bash-Zugriff könnte Claude Code theoretisch beliebige Befehle auf dem Server ausführen, auch wenn der Prompt nur eine Analyse anfordert. Die Einschränkung auf Read, Grep und Glob stellt sicher, dass Claude nur lesen und analysieren kann, aber keine Änderungen am System vornimmt.