Files
karim 4f0f7e62f1 Doku: Supabase-Admin-Setup klarer (Dialog-Output + README Abschnitt 5)
- Tool legt Admin-User nicht an -> Schritt-für-Schritt: User in Supabase
  (Auth → Users → Add user, Auto Confirm) + E-Mail in ADMIN_ALLOWED_EMAILS
- Deploy-Output zeigt Login-Vorbedingungen
- Hinweis auf optionalen Self-Service-Signup

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

351 lines
16 KiB
Markdown
Raw Permalink 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) ← 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.<domain>` → 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` | `<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 ...
```