7c29ee88bd
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>
146 lines
6.0 KiB
Markdown
146 lines
6.0 KiB
Markdown
# 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 (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_
|