- 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>
51 lines
4.5 KiB
Markdown
51 lines
4.5 KiB
Markdown
# 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), analog `MFA_ENC_KEY`.
|
||
- **Umsetzung schlank dank Option C:** der verify-Pfad ist nach WS1/WS4 zentralisiert (Login gegen Identity) → `secret` an genau den zentralen hash/verify-Helfer geben statt an 6 verstreute Stellen. Bevorzugt einen `verifyPassword(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), alle `verify()`-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.md` als 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.
|
||
- **`age`** fü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.
|