Commit Graph

2 Commits

Author SHA1 Message Date
mix b2692e4bb7 Phase 5: full outbound Postfix relay (465/587 SASL+TLS, sender binding, milters)
Generate the relay config from the environment at container start
(build/postfix-config.sh, run from entrypoint.sh):

- smtps 465 (implicit TLS) primary + optional submission 587 (STARTTLS),
  chroot=n so smtpd reaches the sasldb2/sender map under /data.
- Cyrus SASL against the panel-maintained sasldb2; realm left implicit so the
  authenticated name equals the bare login in smtpd_sender_login_maps.
- reject_sender_login_mismatch + relay/recipient restrictions with no
  permit_mynetworks: credentials-only, open relay impossible (spec 5, 5.1).
- TLS cert/key from TLS_CERT_FILE/TLS_KEY_FILE; daily postfix reload picks up
  renewed certs (postfix-cert-reload.sh under supervisord, spec 5.2).
- anvil level-1 rate limit from env (spec 5 p.5).
- Milter chain with per-milter action: OpenDKIM strict (tempfail), journal
  fail-open (accept) so monitoring never blocks the relay (spec 7.3).

Two integration fixes found on the server:
- postconf -F '*/*/chroot=n': Debian's chrooted delivery agent can't read
  /etc/resolv.conf, so MX lookups failed and mail never left.
- entrypoint sets /run/opendkim and /run/selfpost to group selfpost + setgid,
  and the journal stub chmods its socket 0660, so postfix can connect to both
  milter sockets (strict OpenDKIM was milter-rejecting all mail otherwise).

Verified on selfpost.mixfed.ru: gofmt/vet/test green, image builds; container
e2e — 465 auth+send DKIM-signed (d=domain,s=selfpost), 587 STARTTLS auth,
cross-domain sender 553, list-mode per-address binding, unauth relay 554,
real outbound delivery reaching the recipient MX over TLS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:37:16 +03:00
mix ed9e942e42 Phase 1: Docker image, supervisord, three-process cold start
Single bookworm-slim image running opendkim + panel + postfix under
supervisord with enforced start ordering (spec 4):

- build/Dockerfile: multi-stage static Go build; runtime installs postfix,
  opendkim, cyrus-sasl, supervisor, logrotate; unprivileged panel user (7.6.8).
- build/supervisord.conf: priority ordering opendkim -> panel -> postfix;
  crashexit event listener terminates the container on any FATAL process.
- build/postfix-wrapper.sh: waits for both milter sockets (test -S, 30s
  timeout) before `postfix start-fg`, exits non-zero on timeout.
- panel: HTTP :8080 stub + /healthz, journal-milter socket stub (so the
  wrapper's readiness probe passes), log-tailer stub; SIGTERM graceful stop.

Verified on the dev server: image builds, three processes live, panel serves
the stub, wrapper waits for sockets, and an unrecoverable panel failure brings
the container down cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:14:08 +03:00