Files
selfpost/build/supervisord.conf
T
mixandClaude Opus 5.5 7af37654a1 cert-reload: renew Postfix's copy of a renewed certificate (audit H2)
postfix-config.sh copies the mounted certificate and key into
/etc/postfix/tls-internal at start (root-owned, key 0600) and Postfix reads
the copy; cert-reload only ran `postfix reload` daily, which re-read the
stale copy, so a certificate renewed while the container ran expired on
465/587 with the old one.

postfix-cert-reload.sh now, every TLS_RELOAD_INTERVAL_SECONDS: compares the
mount with the copy (cmp); when they differ and the certificate and key
belong together (openssl public keys equal - a renewer writes them one
after the other), writes each file beside its target, root-owned with the
start's modes, renames it over the target, and reloads Postfix. Nothing
changed - no reload. No copy (files missing at start) - the old periodic
reload. Never exits non-zero.

e2e renewed_certificate_picked_up: writes a new certificate into the
stand's mount and waits (30 s) for port 465 to present its serial;
compose.override.yml sets TLS_RELOAD_INTERVAL_SECONDS=2.
supervisord.conf comment, architecture.md (process table, § Postfix TLS
certs, the env knob), guide (after the proxies: picked up within a day).
CHANGELOG [Unreleased], audit plan line ticked, roadmap 12/61.

Evidence: GOOS=linux go vet in test/e2e clean; the server run of the stage
follows before the push.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:25:20 +03:00

128 lines
4.0 KiB
Plaintext

; SelfPost process supervision.
;
; Start ORDER is enforced by priority=: OpenDKIM, then the panel (which opens
; the sender check's and the journal-milter's sockets), then the Postfix
; wrapper — which additionally blocks until the 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. 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. 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
; TLS-certificate refresh. Once a day it compares the certificate the
; reverse-proxy renews in the read-only mount with the copy Postfix reads, and
; when they differ copies it again and runs `postfix reload`.
; 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
; Periodic logrotate for /data/log/mail.log (daily, 7-14 files kept in the
; image). Runs as root so logrotate can read/rotate the log. Exits non-zero when
; the config is group/other-writable (logrotate would ignore it silently);
; autorestart surfaces BACKOFF on the Status page.
[program:logrotate]
command=/usr/local/bin/logrotate-loop.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