# PROJ-11: Audit-Log & Compliance-Berichte ## Status: In Progress **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 (von–bis), 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_