antispam: rspamd strips a sender's own verdict for every client
test / test (push) Canceled after 0s

rspamd 3.11.1's milter_headers skips its verdict routines - removes none
of the sender's X-Spamd-Result / X-Rspamd-Action, adds none - for a client
it counts as local (loopback, a unix socket, local_addrs: the private
ranges) or one that authenticated (skip_local, skip_authenticated, both on
by default; milter_headers.lua skip_wanted(), lua_ip.c lua_ip_is_local()).
The custom routine that signs X-SelfPost-Filter is never put through
skip_wanted(), so it signed the sender's own verdict, and Inbound > Log
showed it as rspamd's. Every sender is private when Docker's proxy relays
port 25 from the bridge gateway. Both options set to false.

Tried 2026-10-14 in a throwaway rspamd/rspamd:3.11.1-3 on the test server
(rspamc --ucl, a message carrying its own verdict headers): old config,
from 10.0.0.5 and as an authenticated user, the milter reply removed and
added neither and added the signature; new config, both removed and
rspamd's own added, as from 203.0.113.9.

Also: the sender rules take the envelope only (extract_from = "smtp"; by
default a bounce's From: header is used - tried: a bounce whose From:
domain is denied was refused before, is not now; a denied sender is
refused either way); the image pinned to 3.11.1-3, the build read (3.11 and
3.11.1 move with each rebuild); the prefilter lines no longer read go.
architecture.md and security.md say so. Assumptions C-R7, B-R1, C-R4,
C-R12, C-R5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-14 19:48:07 +03:00
co-authored by Claude Opus 5.5
parent a658da11f7
commit 31c2b50457
6 changed files with 90 additions and 20 deletions
+9 -5
View File
@@ -118,11 +118,15 @@ smtp/inet` removes Debian's stock smtpd). Outbound delivery still uses the
every change and on every start with the relay on. SelfPost never consults
them. The sidecar mounts the directory and rspamd's multimap rules
([deploy/antispam/rspamd/multimap.conf](../deploy/antispam/rspamd/multimap.conf))
read them: a deny entry is a prefilter `reject`, an allow entry a symbol
weighing -100. Allow is deliberately not a prefilter `accept`: with two
prefilters rspamd lets the one that ran first decide, and deny then won or
lost by rule order (seen on rspamd 3.11). A refusal by the deny list
overrides any score, so deny wins whichever entries match.
read them: a deny entry's rule carries the action `reject`, which rspamd
sets as a pre-result, an allow entry is a symbol weighing -100. Allow is
deliberately not an `accept` action: with two pre-results rspamd keeps the
one set first, and deny then won or lost by rule order (seen on rspamd
3.11, date not recorded). A refusal by the deny list overrides any score,
so deny wins whichever entries match. A sender entry is matched against
the envelope sender only (`extract_from = "smtp"`; by default rspamd takes
a bounce's `From:` header instead - rspamd 3.11.1,
`src/plugins/lua/multimap.lua`, the `from` callback).
How often an entry decided a message is not stored: `Lists.Usage` reads it
off the journal on each view of Filter lists - the rows a list decided (the
deny rule's reply, the allow rule's symbol, which the journal milter keeps
+10 -3
View File
@@ -261,7 +261,13 @@ operator can act on it.
keeps of the filter's verdict only what its patterns recognise. **A verdict
is read only next to the filter's signature.** The filter is attached so
that mail flows when it is down, and then nothing removes verdict headers a
sender wrote himself. So rspamd adds a third header, `X-SelfPost-Filter`:
sender wrote himself. Nor would rspamd itself by default for a client it
counts as local - a private address, which every client has when Docker's
proxy relays port 25 - or an authenticated one: `skip_local` and
`skip_authenticated` (rspamd 3.11.1, `milter_headers.lua`, `skip_wanted()`)
leave the sender's headers in place, while the signing routine below runs
regardless; both are turned off in `milter_headers.conf` (assumptions
C-R7, tried on 2026-10-14). So rspamd adds a third header, `X-SelfPost-Filter`:
the HMAC-SHA256 of the message's queue id under a key SelfPost generates
(`data/antispam/filter.key`) and the sidecar reads from the directory it
mounts. The journal milter recomputes it at end-of-message; with no
@@ -427,8 +433,9 @@ deferred item from the roadmap.
Senders on the internet, whom the signature is for, cannot read it. Trigger
to revisit: the same as for the list files - a shared host.
- **An allow entry for a sender address or domain trusts what the sending
server says.** The filter matches the envelope sender, which is not
authenticated: anyone who knows or guesses an allowed address can send as it
server says.** The filter matches the envelope sender (only that: a
bounce's `From:` header is not looked at, `extract_from = "smtp"`), which
is not authenticated: anyone who knows or guesses an allowed address can send as it
and is given the entry's weight, which no spam score outweighs. The page's
Help and the guide say so and recommend an entry for the client's address or
network instead. Deny entries have no such problem - claiming to be a denied