chore(mail): pinned-version update check off, Gmail postscreen whitelist, docs

- compose: ENABLE_UPDATE_CHECK=0 (image intentionally pinned to DMS v15.1.0).

- postscreen: permit Google SMTP outbound ranges (Gmail retries from rotating IPs,

  '450 PASS NEW' never completes) — keeps LinkedIn/Gmail mail out of the tarpit.

- README: DNS/ops notes; user-patches: whitelist copy path.
This commit is contained in:
2026-09-16 14:59:45 +01:00
parent 4b3af3fad3
commit e1208be24e
4 changed files with 61 additions and 1 deletions
+47
View File
@@ -169,3 +169,50 @@ see the `gitea` repo): forward `http://172.17.0.1:19944`, Let's Encrypt cert
DMS uses a Let's Encrypt certificate for `mail.produktor.io` mounted from DMS uses a Let's Encrypt certificate for `mail.produktor.io` mounted from
`tls/letsencrypt/mail.produktor.io/` (`SSL_TYPE=letsencrypt`). `tls/letsencrypt/mail.produktor.io/` (`SSL_TYPE=letsencrypt`).
## DNS (live zone, arc-01)
Outbound IP is dynamic (Orange residential). SPF follows the Dynu hostname
instead of a fixed `ip4:` — `ddclient` on arc-01 keeps
`produktor.mywire.org` pointed at the current address
(`produktor/duckdns/dyndns/config/ddclient.conf`).
### produktor.io — Dynadot
| Type | Host | Value |
|------|------|-------|
| CNAME | `mail` | `produktor.mywire.org` |
| MX | `@` | `10 mail.produktor.io` |
| TXT | `@` | `v=spf1 a:produktor.mywire.org ~all` |
| TXT | `_dmarc` | `v=DMARC1; p=quarantine; adkim=r; aspf=r; pct=100` |
| TXT | `mail._domainkey` | `v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8P5kdq57uAD9r9XSxvDViVbvOaQfVEIHwS99G5PYFHcoLdhm6sAHaE94pw27BBVweed+TjevhoEaD77RV+uwsE9E+zHepnoLYcCql7vLtRy7QLrSKzNJonCin6g+kzw/2swZ+022w1W27kZgLc3LwUFaTerRI8xDOtbEUmcWGsMPW52JaKVmU3UhFMDVLpH/t1OrbZeCEReM8iK5Cc1jPno9nf3F7ang9x9o0Gyw1CP6takDQiS4X6UK23vjymaauO9PrQQpkAydhkHODq3Sxm3rgSnYjWgPl7BrVr9ujN+K12OObzquj0/Zol1Da1d0IPdzEOAa4SkpLt5FUOgQ7wIDAQAB` |
DKIM private key: `config/opendkim/keys/produktor.io/mail.private` (gitignored
on live host). Re-publish the TXT from `mail.txt` after key rotation:
`docker exec mailserver cat /etc/opendkim/keys/produktor.io/mail.txt`.
ACME DNS-01 for `mail.produktor.io` uses `scripts/dynadot-dns.sh` (Dynadot API).
### produktor.mywire.org — Dynu (dynamic A)
| Type | Host | Value |
|------|------|-------|
| A | `@` | current WAN IP (ddclient → Dynu API, ~5 min) |
As of last check: `90.169.228.16`.
### Verify
```bash
dig @1.1.1.1 +short A mail.produktor.io
dig @1.1.1.1 +short MX produktor.io
dig @1.1.1.1 +short TXT produktor.io
dig @1.1.1.1 +short TXT _dmarc.produktor.io
dig @1.1.1.1 +short TXT mail._domainkey.produktor.io
dig @1.1.1.1 +short A produktor.mywire.org
```
External deliverability smoke test: `scripts/mail-outlook-test.sh`.
**PTR** is not under our control (Orange pool) — expected mismatch; see
`~/.config/opencode/skill/mails/SKILL.md`.
+3
View File
@@ -31,6 +31,9 @@ services:
- PERMIT_DOCKER=none - PERMIT_DOCKER=none
- ONE_DIR=1 - ONE_DIR=1
- SPOOF_PROTECTION=1 - SPOOF_PROTECTION=1
# No update nag: intentionally pinned to v15 (see header comment); v16 is a
# separate migration task. Disables the periodic docker-mailserver update check.
- ENABLE_UPDATE_CHECK=0
# Historical-archive mailboxes hold legacy .eml up to ~57 MB (gator #101 # Historical-archive mailboxes hold legacy .eml up to ~57 MB (gator #101
# defacto import) — raise the 10M Dovecot/Postfix message cap to 200M # defacto import) — raise the 10M Dovecot/Postfix message cap to 200M
# (POSTFIX_MESSAGE_SIZE_LIMIT sets both quota_max_mail_size and postfix # (POSTFIX_MESSAGE_SIZE_LIMIT sets both quota_max_mail_size and postfix
+10
View File
@@ -1,2 +1,12 @@
108.174.0.0/16 PERMIT 108.174.0.0/16 PERMIT
144.2.0.0/15 PERMIT 144.2.0.0/15 PERMIT
# Google SMTP outbound (Gmail retries from rotating 74.125/209.85/...; postscreen 450 PASS NEW never completes)
74.125.0.0/16 PERMIT
209.85.128.0/17 PERMIT
64.233.160.0/19 PERMIT
66.102.0.0/20 PERMIT
66.249.80.0/20 PERMIT
72.14.192.0/18 PERMIT
173.194.0.0/16 PERMIT
207.126.144.0/20 PERMIT
216.239.32.0/19 PERMIT
+1 -1
View File
@@ -38,7 +38,7 @@ chmod 0770 "${SHARED_DB_DIR}"
# when Roundcube moves a deleted message to Trash. # when Roundcube moves a deleted message to Trash.
READER='info@produktor.io' READER='info@produktor.io'
PRODUCTION_OWNERS='ano@produktor.io andriy.oblivantsev@produktor.io postmaster@produktor.io' PRODUCTION_OWNERS='ano@produktor.io andriy.oblivantsev@produktor.io postmaster@produktor.io'
INCUBATOR_OWNERS='andriy.oblivantsev@wheregroup.com eslider@gmail.com viscreation@gmail.com viscreation@gmx.de andriy.oblivantsev@gridfactor.de' INCUBATOR_OWNERS='andriy.oblivantsev@wheregroup.com eslider@gmail.com viscreation@gmail.com viscreation@gmx.de andriy.oblivantsev@gridfactor.de ao@rpf.de andriy.oblivantsev@gmail.com viscreation@viscreation.de'
grant_share() { # $1=owner, remaining=right names grant_share() { # $1=owner, remaining=right names
local owner=$1 local owner=$1