from-header-binding: findings of the technical and security reviews
test / test (push) Canceled after 0s

Both reviews were of c171fb5, read-only, given the code and questions.

Found by both, and the one that would have refused ordinary mail from the
first day: a folded From: line - what a mail library makes of a long display
name - did not parse and was refused with 550. The line is unfolded before it
is read. e2e sends one through Postfix now.

Technical (Opus)
- A display name in a charset Go does not convert (windows-1251, koi8-r) was
  refused: such a name is passed through unread, only the address is judged.
- The statistics ran between binding the socket and serving it, with a
  supervisorctl that has no timeout: they are kept beside the check now and
  nothing of them is waited for. A supervisorctl that fails is logged.
- The count of deferred mail followed smtpd's reject lines and so missed a
  check that stops answering in the middle of a message, where cleanup
  writes them: it is taken at Postfix's warning, the line that names the
  socket. The test line for that case had been invented with the wrong
  process; it is marked as composed from Postfix's source.
- A card whose figures could not be read showed zeros and "ok": dashes and
  "unknown".
- entrypoint removes the sockets a killed container left, which satisfied
  the wrapper's wait before the check was listening.
- Outside quoted strings the line may hold one "@" only - a rule that needs
  no knowledge of OpenDKIM's parser.
- Comments that still spoke of two sockets, or of a session replaced after a
  refusal; the limits of the cards layout on a phone, said where it is defined.

Security (Fable; no Critical or High)
- Another sender's name in an encoded display name passed the second look:
  the decoded name is looked at as well.
- A client could arm the deferred counter by sending the socket's name where
  Postfix quotes it in a warning: the two warnings are matched from their
  first word.
- A refused message wrote a row per recipient: one row, naming the first
  recipient and how many more.
- Docs: the check's lookup is stricter than Postfix's for address extensions
  and the case of logins, rather than "cannot disagree"; what a display name
  looks like to a person is not promised; the timeouts and the 454 at AUTH
  stated as they are; "messages" are connections turned away.

Left as found, in the plan: downtime that crosses midnight is booked on the
day of the restart; a message with the null envelope sender leaves a row
without a domain; a log read again after an unclean stop counts again.

Test server, this tree: go vet ok, go test 23 packages ok, make e2e ok (330 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-12 20:06:02 +03:00
co-authored by Claude Opus 5.5
parent c171fb5bc7
commit 89fd0a6116
17 changed files with 239 additions and 85 deletions
+15 -6
View File
@@ -392,7 +392,10 @@ readings, in the order of what stops mail first:
panel did not answer while Postfix was running, and how many messages
Postfix deferred because of it (see [Who an application may send
as](#who-an-application-may-send-as)). A restart of the whole container is
not counted: mail was stopped for another reason then. The card warns when
not counted: mail was stopped for another reason then. What is counted as
a message is a connection Postfix turned away - it refuses before a
message is offered - so a monitor that only connects during an outage is
counted too. Days are UTC days. The card warns when
a message was deferred in the last 24 hours. If these numbers do not stay
at nothing, the panel is in the way of your mail more than it should be -
report it.
@@ -1097,8 +1100,13 @@ listed for it. Since `2.4.0` that rule covers both senders a message has:
A message whose `From:` is anything else is refused with `550 5.7.1` and a
text naming the line: another of your domains, the server's own name, an
address at a domain that is not on this server, a line with two addresses,
or no `From:` at all. A name this server signs for may not appear anywhere
in the line besides the address itself, a display name included. The refusal
or no `From:` at all. The line is read strictly, because two programs read
it - the check and the signer - and must not be able to read it differently:
outside a quoted display name it may hold one `@`, the address's own, and no
other name this server signs for may follow an `@` anywhere in it, a quoted
name included. A display name with a comma or brackets has to be quoted
(`"Shop, Inc." <orders@…>`), as the mail standard asks and mail libraries
do. The refusal
is permanent, so the application does not send the message again; it is a
row of the [Outbound log](#outbound-log) tagged `wrong From`, and the row's
page shows what the line said.
@@ -1113,9 +1121,10 @@ What this stops working when you upgrade from an earlier version:
`Reply-To:` and send from an address of your own, which is also what
DMARC requires.
The check is strict: while the panel does not answer, Postfix replies `451`
and applications send again later; nothing is accepted unchecked. **Server ›
Health** counts how often that happens.
The check is strict: while the panel does not answer, Postfix answers with a
temporary error (`451`, and `454` to the login) and applications send again
later; nothing is accepted unchecked. **Server › Health** counts how often
that happens.
*Export domain* writes a single-domain file; *Import a domain* on *Server ›
Backup* reads one back in (**global role only**) — see [Exporting and importing