fix(mail): claim inbound IPv6 so IMAP/MX over v6 work; pin Postfix to v4 #98
No reviewers
Labels
No labels
bug
enhancement
in-progress
needs-info
needs-triage
p0
ready-for-agent
ready-for-human
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/nixos!98
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/97"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #97
What
mailpublished anAAAA(imap/mail.cloonar.com→2a01:4f8:c012:9d85::2) but never configured the address, so inbound IPv6 SYNs to IMAPS/MX reached the host's link with no owner and were black-holed — connections time out rather than being refused. This stayed latent until web-arm gained v6 egress (ADR 0010): RFC 6724 then made its FreeScout prefer::2forimap.cloonar.com, and PHP's IMAP client (stream_socket_client/webklex/php-imap) has no happy-eyeballs / v6→v4 fallback, so it hung.Changes
hosts/mail/configuration.nix— declare2a01:4f8:c012:9d85::2/64onenp1s0anddefaultGateway6 = fe80::1, claiming the publishedAAAAso inbound IMAPS and MX-over-v6 work. Dovecot already listens on[::]:993, so no change there.hosts/mail/modules/postfix.nix— pinsmtp_address_preference = ipv4.::2has no rDNS and is in no sending domain's SPF, so the defaultanywould prefer the v6 source to dual-stack MXes (Gmail/Microsoft) and get filed as spam. The address and the pin are coupled and ship together.docs/adr/0016-mail-claims-inbound-ipv6.md— records the decision; themailcounterpart of ADR 0010.Verification
mailpasses (:: mail OK) — the project's eval gate.dig AAAA imap.cloonar.comandmail.cloonar.comboth return2a01:4f8:c012:9d85::2, confirming the claimed address matches the live record (A →91.107.201.241, matching the diag finding).2a01:4f8:c012:9d85::2+enp1s0against the Hetzner console.Post-deploy checks
ip -6 routeshowsdefault via fe80::1 dev enp1s0.[2a01:4f8:c012:9d85::2]:993connects and FreeScout fetches mail.postconf smtp_address_preference→ipv4; a test mail to Gmail still leaves over IPv4 (check theReceived:header).Notes
::2is up, the address-family-agnostic firewall makes every open port reachable over v6 too — including LDAP 389/636 — mirroring today's v4 exposure. Explicitly accepted; out of scope here.Landed. Validated and merged with
tea pr merge --style merge(merge commit). Issue #97 auto-closed via theCloses #97link.Verification signal relied on: the repo's pre-commit dry-build (eval gate,
:: mail OK) — not re-run. The diff changes no derivationsrc/*Hash, so eval-only is adequate at the Nix layer; its only blind spot is the literal address/interface (handled below).Checked:
afk/97→main, workingCloses #97; issue auto-closed on merge, releasing the branch claim.secrets.yamledits;system.stateVersionuntouched (22.11); ADR 0016 is the correct next number (main was at 0015, no collision/renumber).enp1s0+ interface-qualifieddefaultGateway6 = { address = "fe80::1"; interface = "enp1s0"; }, the same pattern already deployed on web-arm.config = {…}block with the newsettings.mainpin. Confirmed against the nixos-25.11 module thatservices.postfix.configis amkRenamedOptionModuleontosettings.main, andmain.cfis built fromsettings.main— so the two merge into one attrset,smtp_address_preferenceis unique (no key collision), and the pin takes effect. The address+pin coupling is correct and documented in both files and the ADR.enp1s0literal — already live-confirmed on mail via the read-only diag channel during triage (#97 captured91.107.201.241/32+fe80::9400:2ff:fe23:b445onenp1s0).Post-deploy checks (the deploy action syncs to the SFTP chroot; mail runs
nixos-rebuild switchwithin ~5 min via bento):ip -6 route→default via fe80::1 dev enp1s0[2a01:4f8:c012:9d85::2]:993connects, and FreeScout (web-arm) fetches mail againpostconf smtp_address_preference→ipv4; a test mail to a Gmail address still shows an IPv4Received:sourceFailure mode of any wrong literal is bounded — admin/SSH stays on IPv4, so a bad v6 route cannot lock anyone out.