Files
archivmail/features/PROJ-11-audit-log.md
T
sysops 7c29ee88bd docs: vollständige README, PROJ-2 Web-Upload, PROJ-19 Mailpiler-Migration
README.md:
- Vollständige Dokumentation aller implementierten Funktionen
- Konfigurationsreferenz, Installation, Systemd, REST-API-Übersicht
- In-Progress-Features klar gekennzeichnet

PROJ-2 (EML/MBOX Web-Upload):
- POST /api/admin/upload – Multipart-Upload mit Hintergrund-Job
- GET /api/admin/upload/{jobID}/progress – Polling
- Admin-Tab "Import" mit Drag-and-Drop, Fortschrittsbalken, Abschlussbericht

PROJ-19 (Mailpiler Migration):
- archivmail import-piler mit Methoden: pilerexport | direct | auto
- Direct: AES-256-CBC + zlib mit defensiven Fallbacks
- pilerexport: Wrapper um mailpilers Export-Tool

Status-Updates: PROJ-3, PROJ-4, PROJ-6, PROJ-7, PROJ-10, PROJ-11 → Deployed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 09:23:34 +01:00

6.0 KiB
Raw Blame History

PROJ-11: Audit-Log & Compliance-Berichte

Status: Deployed

Created: 2026-03-12 Last Updated: 2026-03-13

Dependencies

  • Requires: PROJ-1 (Authentifizierung) Audit-Einträge sind an Nutzer geknüpft

User Stories

  • Als Admin möchte ich sicherheitsrelevante Ereignisse im Audit-Log einsehen, damit ich Zugriffe und Änderungen nachvollziehen kann.
  • Als Admin möchte ich den Audit-Log nach Datum, Nutzer und Ereignistyp filtern.
  • Als Admin möchte ich den Audit-Log als CSV exportieren, damit ich ihn für externe Prüfungen verwenden kann.

Erfasste Ereignisse

Ja wird geloggt:

  • Login (Erfolg und Fehlschlag) inkl. IP-Adresse
  • Logout
  • Suchanfragen (Suchbegriff, Anzahl Treffer, Nutzer)
  • Import gestartet / abgeschlossen / fehlgeschlagen (Quelle, Anzahl E-Mails)
  • Export gestartet / abgeschlossen (Format, Anzahl E-Mails)

Nein wird nicht geloggt:

  • Lesezugriff auf einzelne E-Mails (kein per-Mail-Leselogging)

Acceptance Criteria

  • Jeder Log-Eintrag enthält: Zeitstempel (UTC), Nutzer-ID, Ereignistyp, Details (z.B. Suchbegriff, Import-Quelle), IP-Adresse
  • Audit-Log-Ansicht nur für Auditoren unter /audit/ kein Zugriff für admin oder user
  • Filterung nach: Datum (vonbis), Nutzer, Ereignistyp
  • Paginierung (50 Einträge pro Seite)
  • Export als CSV (gefilterte oder vollständige Ansicht, Streaming-Download)
  • Einträge sind unveränderlich: kein UPDATE/DELETE durch Admin oder Anwendung möglich
  • Retention konfigurierbar in config.yml (audit.retention_days), kein Standardwert erzwungen
  • Doppelte Speicherung: PostgreSQL (für GUI-Abfragen) + Append-only Logdatei auf Disk (als unveränderliches Backup)
  • Logdatei-Format: JSON Lines (ein Eintrag pro Zeile)

Edge Cases

  • Audit-Log über Jahre sehr groß → paginierte DB-Abfragen mit Index auf (timestamp, event_type, user_id), kein Full-Table-Scan
  • Nutzer DSGVO-gelöscht → Audit-Einträge behalten, user_id durch "anonymized" ersetzen, IP-Adresse löschen
  • Logdatei nicht beschreibbar beim Start → Warnung loggen, Dienst läuft weiter (DB-Log bleibt aktiv)
  • Gleichzeitige Schreibzugriffe auf Logdatei → Append mit file lock

Technical Requirements

  • PostgreSQL: separate Tabelle audit_log, kein DELETE/UPDATE per DB-Constraint (Row-Level Security oder Trigger)
  • Logdatei: /var/log/archivmail/audit.log (Pfad konfigurierbar), append-only, JSON Lines
  • Log-Rotation über logrotate (extern konfiguriert), Datei wird nie vom Dienst selbst rotiert oder gelöscht
  • Zeitstempel immer UTC (RFC 3339)

Tech Design (Solution Architect)

Komponentenstruktur

Next.js Frontend (/audit/*):

/audit                         ← nur für Auditoren (RoleGuard)
└── AuditTable
    ├── Spalten: Zeitstempel, Nutzer, Ereignis, Details, IP
    ├── Filter-Leiste
    │   ├── Datepicker: von  bis
    │   ├── Dropdown: Nutzer auswählen
    │   └── Dropdown: Ereignistyp
    ├── Paginierung (50 pro Seite)
    └── [CSV exportieren] Button → Streaming-Download

Go Backend:

Audit-Service (intern, kein eigener HTTP-Handler)
├── WriteEvent(event AuditEvent)
│   ├── → INSERT INTO audit_log (kein UPDATE/DELETE je möglich)
│   └── → Append zu /var/log/archivmail/audit.log (JSON Line)
│       └── File-Lock beim Schreiben (sync.Mutex)
│
└── Audit-API (nur für Auditoren)
    ├── GET /api/audit/events    ← Paginiert, gefiltert
    └── GET /api/audit/export    ← Streaming-CSV-Download

Datenmodell

Tabelle audit_log (append-only via DB-Trigger):

Feld Beschreibung
id Sequentielle ID
timestamp UTC, RFC 3339
user_id Nutzer-ID (NULL nach DSGVO-Löschung)
user_email E-Mail zum Zeitpunkt des Events (für Lesbarkeit nach Anonymisierung)
event_type login_ok, login_fail, logout, search, import_start, import_done, import_fail, export_start, export_done
details JSON: Suchbegriff / Import-Quelle / Anzahl / etc.
ip_address IPv4/IPv6 (NULL nach DSGVO-Löschung)

DB-Constraint: PostgreSQL-Trigger verhindert UPDATE und DELETE auf der gesamten Tabelle → physische Unveränderlichkeit.

Logdatei-Format (/var/log/archivmail/audit.log, JSON Lines):

{"ts":"2024-03-01T10:00:00Z","user":"alice@firma.de","event":"search","details":{"q":"Rechnung","hits":42},"ip":"192.168.1.1"}

Schreibfluss

Beliebige Aktion (Login, Suche, Import...)
        │
        ▼
audit.WriteEvent() aufgerufen
        │
        ├── PostgreSQL INSERT (non-blocking, Goroutine)
        │
        └── File-Append mit sync.Mutex
            (Datei nicht beschreibbar? → Warnung auf stderr, Dienst läuft weiter)

DSGVO-Löschfluss (Nutzer anonymisieren)

DELETE /api/admin/users/{id}
        │
        ├── audit_log: user_id → NULL, ip_address → NULL
        │             user_email → "anonymized"
        └── Logdatei: bleibt unverändert (tamper-evident)

Technische Entscheidungen

Entscheidung Begründung
Audit-Ansicht nur für Auditoren Strict role separation — Admin hat keine Einsicht in Zugriffsprotokolle
DB-Trigger für Unveränderlichkeit Applikationscode kann versehentlich löschen — Trigger ist eine härtere Garantie
Doppelte Speicherung DB für GUI-Abfragen; Logdatei als tamper-evident Backup für externe Prüfungen
logrotate extern Dienst rotiert nie selbst — Logdatei bleibt unter Systemadmin-Kontrolle
DSGVO: IP-Adresse löschen, Event behalten Personenbezug entfernen, Compliance-Nachweis bleibt erhalten
Composite Index (timestamp, event_type, user_id) Schnelle gefilterte Abfragen auch bei sehr großem Log über Jahre

Abhängigkeiten

Go Backend: Nur Stdlib + pgx (bereits vorhanden). Next.js: shadcn/ui Table, Select, DatePicker (bereits installiert).

QA Test Results

To be added by /qa

Deployment

To be added by /deploy