Files
selfpost/build/supervisord.conf
T
mix 2c7f0da3d8 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.example.com: 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

111 lines
3.5 KiB
Plaintext

; SelfPost process supervision (spec 4).
;
; Start ORDER is enforced by priority=: OpenDKIM, then the panel (which opens
; the journal-milter socket), then the Postfix wrapper — which additionally
; blocks until both milter sockets are ready before starting Postfix.
;
; If any managed process exhausts its restart retries (FATAL), the crashexit
; event listener brings the whole container down so Docker's restart policy can
; recreate it cleanly, rather than lingering with a dead component.
[supervisord]
nodaemon=true
user=root
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid
loglevel=info
; The control socket is reachable by the shared `selfpost` group so the panel
; (running as the unprivileged `panel` user) can ask supervisord to send
; OpenDKIM a reload signal after it rewrites the DKIM tables (spec 6.2). It is
; still unreachable by world; only root and the two SelfPost services are in the
; group. No HTTP auth is configured because access is gated by socket
; permissions, not the network.
[unix_http_server]
file=/run/supervisor.sock
chown=root:selfpost
chmod=0770
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///run/supervisor.sock
[program:opendkim]
command=/usr/sbin/opendkim -f -x /etc/opendkim.conf
priority=100
autostart=true
autorestart=true
startretries=3
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:panel]
command=/usr/local/bin/panel
user=panel
priority=200
autostart=true
autorestart=true
startretries=3
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:postfix]
command=/usr/local/bin/postfix-wrapper.sh
priority=300
autostart=true
autorestart=true
startretries=3
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; One-shot reload helper (spec 5.2, 7.2.12). The panel runs unprivileged and
; cannot run `postfix reload` itself, and it cannot signal the Postfix master
; directly: `postfix start-fg` forks a separate master process, so a signal to
; the supervised foreground process never reaches it (unlike OpenDKIM, which
; runs in the foreground as the supervised process itself). Instead the panel
; asks supervisord — over the group-accessible control socket — to run this
; program, which executes the canonical `postfix reload` as root. autostart is
; off (it only runs on demand) and a fast, clean exit is expected, so it never
; trips the crashexit listener.
[program:postfix-reload]
command=/usr/sbin/postfix reload
autostart=false
autorestart=false
startsecs=0
startretries=1
exitcodes=0
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; Periodic TLS-certificate refresh (spec 5.2 p.4). Runs a daily `postfix reload`
; so certificates the reverse-proxy renews in the read-only mount are picked up.
; Long-running (it loops), runs as root so it can reload Postfix, and never exits
; non-zero, so it neither trips the crashexit listener nor needs restarting.
[program:cert-reload]
command=/usr/local/bin/postfix-cert-reload.sh
priority=400
autostart=true
autorestart=true
startsecs=0
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[eventlistener:crashexit]
command=/usr/local/bin/crashexit.py
events=PROCESS_STATE_FATAL
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0