health: sockets dialled, the certificate Postfix serves, "deferred" only for the queue
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
Audit 2026-10-12, stage 2c, "Health says what is served"; the pages of
ea8b6bf's mockup.
Sockets (A-38). A unix socket's file outlives a killed OpenDKIM or panel
and refuses every connect() (unix(7)); Health said "Listening" from a
stat. CheckSocket and CheckMilter's unix case now dial: refused is
"Nothing listens on the ... socket: the program that made it has
stopped", EACCES is unknown (the panel may not connect), anything else
"does not answer". The probe closes before a byte, which no milter logs:
libmilter takes the EOF as SMFIC_EOF without a line (sendmail
8.17.1.9-2+deb12u2, the image's libmilter1.0.1, libmilter/comm.c
mi_rd_cmd(), engine.c mi_engine() - its source is now in dev/upstream),
OpenDKIM's mlfi_close() has no connection to free, go-milter v0.4.1
returns quietly on io.EOF (session.go, HandleMilterCommands()). The page
polls every 5 s while in view, so this mattered.
Certificate (A-65). Postfix serves build/postfix-config.sh's copy,
refreshed by cert-reload every TLS_RELOAD_INTERVAL_SECONDS. The panel now
reads smtpd_tls_cert_file from postconf -h at its start (as the retry
policy) and TLS_RELOAD_INTERVAL_SECONDS; CheckServedCertificate checks the
served copy and, when the mounted file differs byte for byte (cert-reload's
own cmp -s), names it as Renewal: its expiry and mtime, two facts on the
card. Waiting is fine for the interval: a nearing expiry of the served one
is then no warning (an expiry still an error); past interval + 1 h it is a
warning pointing at cert-reload's lines. The notifier sends no expiry
notice while a renewal waits on time.
Wording (D-P1, D-P3, and the same for the spam filter): Sender check
note, help topics sender-check, upstream, filter, certificate - word for
word as in the mockup; the filter row's detail too.
Tests: stale socket file (Linux) refused for CheckSocket and CheckMilter;
CheckServedCertificate (same, waiting, late, expired + waiting, no served
path); intervalText; the card's facts; the notifier quiet while a renewal
waits, speaking once it is late. Stand (2026-10-15, make e2e, exit 0):
new health_shows_what_is_served - the three sockets dialled ok by the
panel's user; a certificate mounted without its key is refused by
cert-reload ("do not belong together") and the card shows the served
expiry, "Renewal expires" with the mounted one's, "Renewal mounted", and
"Postfix still serves the certificate above."; a matching pair is then
served again. go vet and go test ./... pass on the server (24 packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -370,6 +370,9 @@ func (n *Notifier) checkCertificate() {
|
||||
if c.NotAfter.IsZero() {
|
||||
return // no certificate configured, or one that cannot be read: Health says so
|
||||
}
|
||||
if c.Renewal != nil && !c.Renewal.Late {
|
||||
return // a renewal is mounted and Postfix takes it up at its next check
|
||||
}
|
||||
const key = "cert"
|
||||
mark := c.NotAfter.UTC().Format(time.RFC3339)
|
||||
st, have, err := n.st.GetNotifyState(key)
|
||||
|
||||
@@ -251,6 +251,18 @@ func TestCertificateIsSaidTwice(t *testing.T) {
|
||||
want(t, "no certificate", b.hour())
|
||||
}
|
||||
|
||||
// The certificate Postfix serves nears its end while a renewal waits in the
|
||||
// mount: nothing is said while Postfix's check for it is still to come, and
|
||||
// the usual notice once the renewal has waited past it (A-65).
|
||||
func TestCertificateWithARenewalWaiting(t *testing.T) {
|
||||
b := newBench(t)
|
||||
expiry := b.now.Add(10 * 24 * time.Hour)
|
||||
b.cert = health.Certificate{NotAfter: expiry, Renewal: &health.Renewal{NotAfter: expiry.Add(80 * 24 * time.Hour)}}
|
||||
want(t, "renewal waiting", b.hour())
|
||||
b.cert.Renewal.Late = true
|
||||
want(t, "renewal late", b.hour(), "root@example.org|[SelfPost] TLS certificate of mail.example.org expires in 10 days")
|
||||
}
|
||||
|
||||
// A message older than three days is said once, and its going once; a queue
|
||||
// that cannot be listed says nothing.
|
||||
func TestQueueIsSaidOnceAndClearedOnce(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user