base-trixie: the image on Debian trixie, Postfix 3.10.13; Received: hidden on 465/587
test / test (push) Canceled after 0s

The runtime base is debian:trixie-slim, the build stage golang:1.26-trixie:
Postfix 3.10.13 with upstream's 2026 security fixes up to the August
batch, libmilter 8.18.1, OpenSSL 3.5. Every citation of another program in
code, scripts and documents was re-read against the trixie sources
(dev/upstream; bookworm's kept in dev/upstream-bookworm) and updated;
development.md's cited-package table holds the trixie versions.

What the re-reading changed in behaviour:
- logtail: reject lines with sasl_method/sasl_username/sasl_sender
  (smtpd_check.c:1019-1026); cleanup's "removed (canceled)" forgets a
  port-25 id (cleanup_api.c:363-364).
- internal/postfix/retry.go reads postconf's stdout only: since 3.9 a
  deprecated-parameter warning on stderr made one value too many and the
  panel fell back to the compiled-in retry policy.
- logrotate-loop.sh: logrotate 3.22 exits 1 when it ignores a config;
  the loop refuses on that output whatever the status.
- postfix-config.sh: cyrus_sasl_config_path set explicitly (bookworm's
  07_sasl_config.diff is gone); maillog_file_permissions=0640; mynetworks
  explicit; Dockerfile: SYNC_CHROOT= in /etc/default/postfix,
  opendkim-tools dropped, openssl named, systemd-standalone-sysusers so
  cron does not bring the whole of systemd.
- The selfpost group is 10998, outside the system range 100-999: in
  trixie a package took 998 and the build failed (owner's decision:
  a conflict-free id, the distribution's own ids untouched). The
  entrypoint hands /data back by name when the user or the group differs.
  Guide, backup refusal text, help (design commit 68ec8b2), e2e.
- smtpd_hide_client_session=yes on smtps and submission (owner's
  decision): Received: names no client, TLS, login or protocol; port 25
  keeps the full header. e2e checks both.
- test/upstream/opendkim-probe.sh makes keys with openssl genrsa.

Tests: on the server, make e2e exit 0 (674 s, the image built on trixie),
go vet + go test 27 packages ok. CHANGELOG [Unreleased], guide,
architecture.md, security.md, roadmap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-15 19:31:35 +03:00
co-authored by Claude Opus 5.5
parent 68ec8b274c
commit 1c6e04b7d3
80 changed files with 970 additions and 454 deletions
+26 -4
View File
@@ -15,12 +15,12 @@ import (
)
// The mail.log lines in this file are composed by hand, after the formats the
// code cites (Postfix 3.7.11, src/global/log_adhoc.c and the others), unless a
// code cites (Postfix 3.10.13, src/global/log_adhoc.c and the others), unless a
// comment says where one was seen; some carry no stamp, which the parsers
// take as they come. One shape Postfix does not write here: opendkim lines in
// mail.log (OpenDKIM logs through syslog(3), to the container log - build/opendkim.conf).
// qmgr's expiry line, as Postfix 3.7.11 writes it (src/qmgr/qmgr_active.c,
// qmgr's expiry line, as Postfix 3.10.13 writes it (src/qmgr/qmgr_active.c,
// qmgr_active_done_25_generic(): "%s: from=<%s>, status=%s, returned to
// sender"); composed by hand after that format. The sender is the client's
// text, so a line only resembles one when it ends as qmgr's does.
@@ -36,6 +36,11 @@ func TestParseExpiry(t *testing.T) {
// qmgr's ordinary from= line, whose sender carries the text.
{"Oct 14 09:01:02 relay postfix/qmgr[93]: 41E862C00D9E: from=<x>, status=expired, returned to sender>, size=500, nrcpt=1 (queue active)", ""},
{"Oct 14 09:01:02 relay postfix/qmgr[93]: 41E862C00D9E: removed", ""},
// cleanup's removal of a message it did not queue (Postfix 3.10.13,
// src/cleanup/cleanup_api.c, cleanup_flush(): "%s: removed (%s)")
// says nothing of what became of a queued one.
{"Oct 14 09:01:02 relay postfix/cleanup[52]: 41E862C00D9E: removed (canceled)", ""},
{"Oct 14 09:01:02 relay postfix/cleanup[52]: 41E862C00D9E: removed (discarded)", ""},
} {
id, ok := parseExpiry(c.line)
if ok != (c.id != "") || id != c.id {
@@ -194,7 +199,8 @@ func TestParseDelivery(t *testing.T) {
status: store.StatusSent,
},
{
// The stamp Postfix 3.7's postlogd writes, as on the stand.
// The stamp postlogd writes, as on the stand (Postfix 3.7;
// 3.10.13's msg_logger_print() keeps the format).
name: "syslog stamp",
line: "Oct 14 11:05:51 mail postfix/smtp[26]: 41E862C00D9E: to=<a@example.net>, relay=x, dsn=2.0.0, status=sent (250 OK)",
wantOK: true,
@@ -221,6 +227,22 @@ func TestParseDelivery(t *testing.T) {
line: "Oct 14 11:05:51 mail postfix/smtpd[10]: 5B4A2C1D3E: client=unknown[203.0.113.7], sasl_username=41E862C00D9E: to=<victim@example.net>, status=sent",
wantOK: false,
},
{
// Postfix 3.10.13, src/cleanup/cleanup_api.c, cleanup_flush():
// "%s: removed (%s)" for a transaction cleanup did not queue. It
// is no result for any recipient. Composed from that format.
name: "cleanup's removed (canceled) is not one",
line: "Oct 14 11:05:51 mail postfix/cleanup[52]: 41E862C00D9E: removed (canceled)",
wantOK: false,
},
{
// Postfix 3.10.13, src/smtpd/smtpd.c, smtpd_proto(): "%s: %s after
// %s (%lu bytes) from %s", the queue id first since 3.10. Composed
// from that format.
name: "smtpd's lost connection with a queue id is not one",
line: "Oct 14 11:05:51 mail postfix/submission/smtpd[10]: 41E862C00D9E: lost connection after DATA (5420 bytes) from unknown[203.0.113.7]",
wantOK: false,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
@@ -920,7 +942,7 @@ func TestSplitTimestamp(t *testing.T) {
stamp, rest string
}{
{
// An RFC 3339 stamp is not one Postfix 3.7 writes (assumptions
// An RFC 3339 stamp is not one Postfix 3.10 writes (assumptions
// A-12): it is not taken off.
name: "RFC 3339 is not a stamp Postfix writes",
line: "2026-08-03T05:15:52.219218+00:00 mail postfix/qmgr[10]: 4A1B2C3D: removed",