diff --git a/docs/GOBD_DSGVO_CHECKLIST.md b/docs/GOBD_DSGVO_CHECKLIST.md index 4764545..9a70509 100644 --- a/docs/GOBD_DSGVO_CHECKLIST.md +++ b/docs/GOBD_DSGVO_CHECKLIST.md @@ -13,13 +13,11 @@ Integritätsprüfung per SHA-256 (PROJ-18), Export in EML/MBOX/ZIP/CSV (PROJ-12, **Verbleibende Lücken (priorisiert):** -1. **Migrationssicherheit teilweise** – kein dokumentierter Compliance-Erhalt-Check nach Schema-/ - Index-Migrationen (Punkt 10). -2. **Physische Tenant-Trennung fehlt** – Storage ist logisch (DB) getrennt, nicht auf Dateisystem- +1. **Physische Tenant-Trennung fehlt** – Storage ist logisch (DB) getrennt, nicht auf Dateisystem- Ebene (Punkt 15, bekannt seit Tenant-Isolation-Review). -3. **Zeitstempel/Signaturerhalt (BSI TR 03125)** – nicht implementiert (Nice-to-have, niedrige +2. **Zeitstempel/Signaturerhalt (BSI TR 03125)** – nicht implementiert (Nice-to-have, niedrige Priorität, nur relevant bei signierten Mails im Kundenkreis). -4. **Informationspflicht der Mitarbeiter** – organisatorisch, nicht im Code lösbar (Punkt 13). +3. **Informationspflicht der Mitarbeiter** – organisatorisch, nicht im Code lösbar (Punkt 13). --- @@ -36,7 +34,7 @@ Integritätsprüfung per SHA-256 (PROJ-18), Export in EML/MBOX/ZIP/CSV (PROJ-12, | 7 | **Löschsperre** | ✅ Erfüllt | PROJ-34 implementiert: `emails.retain_until`, `ErrRetentionLock` (`internal/storage/storage.go:27,659-667`), `Delete()` verweigert Löschung vor Fristablauf. Zusätzlich Cron-Purge (PROJ-56c, `cmd/archivmail/cmd_purge.go`, nachts 03:40 via `/etc/cron.d/archivmail`): löscht NUR Mails die BEIDE Bedingungen erfüllen — `retain_until < NOW()` UND vom Nutzer explizit `marked_for_deletion=TRUE` gesetzt (`ListExpiredMarkedMailIDs`, `internal/storage/mark_deletion.go:127-148`). Reiner Fristablauf ohne manuelle Markierung löscht nichts automatisch (Vier-Augen-Prinzip: Frist + Mensch). Der manuelle Admin-Button (`Store.Purge()`, `POST /api/admin/purge`) löscht dagegen alles abgelaufene ohne Markierungspflicht — bewusst getrennte, absichtlich unterschiedliche Semantik für Cron vs. manuelle Aktion. | – | | 8 | **Protokollierung/Audit-Log unveränderlich** | ✅ Erfüllt | PROJ-48 (deployt 2026-06-13): `internal/audit/audit.go` installiert Funktion `audit_log_no_mutation()` + Trigger `audit_log_immutable` (blockt UPDATE/DELETE auf DB-Ebene, idempotent via `CREATE OR REPLACE`/`DROP TRIGGER IF EXISTS`). Zusätzlich append-only JSON-Lines-Datei (`ResolvedLogPath()`, Default `/var/log/archivmail/audit.log`), Schreibfehler blockieren den DB-Pfad nicht (best effort, DB bleibt Quelle der Wahrheit). | – | | 9 | **Nachprüfbarkeit durch Dritte / GDPdU-Export** | ✅ Erfüllt | Export-Funktionen: Einzel-EML/PDF, ZIP-Massenexport mit `manifest.csv` (PROJ-12, `internal/api/export.go`), eDiscovery-ZIP mit `metadata.csv` + README (PROJ-39, `internal/api/ediscovery.go`), CLI-Export inkl. Tenant-Voll-Export (PROJ-15, PROJ-47, `cmd/archivmail/cmd_export.go`), Audit-Log-CSV-Export für Auditoren. | Optional: PROJ-47 (In Review) fertigstellen/QA. | -| 10 | **Migrationssicherheit** | ⚠️ Teilweise | Migrationstools vorhanden (PROJ-19 Mailpiler-Import, PROJ-30 Xapian→Manticore), `initSchema`/idempotente `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` Pattern durchgängig genutzt (z.B. `storage.go:99-100`). Kein dokumentierter "Compliance-Erhalt-Check" nach Migration (z.B. automatischer Verify-Lauf nach Schema-Änderung). | Migrations-Runbook + Post-Migration-Integritätscheck (kann PROJ-18-Job nach Migration triggern) dokumentieren. | +| 10 | **Migrationssicherheit** | ✅ Erfüllt | Migrationstools vorhanden (PROJ-19 Mailpiler-Import, PROJ-30 Xapian→Manticore), `initSchema`/idempotente `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` Pattern durchgängig genutzt (z.B. `storage.go:99-100`). Compliance-Erhalt-Check jetzt dokumentiert: `docs/MIGRATION_RUNBOOK.md` (Zählvergleich Baseline/Post, PROJ-18-Integritätscheck-Log, Manticore-Konsistenz, PROJ-52-Reconciliation, Byte-Vergleich per Export). | – | | 11 | **Maschinelle Auswertbarkeit (Standardformate)** | ✅ Erfüllt | Export als EML (Original-MIME, PROJ-12/15), MBOX (`cmd_export.go`), CSV-Metadaten (PROJ-39). | – | | 12 | **Zeitstempel/Signaturerhalt (BSI TR 03125)** | ❌ Fehlt | Keine S/MIME- oder PGP-Signaturprüfung, kein qualifizierter Zeitstempel-Dienst im Code gefunden. Für die meisten KMU nicht zwingend erforderlich. | Als Backlog-Item vermerken, nur bei Bedarf (signierte Mails im Kundenkreis) als neue Spec aufnehmen. | | 13 | **Informationspflicht der Mitarbeiter** | ❌ Fehlt (organisatorisch) | Nicht im Code prüfbar/lösbar. | Organisatorische Maßnahme: Betriebsvereinbarung / Datenschutzhinweis außerhalb des Systems. | @@ -48,12 +46,10 @@ Integritätsprüfung per SHA-256 (PROJ-18), Export in EML/MBOX/ZIP/CSV (PROJ-12, ## Nächste Schritte (Vorschlag, Priorität absteigend) -1. **Migrations-Runbook** (Punkt 10): Compliance-Erhalt-Check nach Schema-/Index-Migrationen - dokumentieren (kann PROJ-18-Integritätsjob nach Migration triggern). -2. **Physische Tenant-Trennung** (Punkt 15) evaluieren, falls von Kunden gefordert +1. **Physische Tenant-Trennung** (Punkt 15) evaluieren, falls von Kunden gefordert (siehe Memory `project_tenant_isolation_review.md`). -3. Nice-to-have: BSI TR 03125 Zeitstempel/Signaturerhalt (Punkt 12), nur bei konkretem Bedarf. -4. Organisatorisch: Mitarbeiter-Informationspflicht (Punkt 13) außerhalb des Codes klären. +2. Nice-to-have: BSI TR 03125 Zeitstempel/Signaturerhalt (Punkt 12), nur bei konkretem Bedarf. +3. Organisatorisch: Mitarbeiter-Informationspflicht (Punkt 13) außerhalb des Codes klären. **Erledigt seit Stand 2026-06-13:** PROJ-48 (Audit-Log-Unveränderbarkeit), PROJ-49 (Verschlüsselungspflicht/Warnung), PROJ-50 (DSGVO-Löschersuchen), PROJ-51 (Retention-Kategorien), diff --git a/docs/MIGRATION_RUNBOOK.md b/docs/MIGRATION_RUNBOOK.md new file mode 100644 index 0000000..6e77aae --- /dev/null +++ b/docs/MIGRATION_RUNBOOK.md @@ -0,0 +1,117 @@ +# Migrations-Runbook – archivmail + +Bezug: GoBD/DSGVO-Checkliste (`docs/GOBD_DSGVO_CHECKLIST.md`), Punkt 10 +"Migrationssicherheit". Ziel: nach jeder Schema-, Index- oder Storage-Migration +belegen, dass keine Mail verloren ging und die GoBD-Kernanforderungen +(Vollständigkeit, Unveränderbarkeit, Auffindbarkeit) weiterhin gelten. + +Gilt für: `initSchema`/`ALTER TABLE`-Änderungen, Manticore-Reindex nach +Schema-Änderung, Storage-Format-Wechsel (z.B. Kompression, Verschlüsselung), +Tenant-Migrationen (`migrate-tenants`), Piler-Import (`import-piler`). + +## Grundsatz + +Jede Migration ist ein potenzieller Datenverlust-Vektor. Vor/während/nach +jeder produktiven Migration gilt: **Zählen, Prüfen, Belegen** — nicht nur +"Migration lief ohne Fehler durch". + +## Ablauf + +### 1. Vor der Migration (Baseline) + +```bash +# Mail-Gesamtzahl + Tenant-Verteilung als Referenzwert +psql "$DSN" -c "SELECT tenant_id, COUNT(*) FROM emails GROUP BY tenant_id ORDER BY 1;" + +# Aktuellen Reconciliation-Stand sichern (PROJ-52) — zeigt ob VOR der Migration +# bereits Lücken bestanden, damit die nicht fälschlich der Migration angelastet werden +archivmail reconcile --days 7 --dry-run 2>&1 | tee /var/log/archivmail/migration-pre-$(date +%F).log + +# Backup, falls nicht ohnehin durch reguläres Backup abgedeckt (siehe devops-deploy) +pg_dump ... # Backup-Kommando gemäß bestehendem Backup-Runbook +``` + +Baseline-Zahlen (Gesamtzahl Mails, Zahl pro Tenant, Zeitpunkt) in den +Deploy-/Change-Log-Eintrag übernehmen (`DEVLOG.md` oder Commit-Message). + +### 2. Migration ausführen + +Reguläres Vorgehen laut `update.sh`/`install.sh` (Schema-Migrationen laufen +idempotent beim Start über `initSchema`, kein manueller Schritt nötig für +reine `ADD COLUMN IF NOT EXISTS`). Bei Index-Migrationen (z.B. neues Manticore- +Feld) ggf. `archivmail reindex` bzw. `archivmail reindex-tenant ` gezielt +ausführen. + +### 3. Nach der Migration (Compliance-Erhalt-Check) + +```bash +# 1. Zählvergleich — Gesamtzahl und Tenant-Verteilung MUSS mit Baseline übereinstimmen +psql "$DSN" -c "SELECT tenant_id, COUNT(*) FROM emails GROUP BY tenant_id ORDER BY 1;" +# Abweichung → STOP, nicht weiter deployen, Ursache klären (siehe "Bei Abweichung" unten) + +# 2. Integritätscheck (PROJ-18) — läuft im Daemon ohnehin alle 5 Minuten automatisch +# (runIntegrityCheck in cmd/archivmail/main.go), nach einer Migration zusätzlich +# gezielt die Logs der ersten Läufe nach Neustart prüfen: +journalctl -u archivmail --since "-15min" | grep "integrity check" +# Erwartung: "integrity check: complete" mit failed=0 + +# 3. Manticore-Index-Konsistenz — Trefferzahl im Index sollte plausibel zur +# Mail-Zahl in Postgres passen (kein 1:1, da Anhänge/OCR-Text mitgezählt werden +# können, aber grobe Größenordnung muss stimmen) +mysql -h127.0.0.1 -P9306 -e "SELECT COUNT(*) FROM emails_global;" + +# 4. Reconciliation-Report (PROJ-52) — Anomalie-Alert darf durch die Migration +# selbst NICHT ausgelöst werden (kein realer Rückgang, nur andere Quelle/Zeitpunkt) +archivmail reconcile --date $(date +%F) +# Dashboard/API /api/admin/reconciliation prüfen: kein unerwarteter alert:true + +# 5. Stichprobe: eine bekannte Mail vor/nach Migration per ID abrufen und +# EML-Export vergleichen (Byte-Identität, PROJ-12 Unveränderbarkeit) +archivmail export --id --out /tmp/post-migration-check.eml +diff /tmp/pre-migration-check.eml /tmp/post-migration-check.eml +``` + +### 4. Bei Abweichung + +- Zahlendifferenz in Schritt 3.1 → Migration NICHT als abgeschlossen betrachten, + keine weiteren Schritte (Cron-Purge, Deploy auf zweiten Server) auslösen. +- Ursache eingrenzen: welcher Tenant/Zeitraum betroffen? Anhand Audit-Log + (`event_type IN ('import','mail_purged')`) im Migrationszeitraum abgleichen. +- Rollback nur nach Rücksprache — Datenbank-Restore aus Schritt 1-Backup, + nicht einfach erneut migrieren ohne Root-Cause. +- Vorfall im DEVLOG.md und ggf. als Audit-Log-Eintrag dokumentieren + (GoBD-Nachvollziehbarkeit: auch fehlgeschlagene Migrationsversuche gehören + ins Protokoll). + +## Speziell: Tenant-Migration (`archivmail migrate-tenants`) + +Zusätzlich zu obigem Ablauf: +- Vor der Migration: `SELECT COUNT(*) FROM emails WHERE tenant_id IS NULL;` + (Mails ohne Tenant-Zuordnung) als Baseline — nach der Migration darf diese + Zahl nur durch bewusste Zuordnung sinken, nie durch Datenverlust steigen. +- Tenant-Quotas (PROJ-29) nach Migration neu berechnen lassen, falls Mails + zwischen Tenants verschoben wurden. + +## Speziell: Piler-Import (`archivmail import-piler`) + +- Piler-seitige Mail-Zahl (aus Piler-Datenbank/Export) als externe Baseline + neben der internen archivmail-Zählung führen — reine interne Zählung + beweist nicht, dass alles aus Piler übernommen wurde. +- Reconciliation-Quelle `import` (PROJ-52) zeigt Tageszahlen des Imports; + gegen Piler-Log gegenprüfen. + +## Speziell: Manticore-Schema-Änderung (z.B. neue Spalte, PROJ-50 `cc_addr`/`bcc_addr`) + +- `ensureColumn`-Migration ist idempotent, aber NICHT rückwirkend befüllend + für bereits archivierte Mails (nur neue/reindexierte Dokumente bekommen den + Wert). Nach Schema-Änderung: `archivmail reindex` für Bestandsdaten planen, + wenn das neue Feld auch für alte Mails durchsuchbar sein soll — sonst + in der jeweiligen Feature-Spec als bekannte Deviation vermerken (siehe + PROJ-50, BCC-Deviation). + +## Verweis + +- PROJ-18 (Integritätsprüfung, SHA-256, alle 5 Minuten automatisch im Daemon) +- PROJ-52 (Vollständigkeits-Reconciliation, täglicher Report) +- PROJ-19 (Piler-Import), PROJ-30 (Xapian→Manticore-Migration) — historische + Migrationen, an denen dieses Runbook sich orientiert