- docker-compose.coolify(.prebuilt).yml: Service craftvia-worker (Target worker, Chromium, shm_size 1gb, gleiche Härtung), Craftvia-Variablen für app und worker. - Dockerfile: worker-Stage mit HOME=/home/app (Chromium-Profil für non-root); lokaler docker build der Targets runner und worker erfolgreich, PDF-Erzeugung im Image geprüft. - .env.example/.env.prod.example/.env.coolify.example: alle Craftvia-Variablen inkl. RLS, KI-Provider, PDF_CHROMIUM_PATH, OFFLINE_MAX_DAYS, API_RATE_LIMIT_*, AI_GENERATION_RETENTION_DAYS, AI_MONTHLY_TOKEN_LIMIT. - CI (.github, .gitea): Job gate mit Postgres (pgvector) und Redis als Service: migrate deploy, seed, Passwort für craftvia_app, tsc, lint, build, npm run test. - docs/craftvia/DEPLOY.md (aus den Certvia-Deploy-Docs abgeleitet): Architektur, Domains, Secrets, Worker, Migrationen, RLS-Aktivierung, Backup/Restore, KI, Rate Limits, Aufbewahrung, Smoke, Update/Rollback. build-and-push-images.sh baut craftvia-worker. - Certvia-/ISMS-Dokumente aus docs/ nach docs/_certvia-archiv/ (mit README); Verweise in README.md und Skript-Kommentaren angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.5 KiB
Secrets-Register — restore-/betriebs-kritische Schlüssel (certvia)
Produkt: certvia (ISMS-Tool). Umsetzung der Härtung §5 (
docs/KONZEPT-haertung.md) und Backup-Konzept §9 (docs/KONZEPT-backup-restore.md). Ops-/Governance-Dokument, kein Code. Strikt getrennt von jedem anderen Produkt (insb. „visitvia") — eigene Secrets, eigener Tresor-Bereich.
Dieses Register ist die einzige verbindliche Übersicht, welche Secrets es gibt, wer sie besitzt, wie (und ob) sie rotiert werden und wo sie liegen. Führung: ISB. Ablageort der Werte: Org-Passwortmanager (je Umgebung getrennter Ordner) + versiegelte Offline-Kopie.
Grundregeln (gelten für ALLE Einträge)
- Je Umgebung getrennt —
test/dev/prodhaben eigene, unterschiedliche Werte. Niemals einen prod-Wert in test/dev wiederverwenden (oder umgekehrt). - Nie im Artefakt-Bucket — Secrets liegen niemals im selben Bucket/Speicher wie die verschlüsselten Backups oder DSGVO-Exporte. Verlust des Keys = Verlust der Wiederherstellbarkeit.
- Nie im Repo / nie im Backup-Artefakt — kein Secret ins Git, keins ins Backup-Payload. Verteilung nur als Coolify-Env-Secret + Passwortmanager.
- Versiegelte Offline-Kopie — pro Umgebung eine versiegelte Offline-Kopie (z. B. verschlossener Umschlag / getrennter Offline-Tresor) für den Totalausfall des Passwortmanagers.
- Rotation protokollieren — jede Rotation mit Datum, Auslöser und ausführender Person im Passwortmanager-Eintrag vermerken.
Register
| Secret | Zweck | Eigentümer (Owner) | Rotierbar? | Rotationsregel je Umgebung |
|---|---|---|---|---|
AUTH_SECRET |
Session-/JWT-Signatur (Auth.js) | ISB / Betrieb | Ja | Rotation invalidiert alle aktiven Sessions (Nutzer müssen neu einloggen). Bei Verdacht auf Kompromittierung sofort, sonst periodisch (z. B. jährlich). Je Umgebung eigener Wert. |
MFA_ENC_KEY |
Verschlüsselung der TOTP-Secrets at-rest (AES-256-GCM) | ISB | NEIN¹ | Nicht rotierbar ohne MFA-Neueinrichtung. „Rotation" = Reset: alle Nutzer müssen MFA neu einrichten (alte TOTP-Secrets werden unlesbar). Nur bei Kompromittierung mit geplantem Reset. |
PASSWORD_PEPPER |
Passwort-Pepper (Argon2-secret, Phase 1) |
ISB | NEIN¹ | Nicht rotierbar ohne Passwort-Reset für alle (kein Rehash-on-Login). „Rotation" = Reset: erzwungener Passwort-Neusatz aller Konten. Bewusst einmalig gesetzt (leere DBs). |
BACKUP_ENC_KEY |
Client-seitige Verschlüsselung des App-Backup-Exports (AES-256-GCM); Fallback AUTH_SECRET |
ISB / Betrieb | Bedingt² | Neuer Key gilt nur für neue Artefakte; alte Backups bleiben nur mit dem alten Key entschlüsselbar → Altschlüssel bis Ablauf der Retention aufbewahren. Je Umgebung eigener Wert. |
pgBackRest-Repo-Key (repo-cipher-pass) |
AES-256-Verschlüsselung des Cluster-Backup-Repos (Postgres WAL/Base) | Betrieb | Ja³ | Rotierbar mit Repo-Rekey; alte Repos/Artefakte brauchen den Altschlüssel → bis Retention-Ende aufbewahren. Je Umgebung eigenes Repo + eigener Key. |
age-Keypair |
Verschlüsselung Per-Tenant-Export + DSGVO-Pakete (X25519) | ISB / DSB | Ja³ | Neuer Recipient gilt für neue Artefakte; Private Key der Alt-Keys bis Retention-Ende aufbewahren (sonst Alt-Exporte nicht entschlüsselbar). Je Umgebung eigenes Keypair. |
| restic-Repo-Passwort | Verschlüsselung des MinIO-Objekt-Backups (restic, AES-256) | Betrieb | Ja³ | Rotierbar (restic key add/remove); Altschlüssel bis Retention-Ende gültig halten. Je Umgebung eigenes Repo + eigenes Passwort. |
¹ Nicht-rotierbar markiert: PASSWORD_PEPPER und MFA_ENC_KEY sind keine rotierbaren
Secrets im üblichen Sinn — eine „Rotation" bedeutet einen Reset/Neuverschlüsselung mit
Benutzer-Impact (Passwort- bzw. MFA-Neueinrichtung für alle). Deshalb bewusst einmalig setzen
(solange DBs leer/frisch) und wie einen Wiederherstellungsschlüssel behandeln.
² BACKUP_ENC_KEY ist technisch tauschbar, aber jeder alte Backup-Stand bleibt an seinen
Erzeugungs-Key gebunden → nicht „rotieren und alten Key wegwerfen".
³ Backup-Keys (pgBackRest / age / restic) sind rotierbar, aber Altschlüssel müssen bis zum
Ende der jeweiligen Retention aufbewahrt werden, sonst werden ältere Backups unwiederherstellbar.
⚠ Restore-Kohärenz (verbindliche Vorbedingung)
PASSWORD_PEPPER, MFA_ENC_KEY und BACKUP_ENC_KEY sind Umgebungs-Secrets und stehen NICHT
im Backup-Artefakt. Ein Restore in eine Umgebung mit anderen Secrets bricht:
- anderer
PASSWORD_PEPPER→ alle Passwort-Prüfungen schlagen fehl (kein Login möglich). - anderes
MFA_ENC_KEY→ TOTP-Secrets nicht entschlüsselbar → MFA-Prüfung bricht. - anderes
BACKUP_ENC_KEY→ das AES-256-GCM-Backup-Artefakt lässt sich gar nicht entschlüsseln.
Vorbedingung vor jedem Restore (auch im Restore-Runbook docs/DEPLOY-PROD-CONTABO.md verankert):
Die Zielumgebung hält exakt die Secrets, mit denen das Backup erzeugt wurde — oder es wird
ein Passwort-/MFA-Reset bzw. eine Neuverschlüsselung eingeplant. Ein Cross-Environment-Restore
(z. B. prod → staging zum Debuggen) läuft nur mit den prod-Secrets oder mit anschließendem Reset.
Ablage & Zugriff (Runbook-Kurzform)
- Primär: Org-Passwortmanager, je Umgebung getrennter Ordner, Zugriff rollenbasiert (ISB + Betrieb).
- Sekundär: versiegelte Offline-Kopie je Umgebung (Notfall/DR).
- Verteilung an die App: ausschließlich als Coolify-Env-Secret (Runtime), nie ins Repo/Image.
- Host-Encryption-Passphrase (LUKS-Volume, s.
docs/DEPLOY-PROD-CONTABO.md): ebenfalls hier führen — dieselben Grundregeln (getrennt je Umgebung, offline versiegelt, nie im Backup-Bucket). - Bei Personalwechsel: rotierbare Secrets (
AUTH_SECRET, Backup-Keys) neu setzen; Zugriff im Passwortmanager entziehen. Nicht-rotierbare (PASSWORD_PEPPER/MFA_ENC_KEY) nur bei begründetem Kompromittierungsverdacht — dann mit geplantem Reset.
Bezug / Weiterführendes
docs/KONZEPT-haertung.md§5 (Register-Anforderung), §1–§3 (Pepper, Host-Encryption, Backups).docs/KONZEPT-backup-restore.md§9 (gemeinsames Verschlüsselungs-Primitiv, Schlüsselverwaltung).docs/DEPLOY-PROD-CONTABO.md(Host-Encryption + verschlüsselte Backups + Restore-Kohärenz, Ops-Runbook).- Phase 2 (offen): Upgrade-Pfad auf HashiCorp Vault / Provider-KMS (Rotation, Audit,
Trennung) sowie DB-Connection-TLS (
sslmode) — bewusst zurückgestellt, nicht Teil dieser Lane.