E-Mail¶
Architektur¶
Internet → Traefik → hosting-postfix (SMTP)
→ hosting-dovecot (IMAP + OAuth2)
→ hosting-rspamd (Spam-Filter)
Platform API → IMAP Proxy (aioimaplib) → Dovecot
→ SMTP Service (aiosmtplib) → Postfix
→ Mail-Watcher (IMAP IDLE) → WebSocket Events
| Komponente | Image | Port | Beschreibung |
|---|---|---|---|
| Postfix | alpine:3.19 | 25, 465, 587 | SMTP (MX, SMTPS, Submission) |
| Dovecot | xynap/dovecot-oauth2:2.3.21 | 993 | IMAPS + OAuth2 (Custom Build) |
| Rspamd | - | 11332 | Milter fuer Postfix |
| Platform API | - | 8099 | Webmail-Proxy, Watcher, Signaturen |
Dovecot Docker-Image¶
Custom-Build aus Source (Alpine-Paket hat keinen OAuth2 passdb-Treiber):
- Dockerfile:
/etc/xynap/hosting/dovecot/Dockerfile - Multi-Stage: Builder (compile) → Runtime (alpine:3.19 + openssl + libcurl + mariadb-connector-c)
- Auth-Mechanisms:
plain login oauthbearer xoauth2(Passwort + OAuth2 parallel)
Authentifizierung¶
Alle Mail-Endpoints akzeptieren zwei Auth-Methoden:
| Methode | Header | Scope |
|---|---|---|
| Service-Token | x-service-token: <token> |
Platform (alle Domains/Accounts sichtbar) |
| Bearer Token | Authorization: Bearer <jwt> |
Tenant-Scope (nur eigene Daten) |
Service-Token Beispiel:
Mail-Administration API¶
Domains¶
# Domains auflisten
GET /api/v1/mail/domains
# Domain erstellen (erstellt auch DNS-Records: MX, SPF, DMARC, DKIM)
POST /api/v1/mail/domains
{"domain": "example.com", "customer_id": 1}
Mailboxen¶
# Mailboxen einer Domain
GET /api/v1/mail/domains/{id}/accounts
# Mailbox erstellen
POST /api/v1/mail/domains/{id}/accounts
{"local_part": "info", "password": "...", "quota_mb": 1024}
# Passwort aendern
PUT /api/v1/mail/accounts/{id}
{"password": "neues-passwort"}
# Mailbox sperren/entsperren
POST /api/v1/mail/accounts/{id}/suspend
POST /api/v1/mail/accounts/{id}/unsuspend
Aliase¶
# Aliase einer Domain
GET /api/v1/mail/domains/{id}/aliases
# Alias erstellen
POST /api/v1/mail/domains/{id}/aliases
{"source": "sales", "destination": "info@example.com"}
Webmail API¶
Nativer IMAP/SMTP-Proxy — ersetzt Roundcube.
Ordner & Nachrichten¶
# Ordnerliste mit Unread-Count
GET /api/v1/mail/webmail/folders
# Nachrichten eines Ordners (paginiert)
GET /api/v1/mail/webmail/messages/{folder}?page=1&per_page=50
# Einzelne Nachricht (HTML Body)
GET /api/v1/mail/webmail/messages/{folder}/{uid}
# Nachricht loeschen (in Trash verschieben)
DELETE /api/v1/mail/webmail/messages/{folder}/{uid}
# Nachricht verschieben
POST /api/v1/mail/webmail/messages/{folder}/{uid}/move
{"target": "Archive"}
# Flags setzen (Gelesen, Stern, etc.)
POST /api/v1/mail/webmail/messages/{folder}/{uid}/flags
{"flags": ["\\Seen", "\\Flagged"]}
Senden¶
# Mail senden (HTML + Plain-Text)
POST /api/v1/mail/webmail/send
{
"to": ["empfaenger@example.com"],
"cc": [],
"bcc": [],
"subject": "Betreff",
"html": "<p>HTML Body</p>",
"plain": "Plain-Text Fallback",
"in_reply_to": "<message-id>" # optional, fuer Reply
}
Signaturen¶
# Signaturen auflisten
GET /api/v1/mail/signatures/
# Signatur erstellen
POST /api/v1/mail/signatures/
{
"name": "Standard",
"html": "<p>Mit freundlichen Gruessen</p>",
"is_default": true,
"rules": [
{"match_type": "domain", "match_value": "example.com"}
]
}
# Passende Signatur fuer Empfaenger
GET /api/v1/mail/signatures/match?to=user@example.com
Kontakte-Autocomplete¶
Echtzeit-Events (Mail-Watcher)¶
Der Mail-Watcher ueberwacht Postfaecher via IMAP IDLE und sendet Events ueber WebSocket.
Methoden (Auto-Detect pro Server)¶
| Methode | Beschreibung | Prioritaet |
|---|---|---|
| IMAP IDLE | Server-Push bei neuen Nachrichten | 1 (primaer) |
| Dovecot Push | HTTP-Webhook vom Mail-Server | 2 |
| Polling | STATUS-Check alle 60 Sekunden | 3 (Fallback) |
WebSocket Events¶
// Neue Nachricht
{"type": "mail.new_message", "data": {
"folder": "INBOX",
"message": {"uid": 42, "from_addr": "sender@example.com", "subject": "..."}
}}
// Ordner-Update (Unread-Count)
{"type": "mail.folder_update", "data": {
"folder": "INBOX",
"unseen": 5,
"exists": 42
}}
Watcher-Status¶
Mail OAuth2 (XOAUTH2 / OAUTHBEARER)¶
Platform API fungiert als OAuth2 Authorization Server fuer den Mail-Stack. Externe IMAP/SMTP-Clients (Thunderbird, Apple Mail, etc.) koennen sich per OAuth2-Token authentifizieren.
Flow¶
1. User Login → POST /api/v1/auth/login → JWT (type=access)
2. Mail-Token → POST /api/v1/auth/mail-token → JWT (type=mail_access, 60 Min)
3. IMAP Connect → AUTHENTICATE XOAUTH2 <token> → Dovecot
4. Introspection → Dovecot POST /introspect → Platform API validiert Token
5. Login OK → IMAP Session aktiv
Token anfordern¶
# Mail-OAuth2-Token fuer eingeloggten User (erfordert aktives Mail-Konto)
POST /api/v1/auth/mail-token
Authorization: Bearer <access_token>
# Optional: spezifisches Mail-Konto angeben
POST /api/v1/auth/mail-token
{"email": "info@example.com"}
Response:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"email": "user@example.com"
}
Token Introspection (RFC 7662)¶
Intern — Dovecot ruft diesen Endpoint auf um Tokens zu validieren:
Response:
{
"active": true,
"username": "user@example.com",
"scope": "imap smtp",
"token_type": "Bearer",
"exp": 1773178446
}
Dovecot-Konfiguration¶
# auth_mechanisms (parallel: Passwort + OAuth2)
auth_mechanisms = plain login oauthbearer xoauth2
# OAuth2 passdb (Token Introspection via Platform API)
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/auth-oauth2.conf.ext
}
auth-oauth2.conf.ext:
introspection_url = http://platform-api:8099/api/v1/auth/token/introspect
introspection_mode = post
username_attribute = username
active_attribute = active
active_value = true
IMAP-Client Beispiel (XOAUTH2)¶
import base64, imaplib
user = "user@example.com"
token = "<mail_access JWT>"
# XOAUTH2 SASL String
auth_string = f"user={user}\x01auth=Bearer {token}\x01\x01"
auth_b64 = base64.b64encode(auth_string.encode()).decode()
imap = imaplib.IMAP4_SSL("mail.xynap.tech", 993)
imap.authenticate("XOAUTH2", lambda x: auth_string.encode())
imap.select("INBOX")
DNS-Records (automatisch)¶
Bei Domain-Erstellung werden folgende Records angelegt:
| Typ | Name | Wert |
|---|---|---|
| MX | @ | nexus.isn-systems.com (Prio 10) |
| TXT | @ | v=spf1 ip4:46.4.96.105 -all |
| TXT | _dmarc | v=DMARC1; p=quarantine; rua=... |
| TXT | mail._domainkey | DKIM Public Key |
| CNAME | autoconfig | nexus.isn-systems.com |
Mail-Storage¶
/var/lib/xynap/mail/
├── vhosts/{domain}/{user}/ # Maildir
├── dkim/ # DKIM-Keys
└── ssl/ # SSL-Zertifikate
vmail User: UID/GID 5000