6.8 KiB
6.8 KiB
PROJ-26: IMAP-Server-Schnittstelle (Read-Only Archivzugriff)
Status: Deployed
Created: 2026-03-18 Last Updated: 2026-04-05
Dependencies
- Requires: PROJ-1 (Authentifizierung & Rollen) — Login via Benutzername/Passwort
- Requires: PROJ-5 (Speicherung & Indexierung) — E-Mails aus dem Archiv lesen
- Requires: PROJ-9 (Labels) — Labels als IMAP-Ordner abbilden
- Requires: PROJ-21 (Multi-Tenancy) — Nutzer sieht nur eigene Mails
User Stories
- Als Nutzer möchte ich mein E-Mail-Archiv mit Thunderbird, Outlook oder Apple Mail öffnen können, damit ich ohne Webinterface auf archivierte Mails zugreifen kann.
- Als Nutzer möchte ich mich mit meinem normalen Benutzername und Passwort am IMAP-Server anmelden, damit ich keine separaten Zugangsdaten brauche.
- Als Nutzer möchte ich meine Labels als IMAP-Ordner sehen, damit ich archivierte Mails thematisch organisiert abrufen kann.
- Als Admin möchte ich, dass Nutzer das Archiv nur lesen können (kein Löschen, kein Verschieben), damit die GoBD-Konformität und Archivintegrität gewahrt bleibt.
- Als Nutzer möchte ich den IMAP-Zugang von außen über Port 993 (Standard IMAPS) oder alternativ Port 9993 (archivmail-spezifisch) nutzen können, damit ich auch unterwegs auf das Archiv zugreifen kann und Port-Konflikte mit einem ggf. vorhandenen regulären Mailserver vermieden werden.
Acceptance Criteria
- Eingebetteter IMAP4rev1-Server läuft als Teil des Go-Backends (Port 1143 intern)
- Externer Zugriff via IMAPS Port 993 (Standard) oder alternativ Port 9993 (archivmail-spezifisch) — nginx terminiert TLS, leitet an internen Port 1143 weiter
- Beide Ports können gleichzeitig aktiv sein (parallele nginx-Listener)
- Authentifizierung mit Benutzername + Passwort (bcrypt-Vergleich, wie Webinterface)
- LDAP-Nutzer können sich ebenfalls per IMAP anmelden (LDAP-Auth-Pfad)
- Jeder Nutzer sieht ausschließlich seine eigenen archivierten E-Mails (Multi-Tenant-Isolation)
- Ordnerstruktur:
INBOX(alle Mails) + Labels als Unterordner (z.B.INBOX/Rechnungen) - Globale Labels (Admin-Labels) erscheinen ebenfalls als Ordner
- Vollständig Read-Only: keine DELETE, STORE \Deleted, COPY oder MOVE-Operationen erlaubt
\Seen-Flag darf NICHT persistent gesetzt werden (Archivintegrität)- IMAP-Kommandos implementiert:
LOGIN,LIST,SELECT,FETCH,SEARCH,LOGOUT,NOOP,CAPABILITY FETCHliefert vollständige RFC-2822 E-Mail (Header + Body + Anhänge)SEARCHunterstützt mindestens:ALL,FROM,TO,SUBJECT,SINCE,BEFORE- Verbindungen werden nach Inaktivität getrennt (Timeout 30 Min.)
- Maximale gleichzeitige Verbindungen pro Nutzer: 5
- Audit-Log-Eintrag bei jedem Login (Erfolg + Fehlschlag)
Edge Cases
- Nutzer hat keine E-Mails →
INBOXist leer,SELECT INBOXantwortet mit 0 EXISTS - Label wurde gelöscht aber IMAP-Client cached den Ordner → leerer Ordner, keine Fehlermeldung
- Falsches Passwort → nach 5 Fehlversuchen temporäre Sperre (30 Min.), Audit-Log-Eintrag
- Sehr großes Archiv (> 100.000 Mails) →
SELECTliefert korrekte EXISTS-Zahl,FETCHpaginiert - IMAP-Client versucht APPEND (Mail hochladen) →
NO [CANNOT] Read-only archive - Gleichzeitige Verbindungen vom gleichen Client → erlaubt bis Limit (5), danach
BYE - LDAP-Nutzer dessen LDAP-Server nicht erreichbar ist → Login verweigert, Fehlermeldung im Audit-Log
- Nutzer wird während aktiver IMAP-Session gelöscht → Session wird beim nächsten Kommando beendet
- Port 993 bereits durch anderen Mailserver belegt → Betrieb ausschließlich auf Port 9993 möglich (nginx-Config anpassen)
Technical Requirements
- Protokoll: IMAP4rev1 (RFC 3501)
- Port intern: 1143 (plaintext, nur localhost/LAN)
- Port extern: 993 (Standard IMAPS) und/oder 9993 (archivmail-spezifisch, vermeidet Konflikte mit existierenden Mailservern) — beide via nginx als TLS-Terminator
- TLS: Pflicht für externe Verbindungen — Zertifikat von
/etc/letsencrypt/oder selbstsigniert - Authentifizierung: LOGIN-Mechanismus (Benutzername/Passwort), PLAIN über TLS
- Performance: SELECT auf 10.000 Mails < 500ms, FETCH einer einzelnen Mail < 200ms
- Bibliothek:
github.com/emersion/go-imap(v1 oder v2) — battle-tested IMAP-Server-Bibliothek für Go - Integration: IMAP-Server startet als Goroutine im bestehenden Go-Backend (wie smtpd)
- Konfiguration: Neuer Abschnitt
imap_server:in/etc/archivmail/config.ymlimap_server: enabled: true bind: "0.0.0.0:1143" tls: false # TLS-Terminierung durch nginx # Externe Ports (nginx-Konfiguration): # 993 → Standard IMAPS (ggf. mit vorhandenem Mailserver kollidierend) # 9993 → archivmail-spezifisch (empfohlen wenn 993 bereits belegt) - Sicherheit: Kein STARTTLS auf 1143 (nginx übernimmt TLS) — Rate-Limiting bei Login-Fehlern
Implementation Notes
What was built (2026-03-18):
New package: internal/imapserver/server.go
- Custom IMAP4rev1 protocol implementation over raw TCP (no dependency on unstable go-imap/v2 server API)
- Architecture mirrors
internal/smtpd/smtpd.go(goroutine-based, background listener) - Authentication via
userstore.VerifyPassword()(bcrypt, bypasses TOTP for protocol access) - Mailbox listing: INBOX (all tenant mails) + INBOX/LabelName per user label
- FETCH: loads full RFC-2822 via
storage.Store.Load(), supports BODY[], ENVELOPE, RFC822.SIZE, INTERNALDATE, BODYSTRUCTURE - SEARCH: ALL, FROM, TO, SUBJECT, SINCE, BEFORE criteria
- UID FETCH and UID SEARCH supported
- Read-only enforcement: STORE, DELETE, COPY, MOVE, APPEND, EXPUNGE, CREATE, RENAME all return
NO [CANNOT] \Seenflag NOT persisted (always reported as set for client compatibility)- Multi-tenant isolation via
storage.GetAllIDsByTenant()filtered byuser.TenantID - Connection limit: 5 concurrent per user (atomic counter with acquire/release)
- Idle timeout: 30 minutes
- IDLE command support (waits for DONE)
- Audit log:
imap_loginon success,imap_login_failedon failure
Config change: config/config.go
- Added
IMAPServer IMAPServerConfigto Config struct - New
IMAPServerConfigstruct withenabledandbindfields
Wiring: cmd/archivmail/main.go
- IMAP server starts after label store init, before SMTP daemon
- Controlled by
imap_server.enabledconfig flag - Default bind:
127.0.0.1:1143
Design decisions:
- Used raw TCP IMAP implementation instead of go-imap/v2
imapserverpackage because the v2 library is still in beta.8 and the server-side API is unstable - TOTP is bypassed for IMAP access (standard IMAP LOGIN does not support 2FA)
- UIDs equal sequence numbers for simplicity (UIDVALIDITY=1, stable within session)
Tech Design (Solution Architect)
To be added by /architecture
QA Test Results
To be added by /qa
Deployment
To be added by /deploy