- 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>
4.5 KiB
Konzept — Sicherheitshärtung (Lane H)
Status: Entscheidungsvorlage / Backlog (kein Code). Produkt: certvia (ISMS-Tool). Freigegeben, weil der Auth-Umbau (Option C, WS0–WS5 + Contract) abgeschlossen ist. Parallel zur Backup-Lane baubar. Trifft die Backup-Lane genau am gemeinsamen Verschlüsselungs-Primitiv (§9 in
docs/KONZEPT-backup-restore.md).
0. Umfang
Alles unter „DB-/Plattform-Härtung", das nicht ins Backup-Konzept gehört: Pepper, Host-Encryption at-rest, verschlüsselte Backups (operativer Bezug zu §9), DB-Connection-TLS, Secrets-Register. Argon2-Parameter sind erledigt.
1. Pepper (Passwort-Hash) — App-Härtung
Ist: src/server/password.ts nutzt Argon2id mit fixierten Parametern (ARGON2_OPTIONS), Salt automatisch, kein Pepper.
Soll: globaler Pepper über die Argon2-secret-Option — bei hash() und jeder verify()-Stelle.
- Schlüsselquelle: neues Umgebungs-Secret
PASSWORD_PEPPER(32-Byte hex), analogMFA_ENC_KEY. - Umsetzung schlank dank Option C: der verify-Pfad ist nach WS1/WS4 zentralisiert (Login gegen Identity) →
secretan genau den zentralen hash/verify-Helfer geben statt an 6 verstreute Stellen. Bevorzugt einenverifyPassword(hash, pw)-Wrapper einführen (falls noch nicht), damit Pepper eine Stelle ist. - ⚠ Nicht rotierbar ohne Passwort-Reset für alle (kein Rehash-on-Login) — wie
MFA_ENC_KEY. Deshalb jetzt setzen, solange test/dev-DBs frisch/leer sind. - Restore-Kohärenz: Pepper ist ein Umgebungs-Secret, steht nicht in Backup-Artefakten → Restore nur in Umgebung mit passendem Pepper (siehe Backup-Konzept §9).
Dateien:
src/server/password.ts(+ zentraler verify-Wrapper), alleverify()-Aufrufer,.env*-Beispiele, Deploy-Env.
2. Host-Encryption at-rest (Live-DB + Objektspeicher)
Ist: pgvector/pgvector (Community-Postgres, kein TDE); pgdata/MinIO-Volumes liegen im Klartext auf der VPS-Platte.
Soll: LUKS/dm-crypt bzw. provider-verschlüsseltes Volume für das Daten-Volume (deckt pgdata und MinIO).
- Schützt gegen physischen Plattendiebstahl/Decommission; transparent im Betrieb (im RAM entschlüsselt) — kein Schutz gegen Live-Server-Kompromittierung (dafür App-Feld-Encryption + Zugriffskontrollen).
- Operativer Runbook-Punkt (nicht App-Code): einmalig einrichten, in
docs/DEPLOY-PROD-CONTABO.mdals Go-Live-Checkliste ergänzen.
3. Verschlüsselte Backups (operativer Bezug zu §9)
Setzt die Entscheidung aus docs/KONZEPT-backup-restore.md §9 operativ um:
- pgBackRest mit
repo-cipher-type=aes-256-cbc+repo-cipher-pass→ S3/MinIO. agefür Per-Tenant-/DSGVO-Artefakte; restic für MinIO-Dateien.- Verdrahtung in Coolify (Cron/Job + Zugangsdaten), Retention, Restore-Test dokumentieren.
4. DB-Connection-TLS
Ist: DATABASE_URL/RLS_DATABASE_URL ohne sslmode → App↔Postgres ohne TLS, aber netz-isoliert (internes Docker-Netz backend, internal: true).
Soll: sslmode=require (bzw. verify-full mit CA) sobald die DB je den Host/eine Vertrauensgrenze verlässt (managed DB, eigener DB-Host). Aktuell durch die Netz-Isolation gemindert → Phase 2 / bedingt, nicht dringlich.
5. Secrets-Register „restore-/betriebs-kritisch"
Ein gepflegtes Register (Ort: Org-Passwortmanager) mit Ownership + Rotationsregel:
| Secret | Zweck | Rotierbar? |
|---|---|---|
AUTH_SECRET |
Session-/JWT-Signatur | ja (invalidiert Sessions) |
MFA_ENC_KEY |
TOTP-Secret-Verschlüsselung (AES-256-GCM) | nein ohne MFA-Neueinrichtung |
PASSWORD_PEPPER |
Passwort-Pepper (neu, §1) | nein ohne Passwort-Reset |
| pgBackRest-Repo-Key | Cluster-Backup-Verschlüsselung | ja (mit Repo-Rekey) |
age-Keypair |
Per-Tenant-/DSGVO-Artefakte | ja (neue Artefakte) |
| Regeln: je Umgebung getrennt (test/dev/prod); niemals im selben Bucket wie die Artefakte; Offline-Kopie versiegelt. |
6. Erledigt (nur Verweis)
- Argon2id-Parameter fixiert/dokumentiert (
src/server/password.ts,ARGON2_OPTIONS) — gemerged. - TOTP-Secrets AES-256-GCM at-rest (
src/server/secret-crypto.ts, SEC3-d) — bestand bereits.
7. Phasen & offene Entscheidungen
Phasen: (1) Pepper (App, jetzt — leere DBs). (2) Host-Encryption + verschlüsselte Backups (Ops, mit Backup-Lane). (3) Secrets-Register formalisieren. (4) DB-TLS + Vault/KMS = Phase 2. Offen (PM/ISB/DSB): Pepper-Env-Name bestätigen + „nicht rotierbar" akzeptieren · Vault/KMS-Zeitpunkt · optional Spalten-Verschlüsselung (pgcrypto) für besonders sensible PII.