M6: network and DNS plan for hosted mail on the QNAP

Hosting decided (spec App. D §D.1 option A): Stalwart on the QNAP, receiving and
sending directly; addresses @ultimo.email; mail apps allowed with app
passwords; mail host mail.ultimo.email. docs/mail-server-network.md records the
connection checks (clean IP, outbound 25 open, generic reverse DNS to change),
the router / Synology / QNAP port layout, container ports, TLS certificate,
the DNS records to set at home.pl, moving mail from home.pl, and checks after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 13:40:26 +02:00
co-authored by Claude Opus 5.5
parent 84d2d24c7d
commit d45fbf2c44
2 changed files with 119 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
# Hosted mail (M6): network and DNS plan
Notes for switching `ultimo.email` mail from home.pl to Ultimo's own mail server
(Stalwart on the QNAP). Companion to the M6 plan in
`webmail-client-spec_v1.5.md` Appendix D. Gathered 2026-10-09/10.
## Decisions
| | Decision |
|---|---|
| Hosting | **Everything on the QNAP** — receive and send directly (spec §D.1 option A). A relay (home.pl SMTP, SMTP2GO, Brevo) is optional, as a fallback only. |
| Domain | Addresses `name@ultimo.email`; webmail stays at `https://ultimo.email`. |
| Mail apps | **Allowed**: Thunderbird and phone mail apps use the mailboxes over IMAP/SMTP, signing in with **app passwords** (required when 2FA is on). |
| Mail host name | **`mail.ultimo.email`** (recommended; `ultimo.email` would also work, see below). |
## Current setup
- Static public IP **`178.219.143.96`** (ISP: Multiplay).
- Traffic → **router** → **Synology DSM** → DSM **reverse proxy** → **QNAP** (the webmail).
- All DNS records are at **home.pl**:
- A `ultimo.email` → `178.219.143.96` (the QNAP, through the Synology);
- MX `ultimo.email` → `serwer2295308.home.pl` — **mail is still at home.pl**;
- other records also point at home.pl.
## Checks of the connection (2026-10-10)
| Check | Result |
|---|---|
| Blocklists — Spamhaus ZEN (incl. PBL), Barracuda, SpamCop, SORBS, UCEPROTECT | **Not listed** |
| Outbound port 25 (tested on the QNAP) | **Open** |
| Reverse DNS of `178.219.143.96` | `pub-143.96.multiplay.pl` — generic: **must change** (below) |
| Inbound 25 / 465 / 587 / 993 from outside | Time out — expected: not forwarded yet, nothing listening. Re-test after setup. |
| Inbound 443 | Open (webmail) |
## To do before switching
### 1. Reverse DNS (ask Multiplay)
Set the reverse DNS (PTR) of `178.219.143.96` to **`mail.ultimo.email`**.
Why: receiving servers (Gmail, Outlook…) check that three things agree — the
IP's reverse DNS name, that name's A record pointing back to the IP, and the
name the mail server introduces itself with (its SMTP greeting). A generic ISP
name or a mismatch reads as a hijacked home computer and hurts delivery.
`ultimo.email` itself would work too (its A record already points here); a
separate `mail.` name keeps mail working if the website ever moves elsewhere.
### 2. Router: forward the mail ports straight to the QNAP
DSM's reverse proxy carries only web traffic (HTTP/HTTPS); SMTP and IMAP are
other protocols, so these ports go from the router directly to the QNAP:
| Outside port | To | Purpose |
|---|---|---|
| 443, 80 | Synology (as today) | webmail, CalDAV/CardDAV (`/dav/`, `/.well-known/…`), certificates |
| **25** | QNAP | receiving mail from other servers (must be 25: MX delivery always uses it) |
| **465** | QNAP | sending from mail apps (SMTP over TLS) |
| **587** | QNAP | sending from mail apps (SMTP + STARTTLS) |
| **993** | QNAP | mail apps reading mail (IMAP over TLS) |
Mail sent by the QNAP leaves through the same router, i.e. from
`178.219.143.96` — the address the reverse DNS names.
### 3. The container's ports
Stalwart listens inside its container on the standard ports; Docker publishes
them on the QNAP — the same numbers if free (`"25:25"`, `"465:465"`, `"587:587"`,
`"993:993"`), or any free QNAP port (e.g. `"2525:25"`) with the router mapping
the standard outside port to it. Outside ports stay standard: 25 is mandatory,
and mail apps assume 465/587/993 when setting up an account.
Verify after setup: Stalwart must see the **real IP** of connecting servers and
apps (not a Docker address) — its spam checks and sign-in throttling use it.
### 4. TLS certificate for the mail server
The Synology holds the HTTPS certificate, but IMAP/SMTP connections end at the
QNAP, so Stalwart needs its own for `mail.ultimo.email`. Options:
- **(recommended)** Stalwart's own Let's Encrypt certificate: Let's Encrypt
checks over port 80, which goes to the Synology — add a **DSM reverse-proxy
rule: host `mail.ultimo.email`, port 80 → QNAP, Stalwart's HTTP port**.
Renews itself.
- DNS-based check, if home.pl's DNS can be updated automatically.
- Copy the Synology's certificate to the QNAP after every renewal (clumsiest).
### 5. DNS records at home.pl (at switch-over)
Lower the TTLs a day before.
| Record | Value |
|---|---|
| A `mail.ultimo.email` | `178.219.143.96` |
| MX `ultimo.email` | `10 mail.ultimo.email.` (replaces `serwer2295308.home.pl`) |
| TXT `ultimo.email` (SPF) | `v=spf1 mx -all` (add the relay's include if one is used) |
| TXT `<selector>._domainkey.ultimo.email` (DKIM) | the public key Stalwart generates |
| TXT `_dmarc.ultimo.email` | `v=DMARC1; p=none; rua=mailto:postmaster@ultimo.email` — watch the reports, then move to `quarantine` / `reject` |
| (optional) `autoconfig` / SRV records | so Thunderbird and phones find the server settings by themselves |
| (optional) MTA-STS, TLS-RPT | enforce encrypted delivery to us |
### 6. Moving the existing mail from home.pl
1. Create the mailboxes in Ultimo.
2. Copy each home.pl mailbox into its new one over IMAP.
3. Switch the MX.
4. Keep the home.pl account in the webmail for a while to catch mail that
still arrives there (DNS caches), then remove it.
### 7. Checks after switching
- From outside: ports 25 / 465 / 587 / 993 answer (with a TLS certificate for
`mail.ultimo.email`).
- Reverse DNS → `mail.ultimo.email` → `178.219.143.96`, and Stalwart greets as
`mail.ultimo.email`.
- Mail to and from Gmail and Outlook: headers show SPF, DKIM and DMARC **pass**,
delivered to the inbox (not spam).
- Thunderbird sets up an account with just the address and an app password.
+2
View File
@@ -843,6 +843,8 @@ Receiving needs inbound port 25, an MX record and a fixed address; many home ISP
| **B. NAS + relay (recommended)** | Small VPS holds the MX, forwards to the NAS over WireGuard | Relay service (SMTP2GO / Postmark / SES) or the VPS | Mail stays on the NAS; reliable delivery; the VPS is the SMTP-time spam edge (§C.7). |
| **C. Stalwart on a VPS** | VPS | VPS | Mail stored off the NAS; webmail reaches it over the internet. |
**Chosen (2026-10-10): option A** — everything on the QNAP; see `docs/mail-server-network.md` for the network, DNS and port plan and the connection checks.
### D.2 Infrastructure
- `stalwart` service in both compose stacks (dev `webmail`, prod `webmail-prod` — never share the project name), image pinned to an exact version.
- **Stores:** data store = a separate `stalwart` database on the existing Postgres; blob store = filesystem under `DATA_ROOT/stalwart` (bind mount, not a named volume). `backup.sh` and `webmail.sh update` (backup → migrate → roll → health-gate) then cover it; no RocksDB to snapshot separately.