fix: port-25 milter chain, DMARC report validation, auto rate-limit window
test / test (push) Has been cancelled
test / test (push) Has been cancelled
Review pass over the 1.4.0-1.9.1 work. Port 25 no longer inherits main.cf's submission milter chain: smtpd_milters is always overridden on smtp/inet, to the antispam milter or to nothing. Without that, OpenDKIM (strict, tempfail) gated relayed mail during a signing outage, and the journal-milter filed inbound mail into the outbound send log under the sender's domain -- so Deliveries showed other people's mail and a forged From: consumed that domain's level-2 budget. architecture.md already described the intended behaviour; the code now matches. DMARC reports are checked before they are stored: the policy domain must be a configured sending domain, and must match the +tag of a per-domain hosted address. The rua address form is predictable, so anyone able to reach it could file reports under any domain and evict genuine ones through the retention cap. Zip attachments are now decompressed as well as gzip. The auto level-2 ceiling is scaled to the level-1 window before the multiplier is applied; any window other than 3600s shifted the allowance by window/3600. Migration 0010 indexes send_log(app_login, created_at) -- both the per-MAIL FROM rate-limit count and the per-application statistics were scanning the created_at range. e2e now regenerates the Postfix config in-container for all four port-25 flag combinations and requires postfix check to pass; the 1.9.1 crash-loop shipped in 1.4.0 and survived three releases because only the flags-off path was exercised. Migration 0009 stays as shipped -- its conversion branch only fires for a database carrying application-scope allowed_ips from before 1.9.0. The schema reference notes that the old list was permissive and the new column is restrictive; the guide documents the allow-list as a sending control on a fail-open path, not an authentication boundary. Also: gofmt across cmd/ and internal/, unused duplicate validators dropped from internal/web/validate, export no longer swallows store errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+30
-4
@@ -729,6 +729,13 @@ only sees SelfPost. Default action is fail-open (`accept`) so a down sidecar
|
||||
does not block backup-MX; set `INBOUND_ANTISPAM_MILTER_ACTION=tempfail` to
|
||||
defer instead.
|
||||
|
||||
**Inbound mail is not in Deliveries.** Port 25 runs its own milter chain: the
|
||||
optional anti-spam milter, and nothing else. OpenDKIM does not sign relayed
|
||||
mail (and a signing outage therefore cannot defer it), and the journal-milter
|
||||
does not file it — [Deliveries](#deliveries) stays a record of what *you* sent.
|
||||
This also keeps a forged `From:` on inbound mail from counting against a
|
||||
sending domain's [level-2 rate limit](#rate-limiting--level-2-domain-and-application).
|
||||
|
||||
Inbound configuration lives in SQLite and `/data/postfix/` map files, so it
|
||||
is included in a [full backup](#full-backup-and-restore). Single-domain
|
||||
export/import is sending domains only.
|
||||
@@ -768,6 +775,15 @@ alone, `check_recipient_access` permits only configured report addresses;
|
||||
everything else is rejected. With inbound relay enabled too, both allow-lists
|
||||
apply.
|
||||
|
||||
**Reports are checked before they are stored.** The address form is
|
||||
predictable, so the report XML is treated as untrusted input: a report is
|
||||
accepted only when the domain it claims in `policy_published` is a sending
|
||||
domain configured here, and — when it arrived at a per-domain hosted address —
|
||||
only when that domain matches the address's `+tag`. Anything else is refused
|
||||
and counted in *parse failures* on the DMARC page, so a stranger cannot file
|
||||
reports under your domain or push genuine ones out through the retention cap.
|
||||
Reports are accepted as gzip, zip, or plain XML.
|
||||
|
||||
Parsed report data lives in SQLite and is included in a
|
||||
[full backup](#full-backup-and-restore).
|
||||
|
||||
@@ -844,9 +860,16 @@ sending as that domain. When unset, only level 1 applies for non-privileged
|
||||
senders.
|
||||
|
||||
**Client IP allow-list (application)** — optional restriction on an
|
||||
application: when enabled, list one or more client IPs that may authenticate
|
||||
and submit mail as that application. When disabled, any client IP is allowed.
|
||||
This is independent of rate limits.
|
||||
application: when enabled, list one or more client IPs that may **submit** mail
|
||||
as that application; a session from any other address is refused with a 4xx at
|
||||
`MAIL FROM`. When disabled, any client IP is allowed. This is independent of
|
||||
rate limits, and it takes exact addresses only — CIDR ranges are not parsed.
|
||||
|
||||
It is **not an authentication boundary.** The SASL login itself still succeeds;
|
||||
the check runs in the journal-milter, on the same fail-open path as level 2
|
||||
(below), so if the milter is unavailable the restriction is skipped rather than
|
||||
enforced. Use it to narrow where an application may send from, not to contain a
|
||||
leaked password — for that, regenerate the password.
|
||||
|
||||
**Level 2 — application** — optional override of the domain limit for one
|
||||
application (≤ level 1). The ceiling may be **higher or lower** than the domain
|
||||
@@ -868,7 +891,10 @@ hours and on demand via **Recalculate now**.
|
||||
journal-milter and is deliberately fail-open: if the rate-limit lookup hits
|
||||
a store error, or the connecting client's IP is not available to the
|
||||
milter, level 2 is skipped and the message is accepted rather than held up.
|
||||
Level 1 is the backstop that keeps working even when level 2 cannot run.
|
||||
The same applies if the milter itself is down — Postfix is configured to
|
||||
accept rather than defer when it cannot reach it. Level 1 is the backstop
|
||||
that keeps working even when level 2 cannot run, and it is the only one of
|
||||
the two that does not depend on the panel process.
|
||||
|
||||
### Deliveries
|
||||
|
||||
|
||||
Reference in New Issue
Block a user