Invite a friend; handle field shows the domain; system mail fixes

- Invitations: each user can invite two friends by email (sign-up works
  through the link even while registration is closed); admin-editable
  template, subject and sender.
- Invitation sender defaults to the system-mail From address, which the
  SMTP account is allowed to send as (was postmaster@APP_DOMAIN, refused
  with 553 when the two differ).
- Sign-in, sign-up and forgot-password fields show the fixed
  @domain after the typed name; the registration status reports it.
- System mail gets a Date header; the invitation's plain-text part keeps
  its paragraphs and line breaks.
- SMTP EHLO uses SMTP_HELO_NAME / APP_DOMAIN, not the container name.
- DNS recommendations for the mail domains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 19:34:31 +02:00
co-authored by Claude Opus 5.5
parent addb2232a1
commit 463f96ad57
30 changed files with 1870 additions and 42 deletions
+107
View File
@@ -0,0 +1,107 @@
# DNS recommendations — ultimo.email and imaginary-pixels.com
Checked 2026-10-14, after the security assessment (DMARC/SPF finding) and the
first DMARC test mails. In priority order. See also `docs/mail-server-network.md`
for the hosted-mail (Stalwart) plan.
## State when checked
| | ultimo.email (DNS at home.pl) | imaginary-pixels.com (DNS at Cloudflare) |
|---|---|---|
| MX | `serwer2295308.home.pl` (46.242.246.139) | `_dc-mx…` → 46.242.246.139 (home.pl) |
| SPF | `v=spf1 a mx ~all` | `v=spf1 mx a ptr ~all` |
| DKIM | selector `dkim` present | selector `dkim` present, signing verified (PASS at Gmail) |
| DMARC | `p=none; rua=mailto:postmaster@ultimo.email; fo=1` | `p=none; rua=mailto:admin@imaginary-pixels.com` (added 2026-10-14) |
| CAA | none | none |
| DNSSEC | no | no |
| Other | wildcard `*.ultimo.email` → `ultimo.email`; SRV `_imaps`/`_submission` → `ultimo.email:993/587` (ports closed) | — |
Mail from the webmail goes out through home.pl's SMTP (46.242.246.139), which
both domains' SPF already allow through `mx`. The home IP 178.219.143.96 has
reverse DNS `pub-143.96.multiplay.pl` (cannot be changed by the ISP), so the
QNAP must not send directly to the internet.
## 1. Remove the mail-app SRV records for ultimo.email — now
```
_imaps._tcp.ultimo.email SRV 10 5 993 ultimo.email.
_submission._tcp.ultimo.email SRV 10 5 587 ultimo.email.
```
They tell Thunderbird, iPhones and other mail apps that ultimo.email's mail
server is `ultimo.email` on 993/587 — the home IP, where those ports are closed
(hosted mail is not live; mail is at home.pl). Apps that set themselves up from
them fail to connect. Delete both in the home.pl DNS panel; add them back when
Stalwart goes live.
## 2. Remove the wildcard `*.ultimo.email` — now
Every made-up name (e.g. `random-xyz123.ultimo.email`) resolves to the home IP
and lands on the Synology; lookups such as `_mta-sts.ultimo.email` and
`_smtp._tls.ultimo.email` come back with the SPF record. Delete the wildcard and
keep explicit records only for names in use: `ultimo.email` itself, plus `www`
if wanted (point 3). `mail.` and `autoconfig.` come back with the Stalwart
launch. Check first whether the wildcard was a home.pl default or deliberate.
## 3. `www.ultimo.email` — decide
It resolves but has no certificate, so `https://www.ultimo.email` shows a
browser error. Either drop it (it disappears with the wildcard), or keep it as
`CNAME ultimo.email`, add it to the DSM certificate and add a reverse-proxy
rule for it (HTTP rule with `X-Forwarded-Proto: http`, HTTPS rule like the
main one).
## 4. CAA for ultimo.email — recommended
Only the named certificate authority may issue certificates for the domain.
The Synology uses Let's Encrypt:
```
ultimo.email. CAA 0 issue "letsencrypt.org"
```
Before adding it, confirm with home.pl that they don't issue a certificate for
ultimo.email themselves; if they do, add their CA as another `issue` line or
skip CAA. Not needed for imaginary-pixels.com (Cloudflare manages its
certificates there).
## 5. SPF cleanups — after reviewing the DMARC reports
- **ultimo.email:** `v=spf1 mx -all`. The QNAP sends through home.pl (covered
by `mx`), so `a` — the home IP — is not needed. Change it together with the
move to `p=quarantine`.
- **imaginary-pixels.com:** `v=spf1 mx ~all`. `a` points at Cloudflare's web
servers (never send mail); `ptr` is deprecated. First check the reports for
any other legitimate sender (e.g. a website contact form).
## 6. DMARC staging — both domains
1. Collect aggregate reports for about 2 weeks (`p=none`). Make sure
`postmaster@ultimo.email` exists at home.pl, or its reports go nowhere.
2. When every legitimate sender passes: `p=quarantine`.
3. After another 2–4 weeks without legitimate failures: `p=reject`.
DMARC TXT records have a 5-minute TTL, but a missing record is cached for up
to 30 minutes (Cloudflare SOA minimum) — retest a while after creating one.
## 7. DNSSEC — optional, worth doing
Protects MX and other records against forged DNS answers.
- **imaginary-pixels.com:** Cloudflare → DNS → Settings → Enable DNSSEC, then
add the DS record it shows at the registrar.
- **ultimo.email:** check the home.pl panel for a DNSSEC option.
## Later — when hosted mail (Stalwart) goes live
- MX to `mail.ultimo.email`; SPF to match the real sending path (with the
home.pl relay, `mx`/home.pl keeps covering it).
- SRV (`_imaps`, `_submission`, `_submissions`) and `autoconfig` back, pointing
at `mail.ultimo.email`.
- Optional MTA-STS + TLS-RPT (needs a real `mta-sts.ultimo.email` with a
certificate — another reason to remove the wildcard now).
- `SMTP_HELO_NAME=mail.ultimo.email` in `docker/.env.prod` if Stalwart's
hostname should be the one announced.
Not fixable in DNS: the home IP's reverse DNS — avoided by always sending
through home.pl.