# docker-mailserver auf Proxmox (LXC) — mit React-Admin UI & SnappyMail Ein vollständiges, selbst gehostetes Mail-Setup für ein kleines Büro (4–5 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= 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** (`` = öffentliche IP): | Typ | Name | Wert | |---|---|---| | A | `mail.kgva.ch.` | `` | | **PTR/rDNS** | `` | `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 (``) 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) ← WICHTIG vor dem ersten Login Die Admin-UI nutzt **Supabase-Auth** (wie OPENBUREAU). **Das Tool legt den Admin-User NICHT selbst an** — du erstellst ihn einmalig im Supabase-Dashboard. Schritt für Schritt: 1. **Supabase-Projekt** (falls noch keins): [supabase.com](https://supabase.com) → New Project (Free-Tier reicht). 2. **Keys holen**: Project Settings → **API** → - `Project URL` → das ist `SUPABASE_URL` - `anon` `public` Key → das ist `SUPABASE_ANON_KEY` 3. **Admin-User anlegen**: Authentication → **Users** → **Add user** → - E-Mail + Passwort eintragen, **„Auto Confirm User"** aktivieren (sonst braucht's eine Bestätigungsmail). 4. **Diese E-Mail** muss in `ADMIN_ALLOWED_EMAILS` stehen (Deploy-Dialog bzw. `/opt/dms-stack/.env`). 5. Werte gesetzt? `cd /opt/dms-stack && docker compose up -d && docker compose restart admin-ui admin-api`. 6. **Login**: `https://admin.` → mit dieser E-Mail + Passwort einloggen. > Nur E-Mails aus `ADMIN_ALLOWED_EMAILS` erhalten Zugriff — alle anderen Supabase-User werden mit 403 abgewiesen. > > **Alternative (Self-Service):** In Supabase unter Authentication → Providers → Email **„Enable Signup"** > aktivieren; dann kann sich die erlaubte E-Mail über die Login-Seite selbst registrieren. (Standard: aus.) > > Weitere Admins später: einfach in Supabase als User anlegen **und** die E-Mail in `ADMIN_ALLOWED_EMAILS` ergänzen. --- ## 6. Nginx Proxy Manager (HTTPS für Web) Im NPM zwei **Proxy Hosts** anlegen: | Domain | Forward Hostname/IP | Port | SSL | |---|---|---|---| | `admin.example.com` | `` | `8080` | Let's Encrypt anfordern, „Force SSL" | | `mail.example.com` | `` | `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://: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.` → `: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.` 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 ... ```