Files
archivmail/docs/GOBD_DSGVO_CHECKLIST.md
T
sysops a7390151a1 docs: GoBD-Checklist auf aktuellen Stand bringen (PROJ-52, PROJ-56c)
Punkt 1 (Vollständigkeit) war noch als "Teilweise" markiert, obwohl PROJ-52
(Reconciliation-Report) das inzwischen abdeckt. Punkt 7 (Löschsperre)
beschrieb noch den alten Purge() ohne Markierungspflicht; ergänzt um
PROJ-56c Cron-Purge, der nur retain_until < NOW() UND marked_for_deletion
löscht.
2026-07-04 12:07:21 +02:00

9.8 KiB
Raw Blame History

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 Erfüllt 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. Vollständigkeits-Reconciliation (PROJ-52, deployt 2026-07-04): täglicher Cron-Report internal/reconciliation/ zählt archivierte Mails je Quelle, Alert bei >50% Abweichung vom 7-Tage-Schnitt, Audit-Eintrag reconciliation_anomaly.
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. 610 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. 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 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.