Files
DOCKERMAILSERVER-LXC/README.md
T
karim 1d3818e725 docker-mailserver LXC für Proxmox: Stack + Admin-UI + Webmail + Hardening
- dms-lxc.sh: Proxmox-Host-Installer (unprivilegierter LXC, Debian 13, Docker),
  curl-Self-Download, Multi-Domain-DKIM, SnappyMail-Provisionierung, PVE-Firewall
- Stack: docker-mailserver, Node-Admin-API (Supabase-Auth), React-Admin-UI
  (OPENBUREAU-Look), SnappyMail (Shibui-Theme), Rspamd-Web-UI, docker-socket-proxy
- Admin: Postfächer/Aliase/Catch-all/Quota, editierbare Domains+Settings,
  Server (Quota/Queue über abgesicherte Bridge), Status & DNS
- Hardening: no-new-privileges, Whitelisted exec-Bridge, Rspamd-Passwort,
  .env chmod 600, PVE-CT-Firewall, generisch/teilbar (keine festen Domains)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 02:26:28 +02:00

346 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# docker-mailserver auf Proxmox (LXC) — mit React-Admin UI & SnappyMail
Ein vollständiges, selbst gehostetes Mail-Setup für ein kleines Büro (45 Personen):
| Komponente | Aufgabe |
|---|---|
| **docker-mailserver (DMS)** | Postfix + Dovecot + Rspamd (SMTP/IMAP, Spam, DKIM) |
| **Admin-API** (Node.js) | verwaltet Postfächer/Aliase/Quotas über die DMS-Config-Dateien — Auth via Supabase |
| **Admin-UI** (React-Admin) | Weboberfläche im Stil von OPENBUREAU |
| **SnappyMail** | schlankes Webmail für die Mitarbeiter |
| **Nginx Proxy Manager** *(bereits vorhanden)* | HTTPS/Let's-Encrypt für Admin-UI & Webmail |
Das Ganze läuft in **einem unprivilegierten LXC-Container** (Debian 13) auf Proxmox, in dem Docker mit `nesting=1` betrieben wird.
```
Internet
Port 25/465/587/ │ Port 80/443
143/993 ──────► ▼ ──────► Nginx Proxy Manager (HTTPS)
direkt zum DMS │
(Mail-TLS im DMS) ├─► admin.example.com → Admin-UI (:8080)
└─► mail.example.com → SnappyMail (:8888)
```
---
## 1. Voraussetzungen
- **Proxmox VE** (8.x), du arbeitest als `root` auf dem Host.
- Eine **Domain** (`example.com`) mit Zugriff auf die DNS-Einstellungen.
- **Port 25 ausgehend/eingehend** beim Hoster/ISP freigegeben (viele Privatanschlüsse blockieren 25!) und eine **statische öffentliche IP** mit der Möglichkeit, einen **PTR/rDNS** zu setzen.
- Eine **Supabase-Instanz** (dieselbe wie bei OPENBUREAU) für das Admin-Login.
- Der bereits vorhandene **Nginx Proxy Manager**.
---
## 2. Installation
**Variante A — Einzeiler** (auf dem Proxmox-Host als root). Das Skript lädt den
`stack/` bei Bedarf selbst herunter:
```bash
bash <(curl -fsSL https://git.kgva.ch/karim/DOCKERMAILSERVER-LXC/raw/branch/main/dms-lxc.sh)
# privates Repo: GIT_TOKEN=<token> bash <(curl -fsSL .../dms-lxc.sh)
```
**Variante B — Ordner kopieren** (inkl. `stack/`) und ausführen:
```bash
cd /root/dms && bash dms-lxc.sh
```
Das Skript fragt **alles interaktiv ab** (Domains, Webmail-/Admin-Domain, Brand,
Supabase, NPM-IP …) — oder per ENV vorbelegen:
```bash
CTID=110 \
MAIL_FQDN=mail.example.com MAIL_DOMAIN=example.com MAIL_DOMAINS="example.com weitere.tld" \
BRAND="Mein Büro" WEBMAIL_FQDN=mail.example.com ADMIN_FQDN=admin.example.com \
NET_IP=192.168.1.50/24 NET_GW=192.168.1.1 \
NPM_IP=192.168.1.10 \
ADMIN_ALLOWED_EMAILS="admin@example.com" \
SUPABASE_URL=https://xxxx.supabase.co SUPABASE_ANON_KEY=eyJ... \
bash dms-lxc.sh
```
> **Generisch & teilbar:** Es sind **keine festen Domains** im Stack — alles kommt aus dem
> Dialog und ist später in der Admin-UI editierbar. Du kannst das Skript also weitergeben.
Es macht automatisch:
1. unprivilegierten LXC (Debian 13, `nesting=1,keyctl=1`) anlegen + **PVE-CT-Firewall**,
2. Docker installieren,
3. **Self-signed-Cert + erstes Postfach vor-seeden**, dann den Stack bauen & starten,
4. **DKIM je Domain** erzeugen, SnappyMail provisionieren (Domain→mailserver, Shibui-Theme),
5. am Ende **DNS-Records (alle Domains, inkl. DKIM)** + **Rspamd-Passwort** + Hardening-Checkliste ausgeben.
> **Wichtige Variablen** (oben im Skript / per ENV): `CORES`, `RAM_MB` (4096), `DISK_GB` (20),
> `STORAGE`, `BRIDGE`, `ADMIN_PORT`/`WEBMAIL_PORT`/`RSPAMD_PORT`, `NPM_IP`, `HARDEN_FIREWALL`, `ENABLE_CLAMAV`.
Verwaltung später:
```bash
pct enter 110
cd /opt/dms-stack
docker compose ps
docker compose logs -f mailserver
```
---
## 3. DNS einrichten
**Ein Mailserver, mehrere Domains:** Es gibt **einen** Mailhost (`mail.kgva.ch`), der die Postfächer
**aller** Domains bedient (kgva.ch, gabrielevarano.ch, karimgabrielevarano.xyz, openbureau.ch …).
Alle Clients/Webmail verbinden sich immer zu `mail.kgva.ch` — egal welche Adress-Domain.
**Einmalig für den Mailhost** (`<IP>` = öffentliche IP):
| Typ | Name | Wert |
|---|---|---|
| A | `mail.kgva.ch.` | `<IP>` |
| **PTR/rDNS** | `<IP>` | `mail.kgva.ch`**beim Hoster/ISP setzen!** |
**Pro Mail-Domain** (Beispiel `kgva.ch` — analog für jede weitere Domain):
| Typ | Name | Wert |
|---|---|---|
| MX | `kgva.ch.` | `10 mail.kgva.ch.` |
| TXT (SPF) | `kgva.ch.` | `v=spf1 mx ~all` |
| TXT (DMARC) | `_dmarc.kgva.ch.` | `v=DMARC1; p=quarantine; rua=mailto:postmaster@kgva.ch` |
| TXT (DKIM) | je Domain eigener Key — siehe Skript-Ausgabe / Admin-UI → **Status & DNS** | (langer Public-Key) |
> Jede Domain hat einen **eigenen DKIM-Schlüssel**. Die fertigen Records (alle Domains) findest du
> nach dem Setup in der Admin-UI unter **Status & DNS** zum Kopieren — oder per Skript-Ausgabe.
> Weitere Domain später hinzufügen: `MAIL_DOMAINS` in `.env` ergänzen, Konto anlegen,
> `docker exec mailserver setup config dkim domain neue-domain.tld`, DNS setzen.
Der **PTR-Record** ist entscheidend dafür, dass deine Mails nicht als Spam landen.
DKIM später erneut anzeigen:
```bash
pct enter 110
cat /opt/dms-stack/docker-data/dms/config/rspamd/dkim/*.dns.txt
```
Prüfen lassen kannst du alles bequem über **[mail-tester.com](https://www.mail-tester.com)** und **[dkimvalidator.com](https://dkimvalidator.com)**.
---
## 4. Port-Weiterleitung / Firewall
Diese Ports müssen von außen zum Container (`<IP>`) gelangen:
| Port | Zweck |
|---|---|
| 25 | SMTP (eingehende Mails von anderen Servern) |
| 587 / 465 | Mail-Versand der Mitarbeiter (Submission) |
| 143 / 993 | IMAP (Mailabruf) |
Falls der Container hinter NAT liegt, am Router entsprechend weiterleiten.
Die Web-Ports **80/443** gehen an den **Nginx Proxy Manager**, nicht direkt an den Container.
---
## 5. Supabase-Login einrichten (Admin-UI)
Die Admin-UI nutzt Supabase-Auth (wie OPENBUREAU):
1. In deinem Supabase-Projekt unter **Authentication → Users** einen Benutzer mit deiner E-Mail anlegen (oder Einladung).
2. Diese E-Mail muss in `ADMIN_ALLOWED_EMAILS` stehen (in `/opt/dms-stack/.env`).
3. `SUPABASE_URL` und `SUPABASE_ANON_KEY` in `.env` müssen gesetzt sein.
Änderungen an der `.env` aktiv machen:
```bash
cd /opt/dms-stack
docker compose up -d # liest .env neu (UI/-API neu starten)
docker compose restart admin-ui admin-api
```
> Nur E-Mails aus `ADMIN_ALLOWED_EMAILS` erhalten Zugriff — alle anderen Supabase-User werden von der API mit 403 abgewiesen.
---
## 6. Nginx Proxy Manager (HTTPS für Web)
Im NPM zwei **Proxy Hosts** anlegen:
| Domain | Forward Hostname/IP | Port | SSL |
|---|---|---|---|
| `admin.example.com` | `<Container-IP>` | `8080` | Let's Encrypt anfordern, „Force SSL" |
| `mail.example.com` | `<Container-IP>` | `8888` | Let's Encrypt anfordern, „Force SSL" |
Für SnappyMail unter **Advanced** ggf. „Websockets Support" aktivieren.
---
## 7. TLS für den Mailserver (Port 465/587/993)
Mail-Protokolle laufen **nicht** über NPM, der Mailserver braucht also ein eigenes Zertifikat.
DMS ist auf **`SSL_TYPE=manual`** mit festem Pfad konfiguriert:
```
/opt/dms-stack/docker-data/certs/cert.pem (Zertifikat / fullchain)
/opt/dms-stack/docker-data/certs/key.pem (privater Schlüssel)
```
Das Setup-Skript legt dort beim ersten Start automatisch ein **self-signed** Zertifikat ab,
damit STARTTLS sofort funktioniert (Clients warnen wegen Selbstsignierung — für interne
Tests okay). **Für ein echtes Let's-Encrypt-Zertifikat einfach diese zwei Dateien ersetzen:**
1. In NPM unter **SSL Certificates** ein Zertifikat für `mail.kgva.ch` via **DNS-Challenge**
anfordern (funktioniert auch ohne offenen Port 80).
2. Dessen `fullchain.pem``docker-data/certs/cert.pem` und `privkey.pem``docker-data/certs/key.pem` kopieren.
3. `cd /opt/dms-stack && docker compose restart mailserver`**keine Config-Änderung nötig**, der Pfad bleibt gleich.
> Bei Erneuerung (alle ~90 Tage) die beiden Dateien neu kopieren und `docker compose restart mailserver`.
> Das lässt sich per Cron/Hook automatisieren. Alternativ `acme.sh`/certbot direkt im LXC (DNS-Plugin)
> und die Cert-Dateien an dieselben Pfade schreiben.
---
## 8. Admin-UI benutzen
`https://admin.example.com` öffnen → mit Supabase-E-Mail/Passwort einloggen.
- **Übersicht (Dashboard)**: Zähler (Postfächer/Aliase/Domains), Domains, Schnell-Links.
- **Postfächer**: Konten anlegen/löschen, Passwort ändern, **Quota** setzen (z.B. `5G`, leer = unbegrenzt). Validierung von E-Mail/Quota/Passwortlänge.
- **Aliase**: Weiterleitungen, z.B. `info@example.com``chef@example.com` (mehrere Ziele mit Komma).
**Catch-all**: als Quelle `@example.com` eintragen → fängt alle unbekannten Adressen der Domain.
- **Einstellungen**: **Domains hinzufügen/entfernen** (Hinzufügen erzeugt automatisch den DKIM-Key),
Brand, Webmail-/Admin-Domain und Mailserver-FQDN bearbeiten. (Erst-Befüllung aus dem Deploy-Dialog.)
- **Server**: **Quota-Auslastung** (belegt/frei je Postfach), **Mail-Queue**, **aktive Sessions**.
Läuft über eine abgesicherte Bridge (docker-socket-proxy, nur `exec`, Whitelist).
- **Status & DNS**: pro Domain MX/SPF/DMARC + DKIM zum Kopieren, „DKIM erzeugen/erneuern"-Button.
Konten/Aliase werden direkt in die DMS-Config-Dateien geschrieben; DMS übernimmt Änderungen automatisch.
---
## 9. SnappyMail (Webmail) einrichten
1. `https://mail.example.com` öffnen.
2. Admin-Panel beim ersten Start: `https://mail.example.com/?admin` — das Admin-Passwort steht in
`/opt/dms-stack/docker-data/snappymail/_data_/_default_/admin_password.txt`.
3. Unter **Domains** die Domain `example.com` hinzufügen:
- IMAP: `mailserver` Port `993` (SSL/TLS) — innerhalb des Docker-Netzes per Containername erreichbar,
extern `mail.example.com` Port `993`.
- SMTP: Port `587` (STARTTLS).
4. Mitarbeiter loggen sich dann mit ihrer vollen E-Mail-Adresse + Passwort ein.
### KGVA-Theme („Shibui")
SnappyMail wird im selben Look wie das KGVA-Nextcloud-Theme ausgeliefert. Das Theme liegt unter
`stack/snappymail-theme/Shibui/styles.css` und wird per Volume nach `/snappymail/themes/` gemountet —
es erscheint in SnappyMail als **`Shibui@custom`**.
Aktivieren (eine der beiden Varianten):
- **Für alle (empfohlen):** Admin-Panel (`https://mail.example.com/?admin`) → **Themes** → Standard-Theme
auf `Shibui` setzen. Alternativ in `data/_data_/_default_/configs/application.ini` unter `[webmail]`
`theme = "Shibui@custom"` eintragen und `docker compose restart snappymail`.
- **Pro Nutzer:** Webmail → **Einstellungen → Allgemein → Theme → Shibui**.
> Anpassen: einfach `styles.css` editieren und `docker compose restart snappymail`. Die Farb-/Font-Variablen
> stehen gesammelt im `:root`-Block oben (Petrol-Akzent `#7BA89B`, washi-Neutraltöne, Inter/Instrument Serif/JetBrains Mono).
> Hinweis: Die Fonts werden aktuell von Google Fonts geladen; auf Wunsch hoste ich sie lokal (DSGVO).
---
## 10. Mail-Client-Einstellungen (für Outlook/Thunderbird/Apple Mail)
| Einstellung | Wert |
|---|---|
| Posteingang (IMAP) | `mail.example.com`, Port **993**, SSL/TLS |
| Postausgang (SMTP) | `mail.example.com`, Port **587** (STARTTLS) oder **465** (SSL) |
| Benutzername | volle E-Mail-Adresse |
| Authentifizierung | normales Passwort |
---
## 11. Backup
Alle Daten liegen unter `/opt/dms-stack/docker-data/`:
```bash
pct enter 110
cd /opt/dms-stack
docker compose down
tar czf /root/dms-backup-$(date +%F).tar.gz docker-data .env mailserver.env
docker compose up -d
```
Zusätzlich empfiehlt sich ein **Proxmox-Backup (vzdump)** des ganzen LXC sowie für Postfächer ggf. eine Offsite-Kopie von `docker-data/dms/mail-data/`.
---
## 12. Troubleshooting
```bash
pct enter 110 && cd /opt/dms-stack
docker compose ps # Status aller Container
docker compose logs -f mailserver # Mail-Logs
docker exec mailserver setup email list # Postfächer auflisten (CLI)
docker exec mailserver postqueue -p # Mail-Queue ansehen
docker exec mailserver setup debug fetchmail # Debug
```
**Häufige Stolperfallen:**
- **Mails kommen nicht raus / landen im Spam:** Port 25 vom ISP geblockt, fehlender PTR, oder DKIM/SPF/DMARC nicht gesetzt → `mail-tester.com` nutzen.
- **Docker startet im LXC nicht:** `nesting=1,keyctl=1` prüfen (`pct config 110`); bei Bedarf `pct set 110 --features nesting=1,keyctl=1` und Container neu starten.
- **Admin-UI „Token ungültig":** E-Mail steht nicht in `ADMIN_ALLOWED_EMAILS`, oder `SUPABASE_URL/ANON_KEY` falsch.
- **Admin-UI lädt, aber Login schlägt fehl:** `config.js` prüfen → `curl http://<ip>:8080/config.js` sollte deine Supabase-URL zeigen; sonst `docker compose up -d admin-ui`.
---
## 13. Spam-Filter (Rspamd)
docker-mailserver bringt **Rspamd** mit (aktiviert: Bayes, SPF/DKIM/DMARC-Checks, Greylisting, RBLs) —
ein sehr guter Spam-Filter ohne Extra-Infrastruktur. Die **Web-UI** (Statistik/Training) läuft auf Port
`11334` und ist **passwortgeschützt** (Passwort wird beim Deploy generiert und ausgegeben; steht in
`docker-data/dms/config/rspamd/override.d/worker-controller.inc`).
- Im NPM als Proxy-Host `rspamd.<domain>``<CT-IP>:11334` (HTTPS).
- Passwort ändern: Datei editieren (`password` / `enable_password`) + `docker compose restart mailserver`.
- Optional härter hashen: `docker exec mailserver rspamadm pw` → Hash statt Klartext eintragen.
> Für größere Setups/dedizierte Quarantäne kann man optional ein **Proxmox Mail Gateway** davorschalten — für ein kleines Büro ist Rspamd i.d.R. ausreichend.
---
## 14. Hardening
**Schon eingebaut:**
- **Unprivilegierter LXC** + Nesting; Container mit `no-new-privileges` (admin-api, admin-ui, snappymail, socket-proxy).
- **Mailserver-Bridge** nur über **docker-socket-proxy** (ausschließlich `exec`) + Kommando-Whitelist in der API — **kein** roher Docker-Socket an die App.
- **Rspamd-UI** passwortgeschützt; **Fail2ban** aktiv (`cap_add: NET_ADMIN`); DMS kein Open-Relay (`PERMIT_DOCKER=none`, `SPOOF_PROTECTION=1`).
- Admin nur für E-Mails aus `ADMIN_ALLOWED_EMAILS` (Supabase). **`.env` ist `chmod 600`**.
- **Proxmox-CT-Firewall**: Mail-Ports offen, Web/Admin/Webmail/Rspamd nur von `NPM_IP`. Wirkt, sobald die **Datacenter/Node-Firewall aktiv** ist (`Datacenter → Firewall → Options → Enable: Yes`).
**Vor dem Echtbetrieb noch erledigen:**
- [ ] **Echtes TLS** für Mail: NPM-Zertifikat (DNS-Challenge) → `docker-data/certs/cert.pem|key.pem`, `restart mailserver` (ersetzt self-signed).
- [ ] Admin-UI/Webmail/Rspamd **nur über NPM (HTTPS)** erreichbar machen — rohe Ports nicht ins Internet; `NPM_IP` setzen.
- [ ] **Supabase-Login** mit 2FA absichern; starke Postfach-Passwörter.
- [ ] **Backups** (vzdump + `docker-data/`), siehe Abschnitt 11.
- [ ] **PTR/Reverse-DNS** auf `mail.<domain>` setzen; **Port 25** beim ISP prüfen (sonst Smarthost/`RELAY_HOST`).
- [ ] **DMARC** nach der Testphase auf `p=reject` verschärfen.
---
## Projektstruktur
```
.
├── dms-lxc.sh # Proxmox-Host-Skript (LXC anlegen + Stack deployen)
├── README.md
└── stack/ # wird in den Container nach /opt/dms-stack kopiert
├── docker-compose.yml
├── .env.example
├── mailserver.env
├── api/ # Node.js Admin-API (Supabase-Auth)
│ ├── server.js
│ └── lib/{store,auth}.js
└── admin/ # React-Admin UI (Vite → nginx)
└── src/{App,Layout,Status,authProvider,dataProvider}.jsx ...
```