README: Feature-Tabelle und Inhaltsverzeichnis auf aktuellen Stand gebracht (POP3, OCR, eDiscovery, DSGVO, Retention, LDAP, TOTP, IMAP-Server, Prometheus, Tenant-Export etc. ergänzt). Zusätzlich aufgenommen: CODEBASE.md (Dateiindex, Stand 2026-03-31 — vor PROJ-44 eingefroren, sollte bei Gelegenheit aktualisiert werden), DEVLOG.md (Session-Log), docs/GOBD_DSGVO_CHECKLIST.md (Compliance-Checkliste), resume (Session-Notiz), screenshots/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
65 lines
9.2 KiB
Markdown
65 lines
9.2 KiB
Markdown
# GoBD/DSGVO-Compliance-Checkliste – archivmail
|
||
|
||
Bewertung gegen den "Leitfaden zur E-Mail-Archivierung in Deutschland" (VOI-Grundsätze, GoBD, DSGVO).
|
||
Stand: 2026-06-13, basierend auf Code-Review (nicht nur Spec-Review).
|
||
|
||
## Zusammenfassung
|
||
|
||
**Gut abgedeckt:** Verschlüsselung at-rest (AES-256-GCM), Volltextsuche (Manticore), Audit-Log
|
||
in PostgreSQL mit Filterung/Export, Retention/Löschsperre (PROJ-34), Multi-Tenant-Rollenmodell,
|
||
Integritätsprüfung per SHA-256 (PROJ-18), Export in EML/MBOX/ZIP/CSV (PROJ-12, PROJ-15, PROJ-39, PROJ-47).
|
||
|
||
**Top-Lücken (priorisiert):**
|
||
|
||
1. **Audit-Log ist NICHT unveränderbar** – kein DB-Trigger/Constraint gegen UPDATE/DELETE auf
|
||
`audit_log`, und die in PROJ-11 spezifizierte append-only Flat-File (`/var/log/archivmail/audit.log`,
|
||
JSON Lines) existiert im Code nicht. Das ist ein Kernpunkt von VOI-Grundsatz 8 (Protokollierung)
|
||
und wird bei einer Prüfung als erstes angeschaut.
|
||
2. **Verschlüsselung ist optional/deaktivierbar** – `Keyfile` kann leer bleiben → kein "at rest"-Schutz
|
||
garantiert, kein erzwungenes Minimum.
|
||
3. **Keine Kennzeichnung/Klassifikation personenbezogener Daten** – kein `contains_personal_data`
|
||
o.ä. Flag im Mail-Datenmodell; DSGVO-Löschkonflikt (Art. 17 vs. GoBD-Aufbewahrung) ist nirgends
|
||
im Code dokumentiert oder technisch abgebildet.
|
||
4. **Mindestaufbewahrung nicht erzwungen** – `retention_days` ist frei konfigurierbar inkl. `0`
|
||
(kein Lock); es gibt keine Differenzierung nach Dokumentenart (6 vs. 10 Jahre vs. dauerhaft) und
|
||
kein erzwungenes Minimum von 10 Jahren für Buchungsbelege.
|
||
5. **Zeitstempel/Signaturerhalt (BSI TR 03125)** – nicht implementiert (Nice-to-have, niedrige Priorität).
|
||
|
||
---
|
||
|
||
## Checkliste
|
||
|
||
| # | Anforderung | Status | Begründung / Codeverweis | Empfehlung |
|
||
|---|---|---|---|---|
|
||
| 1 | **Vollständigkeit** – 100% Ein-/Ausgang archiviert | ⚠️ Teilweise | SMTP-BCC-Journal (PROJ-4), IMAP/POP3-Import (PROJ-3/14), EML/MBOX-Import (PROJ-2). SMTP-Daemon (`internal/smtpd/smtpd.go`) gibt bei Storage-Fehler `452` zurück → MTA-Retry, kein Verlust beim Empfang. Kein zentraler "Vollständigkeits-Reconciliation"-Mechanismus zwischen Mailserver-Log und Archiv. | Prüfung/Abgleich-Report (z.B. erwartete vs. archivierte Anzahl pro Tag) als neues Feature vorschlagen. |
|
||
| 2 | **Frühzeitigkeit** – Archivierung bei Eingang/Ausgang | ✅ Erfüllt | SMTP-BCC-Journaling (PROJ-4) archiviert synchron beim Empfang/Versand; IMAP-Cron-Sync (PROJ-8) ergänzt. `internal/storage/storage.go:343 Save()` läuft direkt im Empfangspfad. | – |
|
||
| 3 | **Unveränderbarkeit / Formaterhalt** | ✅ Erfüllt | `Save()` speichert Original-RFC2822-Bytes (ggf. gzip-komprimiert, PROJ-36) ohne inhaltliche Konvertierung; `Load()`/`maybeDecompress()` liefert Original zurück. Export liefert EML 1:1 (PROJ-12, `internal/api/export.go`). Kein Schreibzugriff/UPDATE auf gespeicherte Mail-Dateien im Code gefunden. | – |
|
||
| 4 | **Zugriffsschutz** | ✅ Erfüllt | JWT-Auth (httpOnly Cookie), bcrypt Cost 12 (PROJ-1). Rollenmodell `user`/`auditor`/`admin` strikt getrennt; `admin` hat explizit KEINEN Mail-Zugriff. Tenant-Isolation via `tenantAccessAllowed()` (`internal/api/import_handlers.go:22`). | – |
|
||
| 5 | **Wiederauffindbarkeit / Volltextsuche** | ✅ Erfüllt | Manticore-RT-Index (PROJ-30), Volltextsuche inkl. Anhänge via OCR (PROJ-35/44), Filter nach Datum/Absender/etc. (PROJ-6). | – |
|
||
| 6 | **Aufbewahrungsfristen (konfigurierbar je Dokumentenart, i.d.R. 6–10 Jahre)** | ⚠️ Teilweise | `RetentionDays` global (`config/config.go:74`) und pro Tenant (`config/config.go:122`, Spalte `tenants.retention_days`) konfigurierbar — aber als EINZELNER Wert für alle Mails, keine Differenzierung nach Dokumentenart (Geschäftsbrief 6J / Rechnung 10J / Vertrag dauerhaft). `0` = kein Lock ist als Default zulässig (kein erzwungenes Minimum). | Spec-Erweiterung: Mindestwert (z.B. Warnung/Block unter 2190 Tagen) und/oder Kategorisierung nach Mail-Typ (PROJ-43 Archivierungsregeln könnte hierfür genutzt werden). |
|
||
| 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, `Purge()` löscht nur abgelaufene Mails (`storage.go:710-718`). | – |
|
||
| 8 | **Protokollierung/Audit-Log unveränderlich** | ❌ Fehlt | `internal/audit/audit.go:64-75` erstellt `audit_log`-Tabelle OHNE Trigger/Rule gegen `UPDATE`/`DELETE` (PROJ-11 AC: "kein UPDATE/DELETE durch Admin oder Anwendung möglich" — NICHT umgesetzt). Die geforderte append-only JSON-Lines-Datei (`/var/log/archivmail/audit.log`) existiert nicht im Code — nur `New(dsn, logDir, logger)` mit Kommentar "logDir is reserved for future". Audit-Events (Login, Suche, Export, Import) werden korrekt geschrieben, aber Unveränderbarkeit ist nicht garantiert. | PROJ-11 nachbessern: (a) PostgreSQL-Trigger/REVOKE UPDATE/DELETE auf `audit_log`, (b) append-only Flat-File-Logger implementieren. Hohe Priorität für GoBD-Prüfungen. |
|
||
| 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. |
|
||
| 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. |
|
||
| 14 | **Trennung/Kennzeichnung personenbezogener Daten, Löschkonflikt Art. 5/17/32** | ❌ Fehlt | Kein Feld wie `contains_personal_data` o.ä. im Mail-Datenmodell (`internal/storage/storage.go` Tabelle `emails`); PROJ-20 (Nutzer-Löschung) behandelt nur Anonymisierung von Nutzerkonten/Audit-Einträgen, nicht von Mail-Inhalten. Konflikt GoBD-Aufbewahrung vs. DSGVO-Löschanspruch ist nirgends dokumentiert oder im Code abgebildet (z.B. `Purge()` prüft nur `retain_until`, keine "Lösch-trotz-Anfrage-abgelehnt"-Logik mit Begründung). | Neue Feature-Spec: DSGVO-Auskunfts-/Löschersuchen-Workflow, der bei aktivem `retain_until` automatisch auf "Aufbewahrungspflicht hat Vorrang" verweist und dies dokumentiert/protokolliert. |
|
||
| 15 | **Mandantentrennung/Zugriffskontrolle nach Rolle (Multi-Tenant)** | ✅ Erfüllt | PROJ-21 Multi-Tenancy, `tenantAccessAllowed()` als Standardmuster (`internal/api/import_handlers.go:22`), Tenant-Quotas (PROJ-29), Pro-Tenant-Retention (`tenants.retention_days`). Laut Memory: keine physische Storage-Trennung pro Tenant (logische Trennung über `email_refs`/DB). | Bekannte Lücke (siehe Memory `project_tenant_isolation_review.md`): physische Storage-Trennung als optionales Härtungsfeature evaluieren, falls von Kunden gefordert. |
|
||
| 16 | **Verschlüsselung at-rest** | ⚠️ Teilweise | AES-256-GCM implementiert (`internal/storage/storage.go:154-...`, `encrypt()`), inkl. gzip vor Verschlüsselung (PROJ-36). ABER: `Keyfile` ist optional — `loadKey()` gibt bei leerem Pfad `nil` zurück → "no encryption configured" (`storage.go:126`). Es gibt keine Pflicht/Warnung im Setup, dass ein Keyfile gesetzt sein MUSS. | Setup-/Healthcheck-Warnung (evtl. in `archivmail status`, siehe letzter Commit) ergänzen, falls `Keyfile` leer ist — und/oder Pflichtfeld in Produktions-Config-Validierung. |
|
||
|
||
---
|
||
|
||
## Nächste Schritte (Vorschlag, Priorität absteigend)
|
||
|
||
1. **PROJ-11 nachbessern** (Audit-Log-Unveränderbarkeit): DB-Trigger gegen UPDATE/DELETE +
|
||
append-only JSON-Lines-Datei wie ursprünglich spezifiziert.
|
||
2. **Encryption-Pflicht**: Healthcheck/Setup-Validierung, dass `storage.keyfile` konfiguriert ist.
|
||
3. **Neue Spec**: DSGVO-Löschersuchen-Workflow mit GoBD-Vorrang-Dokumentation (Punkt 14).
|
||
4. **PROJ-34 erweitern**: Mindestaufbewahrung (kein `retention_days=0` als stiller Default in
|
||
produktiven Setups) + Differenzierung nach Dokumentenart (ggf. via PROJ-43 Regeln).
|
||
5. **Vollständigkeits-Reconciliation** (Punkt 1) als neues Feature evaluieren.
|
||
6. Nice-to-have: BSI TR 03125 Zeitstempel/Signaturerhalt (Punkt 12), nur bei konkretem Bedarf.
|
||
|
||
PROJ-47 (Tenant-Voll-Export, Status "In Review") sollte für Punkt 9 final abgeschlossen werden.
|