antispam: rspamd 4.2.2-0, no capabilities, only the milter on the network (audit S4)
Security audit 2026-10-17, finding S4 (low), stage 4. The sidecar ran
rspamd 3.11.1 (December 2024) with default capabilities; upstream is 4.2.2
(2026-10-10), and the line between fixed a heap overflow in
util.lower_utf8, a use-after-free on reload and parser crashes - rspamd
reads every byte a stranger sends to port 25.
A read-only reading of rspamd 3.11.1 and 4.2.2 and of rspamd-docker
v3.11.1+3 and v4.2.2+0, side by side (sources in dev/upstream), found every
function SelfPost relies on unchanged: milter_headers (skip_wanted, the
custom routine, remove 0, create_specific_keyed, get_queue_id from {i}),
the X-Spamd-Result text, multimap (from/smtp, ip, the reject pre-result,
file-map rereading), the image's uid 11333 and CMD. It also found that the
image binds the normal worker (which takes client-named file and shm
inputs) and the controller (default password "q1") on every address,
reachable from any container on the compose network - so:
- image rspamd/rspamd:4.2.2-0 (the one build of 4.2.2; the others float);
- cap_drop: [ALL], no-new-privileges (rspamd starts unprivileged, ports
above 1024, nothing setuid);
- RSPAMD_LOCAL_ADDR=localhost keeps every worker on loopback, and a new
antispam/rspamd/worker-proxy.inc puts the proxy alone on *:11332. rspamd
expands its template syntax in comments too - the first version of that
comment quoted it and the config failed to load; it quotes none now.
Checked in a throwaway container on the test server with the three files
and the fragment's options (dev/server/rspamd-try.sh): proxy on any
address, 11333/11334 on loopback; from a private address a sender's own
verdict headers removed and rspamd's added; X-SelfPost-Filter equal to
openssl's HMAC-SHA256 of the queue id; a denied address refused with the
deny list's message; healthy. The e2e has no rspamd; the live sidecar is
checked at the rollout. Citations re-pointed to 4.2.2.
Docs: guide § Inbound relay (the third file; copy antispam/ again);
CHANGELOG; plan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
- **A session made under a longer `PANEL_SESSION_IDLE_DAYS` is cut to the
|
- **A session made under a longer `PANEL_SESSION_IDLE_DAYS` is cut to the
|
||||||
current window** at its next request (audit A10), not kept until its old
|
current window** at its next request (audit A10), not kept until its old
|
||||||
expiry.
|
expiry.
|
||||||
|
- **The antispam sidecar runs rspamd 4.2.2, hardened** (audit S4): from
|
||||||
|
3.11.1 (December 2024; the line between fixed a heap overflow, a
|
||||||
|
use-after-free on reload and parser crashes), with no Linux capability,
|
||||||
|
`no-new-privileges`, and only the milter on the compose network - the
|
||||||
|
scanner and the controller, whose default password is public, stay on the
|
||||||
|
sidecar's loopback. A third file, `antispam/rspamd/worker-proxy.inc`, is
|
||||||
|
mounted; copy it with the fragment.
|
||||||
- **The TLS private key no longer has to be readable by Postfix** (audit
|
- **The TLS private key no longer has to be readable by Postfix** (audit
|
||||||
I1): the guide gave it to group 10998 for the panel's backup, and
|
I1): the guide gave it to group 10998 for the panel's backup, and
|
||||||
`postfix` is in that group, so every Postfix daemon - an smtpd talking to
|
`postfix` is in that group, so every Postfix daemon - an smtpd talking to
|
||||||
|
|||||||
@@ -9,10 +9,11 @@
|
|||||||
# Requires INBOUND_RELAY_ENABLE=true in .env. The milter sees the real client
|
# Requires INBOUND_RELAY_ENABLE=true in .env. The milter sees the real client
|
||||||
# IP, HELO and PTR — unlike the upstream, which only sees SelfPost's address.
|
# IP, HELO and PTR — unlike the upstream, which only sees SelfPost's address.
|
||||||
#
|
#
|
||||||
# The rspamd image is pinned to one build, 3.11.1-3 - "3.11" and "3.11.1"
|
# The rspamd image is pinned to one build, 4.2.2-0 - "4.2", "4" and "latest"
|
||||||
# move with each rebuild (Docker Hub rspamd/rspamd tags 3.11.1-0 ... -3) -
|
# are rebuilt every night - and it is the build the comments in
|
||||||
# and it is the build the comments in antispam/rspamd/ were read against;
|
# antispam/rspamd/ were read against (rspamd 4.2.2, rspamd-docker v4.2.2+0;
|
||||||
# bump it deliberately, and read them again. Default milter action is fail-open (accept) so a down sidecar
|
# 3.11.1-3 until 2.14.4, security audit 2026-10-17, S4); bump it
|
||||||
|
# deliberately, and read them again. Default milter action is fail-open (accept) so a down sidecar
|
||||||
# does not block backup-MX. Set INBOUND_ANTISPAM_MILTER_ACTION=tempfail to
|
# does not block backup-MX. Set INBOUND_ANTISPAM_MILTER_ACTION=tempfail to
|
||||||
# defer instead.
|
# defer instead.
|
||||||
|
|
||||||
@@ -26,12 +27,21 @@ services:
|
|||||||
- antispam
|
- antispam
|
||||||
|
|
||||||
antispam:
|
antispam:
|
||||||
# The milter: rspamd 3.11.1, conf/worker-proxy.inc, "milter = yes" for the
|
# The milter: rspamd 4.2.2, conf/worker-proxy.inc, "milter = yes" for the
|
||||||
# rspamd_proxy worker, which the image binds to *:11332 (rspamd-docker
|
# rspamd_proxy worker, bound to *:11332 by antispam/rspamd/worker-proxy.inc
|
||||||
# v3.11.1+3, patches/rspamd-docker-config.diff), and runs as uid 11333
|
# while every other worker stays on loopback (RSPAMD_LOCAL_ADDR below);
|
||||||
# (its Dockerfile, USER 11333:11333).
|
# it runs as uid 11333 (rspamd-docker v4.2.2+0, Dockerfile, USER
|
||||||
image: rspamd/rspamd:3.11.1-3
|
# 11333:11333) and needs no capability: it starts unprivileged, binds
|
||||||
|
# ports above 1024 and runs nothing setuid (src/rspamd.c, detect_priv()).
|
||||||
|
image: rspamd/rspamd:4.2.2-0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# Workers on loopback; the proxy alone is put back on every address.
|
||||||
|
RSPAMD_LOCAL_ADDR: localhost
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
# A ceiling on processes and threads (security audit 2026-10-17, I3).
|
# A ceiling on processes and threads (security audit 2026-10-17, I3).
|
||||||
pids_limit: 256
|
pids_limit: 256
|
||||||
# No host ports: only SelfPost talks to the milter on the compose network.
|
# No host ports: only SelfPost talks to the milter on the compose network.
|
||||||
@@ -41,4 +51,6 @@ services:
|
|||||||
# The allow and deny lists kept in the panel: the rules, and the files
|
# The allow and deny lists kept in the panel: the rules, and the files
|
||||||
# SelfPost writes them to.
|
# SelfPost writes them to.
|
||||||
- ./antispam/rspamd/multimap.conf:/etc/rspamd/local.d/multimap.conf:ro
|
- ./antispam/rspamd/multimap.conf:/etc/rspamd/local.d/multimap.conf:ro
|
||||||
|
# The proxy (the milter) on the compose network, nothing else.
|
||||||
|
- ./antispam/rspamd/worker-proxy.inc:/etc/rspamd/local.d/worker-proxy.inc:ro
|
||||||
- ./data/antispam:/etc/rspamd/selfpost:ro
|
- ./data/antispam:/etc/rspamd/selfpost:ro
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# rspamd removes headers of these names that a sender wrote himself before it
|
# rspamd removes headers of these names that a sender wrote himself before it
|
||||||
# adds its own - for every client, because of the two lines below. Without
|
# adds its own - for every client, because of the two lines below. Without
|
||||||
# them it would do neither for a client it counts as local or one that
|
# them it would do neither for a client it counts as local or one that
|
||||||
# authenticated: rspamd 3.11.1, src/plugins/lua/milter_headers.lua,
|
# authenticated: rspamd 4.2.2 (as 3.11.1), src/plugins/lua/milter_headers.lua,
|
||||||
# skip_wanted() - skip_local and skip_authenticated are on by default, and
|
# skip_wanted() - skip_local and skip_authenticated are on by default, and
|
||||||
# "local" is loopback, a unix socket, or an address in local_addrs, the
|
# "local" is loopback, a unix socket, or an address in local_addrs, the
|
||||||
# private ranges (src/lua/lua_ip.c, lua_ip_is_local(); conf/options.inc). The
|
# private ranges (src/lua/lua_ip.c, lua_ip_is_local(); conf/options.inc). The
|
||||||
@@ -20,7 +20,9 @@
|
|||||||
# message carrying its own X-Spamd-Result and X-Rspamd-Action): without the
|
# message carrying its own X-Spamd-Result and X-Rspamd-Action): without the
|
||||||
# two lines, from 10.0.0.5 or as an authenticated user, the milter reply
|
# two lines, from 10.0.0.5 or as an authenticated user, the milter reply
|
||||||
# removed neither header, added neither, and added the signature; with
|
# removed neither header, added neither, and added the signature; with
|
||||||
# them, both removed and rspamd's own added, as from a public address.
|
# them, both removed and rspamd's own added, as from a public address. The
|
||||||
|
# same again on 2026-10-17 in rspamd/rspamd:4.2.2-0 (from 10.0.0.5), and the
|
||||||
|
# signature equal to HMAC-SHA256 of the queue id computed with openssl.
|
||||||
#
|
#
|
||||||
# Without this file the journal still lists accepted mail, with no verdict
|
# Without this file the journal still lists accepted mail, with no verdict
|
||||||
# next to it.
|
# next to it.
|
||||||
@@ -34,7 +36,7 @@
|
|||||||
# signature travels on with the mail and is of no use to whoever sees it: it
|
# signature travels on with the mail and is of no use to whoever sees it: it
|
||||||
# fits that one queue id.
|
# fits that one queue id.
|
||||||
#
|
#
|
||||||
# rspamd 3.11.1, src/plugins/lua/milter_headers.lua: a custom routine is called
|
# rspamd 4.2.2 (as 3.11.1), src/plugins/lua/milter_headers.lua: a custom routine is called
|
||||||
# as function(task, common_meta) and returns error, headers to add, headers to
|
# as function(task, common_meta) and returns error, headers to add, headers to
|
||||||
# remove, metadata; a remove value of 0 removes every header of that name
|
# remove, metadata; a remove value of 0 removes every header of that name
|
||||||
# (docs.rspamd.com/modules/milter_headers, "Custom routines").
|
# (docs.rspamd.com/modules/milter_headers, "Custom routines").
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# The allow and deny lists kept in the panel (Inbound > Filter lists). SelfPost
|
# The allow and deny lists kept in the panel (Inbound > Filter lists). SelfPost
|
||||||
# writes them as six files under data/antispam/, which the fragment mounts at
|
# writes them as six files under data/antispam/, which the fragment mounts at
|
||||||
# /etc/rspamd/selfpost; rspamd rereads a file when it changes, no restart.
|
# /etc/rspamd/selfpost; rspamd rereads a file when it changes, no restart.
|
||||||
# (rspamd 3.11.1, src/libserver/maps/map.c: a file map is watched with libev's
|
# (rspamd 4.2.2 (as 3.11.1), src/libserver/maps/map.c: a file map is watched with libev's
|
||||||
# ev_stat - inotify where the kernel has it, else a stat poll every
|
# ev_stat - inotify where the kernel has it, else a stat poll every
|
||||||
# map_watch_interval x map_file_watch_multiplier, 5min x 0.1, conf/options.inc;
|
# map_watch_interval x map_file_watch_multiplier, 5min x 0.1, conf/options.inc;
|
||||||
# docs.rspamd.com/configuration/maps asks for an atomic rename, which
|
# docs.rspamd.com/configuration/maps asks for an atomic rename, which
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# allowed one is not accepted outright - rspamd would then let whichever of
|
# allowed one is not accepted outright - rspamd would then let whichever of
|
||||||
# the two rules ran first decide - but given a weight no spam score
|
# the two rules ran first decide - but given a weight no spam score
|
||||||
# outweighs, which a refusal by the deny list still overrides. (rspamd
|
# outweighs, which a refusal by the deny list still overrides. (rspamd
|
||||||
# 3.11.1, src/plugins/lua/multimap.lua: every rule is registered as a
|
# 4.2.2 (as 3.11.1), src/plugins/lua/multimap.lua: every rule is registered as a
|
||||||
# "normal" symbol, one with an action with the "passthrough" augmentation;
|
# "normal" symbol, one with an action with the "passthrough" augmentation;
|
||||||
# no "prefilter" setting is read. Measured with this file in a throwaway
|
# no "prefilter" setting is read. Measured with this file in a throwaway
|
||||||
# rspamd/rspamd:3.11.1-3, 2026-10-15: every mix of an allow and a deny entry
|
# rspamd/rspamd:3.11.1-3, 2026-10-15: every mix of an allow and a deny entry
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
# bounce whose From: domain is on the deny list was refused by default and
|
# bounce whose From: domain is on the deny list was refused by default and
|
||||||
# is not with this setting; a sender on the list is refused either way.
|
# is not with this setting; a sender on the list is refused either way.
|
||||||
#
|
#
|
||||||
# A domain entry is that domain, not its subdomains. (rspamd 3.11.1,
|
# A domain entry is that domain, not its subdomains. (rspamd 4.2.2 (as 3.11.1),
|
||||||
# src/libserver/maps/map_helpers.c: a hash map is looked up by the whole key,
|
# src/libserver/maps/map_helpers.c: a hash map is looked up by the whole key,
|
||||||
# case-insensitively - rspamd_icase_hash, rspamd_lc_cmp; "email:domain" gives
|
# case-insensitively - rspamd_icase_hash, rspamd_lc_cmp; "email:domain" gives
|
||||||
# the address's domain part, src/plugins/lua/multimap.lua, apply_addr_filter().)
|
# the address's domain part, src/plugins/lua/multimap.lua, apply_addr_filter().)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Mounted into the sidecar as /etc/rspamd/local.d/worker-proxy.inc.
|
||||||
|
#
|
||||||
|
# The milter - the proxy worker - is the one listener SelfPost needs on the
|
||||||
|
# compose network. The fragment sets RSPAMD_LOCAL_ADDR=localhost, which keeps
|
||||||
|
# every worker on the container's loopback: the normal worker (11333), whose
|
||||||
|
# scan accepts a client-named file or shared-memory input, and the controller
|
||||||
|
# (11334), whose default password is the well-known "q1" (rspamd 4.2.2,
|
||||||
|
# conf/rspamd.conf: each worker's bind_socket is the env variable LOCAL_ADDR,
|
||||||
|
# localhost by default, and its port; conf/worker-controller.inc: password =
|
||||||
|
# "q1"; the rspamd-docker v4.2.2+0 Dockerfile sets RSPAMD_LOCAL_ADDR=* for all
|
||||||
|
# three). This line puts the proxy alone back on every address: a local.d
|
||||||
|
# scalar at priority 1 replaces the default one (contrib/libucl/ucl_parser.c,
|
||||||
|
# UCL_DUPLICATE_MERGE). The proxy still reaches the normal worker on
|
||||||
|
# localhost, as before (security audit 2026-10-17, S4).
|
||||||
|
#
|
||||||
|
# rspamd expands its template syntax in these files, comments included:
|
||||||
|
# quote none of it here.
|
||||||
|
bind_socket = "*:11332";
|
||||||
@@ -130,7 +130,7 @@ smtp/inet` removes Debian's stock smtpd). Outbound delivery still uses the
|
|||||||
rspamd removes a sender's own copies of the three headers and writes and
|
rspamd removes a sender's own copies of the three headers and writes and
|
||||||
signs its own for every message it lets through: `skip_local` and
|
signs its own for every message it lets through: `skip_local` and
|
||||||
`skip_authenticated` are off in that file, so this holds whatever the
|
`skip_authenticated` are off in that file, so this holds whatever the
|
||||||
sender's address (rspamd 3.11.1, `src/plugins/lua/milter_headers.lua`,
|
sender's address (rspamd 4.2.2 (as 3.11.1), `src/plugins/lua/milter_headers.lua`,
|
||||||
`skip_wanted()`).
|
`skip_wanted()`).
|
||||||
With the filter down - it is attached fail-open - a sender's own verdict
|
With the filter down - it is attached fail-open - a sender's own verdict
|
||||||
headers carry no signature and the row is that of mail nobody judged.
|
headers carry no signature and the row is that of mail nobody judged.
|
||||||
@@ -171,7 +171,7 @@ smtp/inet` removes Debian's stock smtpd). Outbound delivery still uses the
|
|||||||
allowed IP with a denied address were all `reject` (assumptions
|
allowed IP with a denied address were all `reject` (assumptions
|
||||||
D-arch:121). A sender entry is matched against
|
D-arch:121). A sender entry is matched against
|
||||||
the envelope sender only (`extract_from = "smtp"`; by default rspamd takes
|
the envelope sender only (`extract_from = "smtp"`; by default rspamd takes
|
||||||
a bounce's `From:` header instead - rspamd 3.11.1,
|
a bounce's `From:` header instead - rspamd 4.2.2 (as 3.11.1),
|
||||||
`src/plugins/lua/multimap.lua`, the `from` callback).
|
`src/plugins/lua/multimap.lua`, the `from` callback).
|
||||||
How often an entry decided a message is not stored: `Lists.Usage` reads it
|
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
|
off the journal on each view of Filter lists - the rows a list decided (the
|
||||||
|
|||||||
@@ -1300,6 +1300,13 @@ rereads the list files by itself and reads the key anew for every message. A
|
|||||||
restart is needed only for a change of its own configuration - the files of
|
restart is needed only for a change of its own configuration - the files of
|
||||||
`antispam/rspamd/`.
|
`antispam/rspamd/`.
|
||||||
|
|
||||||
|
The third file there, [worker-proxy.inc](../deploy/antispam/rspamd/worker-proxy.inc),
|
||||||
|
keeps rspamd's scanner and its controller - whose default password is public -
|
||||||
|
on the sidecar's own loopback and puts only the milter on the compose network;
|
||||||
|
the fragment runs rspamd with no Linux capability. Upgrading SelfPost to
|
||||||
|
`2.14.4` brings rspamd 4.2.2 with it: copy `antispam/` again, then
|
||||||
|
`docker compose ... up -d` recreates the sidecar.
|
||||||
|
|
||||||
The same directory holds
|
The same directory holds
|
||||||
[multimap.conf](../deploy/antispam/rspamd/multimap.conf), the rules for the
|
[multimap.conf](../deploy/antispam/rspamd/multimap.conf), the rules for the
|
||||||
**allow and deny lists** kept in the panel. SelfPost does not apply the lists
|
**allow and deny lists** kept in the panel. SelfPost does not apply the lists
|
||||||
|
|||||||
@@ -817,10 +817,20 @@ the argument.
|
|||||||
**Sonnet**, 1.5 h. *Done 2026-10-17:*
|
**Sonnet**, 1.5 h. *Done 2026-10-17:*
|
||||||
TestImportKeyRefusesAnOversizedKey and the import cases (a key under
|
TestImportKeyRefusesAnOversizedKey and the import cases (a key under
|
||||||
1024 bits the parser already refuses).
|
1024 bits the parser already refuses).
|
||||||
- [ ] **S4.** rspamd pin → `4.2.x` after reading `milter_headers`/`multimap`
|
- [x] **S4.** rspamd pin → `4.2.x` after reading `milter_headers`/`multimap`
|
||||||
against the 4.x docs and one e2e run (`inbound_upstream_test`,
|
against the 4.x docs and one e2e run (`inbound_upstream_test`,
|
||||||
`antispam_test`); `cap_drop: [ALL]`, `no-new-privileges`, `pids_limit` on
|
`antispam_test`); `cap_drop: [ALL]`, `no-new-privileges`, `pids_limit` on
|
||||||
the sidecar. — **Opus**, 1.5 h.
|
the sidecar. — **Opus**, 1.5 h. *Done 2026-10-17:* rspamd
|
||||||
|
4.2.2 and 3.11.1 read side by side (the functions SelfPost relies on are
|
||||||
|
unchanged); `rspamd/rspamd:4.2.2-0` with `cap_drop: [ALL]`,
|
||||||
|
`no-new-privileges`, and - recommended by that reading - the normal
|
||||||
|
worker and the controller (default password "q1") kept on loopback, the
|
||||||
|
proxy alone on the network (`worker-proxy.inc`, `RSPAMD_LOCAL_ADDR`). The
|
||||||
|
e2e has no rspamd, so a throwaway container on the test server with our
|
||||||
|
three files checked it: the listeners, a sender's own verdict headers
|
||||||
|
removed from a private address, rspamd's added, the signature equal to
|
||||||
|
openssl's HMAC, the deny list's 554 message, healthy. The live sidecar at
|
||||||
|
the rollout.
|
||||||
|
|
||||||
### Stage 5 — supply chain and currency (~2.5 h)
|
### Stage 5 — supply chain and currency (~2.5 h)
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type ListStore interface {
|
|||||||
// a file when it changes. Which list wins when both match is the filter's
|
// a file when it changes. Which list wins when both match is the filter's
|
||||||
// configuration: deny.
|
// configuration: deny.
|
||||||
//
|
//
|
||||||
// rspamd 3.11.1, src/libserver/maps/map.c: a file map is watched with libev's
|
// rspamd 4.2.2 (as 3.11.1), src/libserver/maps/map.c: a file map is watched with libev's
|
||||||
// ev_stat (inotify, or a stat poll every map_watch_interval x
|
// ev_stat (inotify, or a stat poll every map_watch_interval x
|
||||||
// map_file_watch_multiplier = 30 s, conf/options.inc), so a change takes
|
// map_file_watch_multiplier = 30 s, conf/options.inc), so a change takes
|
||||||
// effect within that, not at once; docs.rspamd.com/configuration/maps asks
|
// effect within that, not at once; docs.rspamd.com/configuration/maps asks
|
||||||
@@ -164,7 +164,7 @@ func (l *Lists) sync() error {
|
|||||||
// Values were validated on the way in; this is the writer's own guard
|
// Values were validated on the way in; this is the writer's own guard
|
||||||
// against a row that got into the table some other way. In a map file a
|
// against a row that got into the table some other way. In a map file a
|
||||||
// newline ends the entry, whitespace starts its value and # a comment
|
// newline ends the entry, whitespace starts its value and # a comment
|
||||||
// (rspamd 3.11.1, src/libserver/maps/map_helpers.c,
|
// (rspamd 4.2.2 (as 3.11.1), src/libserver/maps/map_helpers.c,
|
||||||
// rspamd_parse_kv_list()). An IP map takes bare v4 and v6 addresses and
|
// rspamd_parse_kv_list()). An IP map takes bare v4 and v6 addresses and
|
||||||
// CIDR networks (docs.rspamd.com/configuration/maps, IP maps).
|
// CIDR networks (docs.rspamd.com/configuration/maps, IP maps).
|
||||||
if err := configsafe.Token("filter list entry", e.Value, " \t\r\n#"); err != nil {
|
if err := configsafe.Token("filter list entry", e.Value, " \t\r\n#"); err != nil {
|
||||||
@@ -174,7 +174,7 @@ func (l *Lists) sync() error {
|
|||||||
}
|
}
|
||||||
// The filter runs under another user in another container: the directory
|
// The filter runs under another user in another container: the directory
|
||||||
// and the files are readable by anyone. They hold addresses, not secrets.
|
// and the files are readable by anyone. They hold addresses, not secrets.
|
||||||
// (The rspamd image runs as uid 11333: rspamd-docker v3.11.1+3,
|
// (The rspamd image runs as uid 11333: rspamd-docker v4.2.2+0 (as v3.11.1+3),
|
||||||
// Dockerfile, USER 11333:11333.)
|
// Dockerfile, USER 11333:11333.)
|
||||||
if err := os.MkdirAll(l.dir, 0o755); err != nil {
|
if err := os.MkdirAll(l.dir, 0o755); err != nil {
|
||||||
return fmt.Errorf("create %s: %w", l.dir, err)
|
return fmt.Errorf("create %s: %w", l.dir, err)
|
||||||
@@ -309,7 +309,7 @@ func normalizeNote(raw string) (string, error) {
|
|||||||
// rejected row. An allow rule adds a symbol named after the kind of entry,
|
// rejected row. An allow rule adds a symbol named after the kind of entry,
|
||||||
// which the journal milter keeps in the verdict of an accepted row.
|
// which the journal milter keeps in the verdict of an accepted row.
|
||||||
//
|
//
|
||||||
// rspamd 3.11.1: the deny message reaches Postfix as the milter reply "554
|
// rspamd 4.2.2 (as 3.11.1): the deny message reaches Postfix as the milter reply "554
|
||||||
// 5.7.1 <message>" (src/libserver/milter.c, rspamd_milter_send_task_results());
|
// 5.7.1 <message>" (src/libserver/milter.c, rspamd_milter_send_task_results());
|
||||||
// src/plugins/lua/milter_headers.lua, routine x-spamd-result, writes each
|
// src/plugins/lua/milter_headers.lua, routine x-spamd-result, writes each
|
||||||
// symbol as "%s(%.2f)[%s]" - name, score, options.
|
// symbol as "%s(%.2f)[%s]" - name, score, options.
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ func (s *inboundSession) RcptTo(rcpt string, m *milter.Modifier) (milter.Respons
|
|||||||
// milter8_message(), reads it with rec_get(), which follows the pointer
|
// milter8_message(), reads it with rec_get(), which follows the pointer
|
||||||
// records src/cleanup/cleanup_milter.c, cleanup_add_header(), leaves after
|
// records src/cleanup/cleanup_milter.c, cleanup_add_header(), leaves after
|
||||||
// the last header for a header the milter before added.
|
// the last header for a header the milter before added.
|
||||||
// rspamd 3.11.0, src/plugins/lua/milter_headers.lua, routines x-spamd-result
|
// rspamd 4.2.2 (as 3.11.0), src/plugins/lua/milter_headers.lua, routines x-spamd-result
|
||||||
// and x-rspamd-action: remove = 0, every instance
|
// and x-rspamd-action: remove = 0, every instance
|
||||||
// (src/libserver/milter.c, rspamd_milter_remove_header_safe()), and the new
|
// (src/libserver/milter.c, rspamd_milter_remove_header_safe()), and the new
|
||||||
// one added at the end (order -1, ADDHEADER) - when skip_wanted() does not
|
// one added at the end (order -1, ADDHEADER) - when skip_wanted() does not
|
||||||
@@ -146,7 +146,8 @@ var signatureHeader = textproto.CanonicalMIMEHeaderKey(SignatureHeader)
|
|||||||
//
|
//
|
||||||
// Measured, not read: what rspamd 3.11 writes for a key and a queue id was
|
// Measured, not read: what rspamd 3.11 writes for a key and a queue id was
|
||||||
// taken in a throwaway container on 2026-10-12
|
// taken in a throwaway container on 2026-10-12
|
||||||
// (TestFilterSignatureIsWhatTheFilterWrites).
|
// (TestFilterSignatureIsWhatTheFilterWrites); rspamd 4.2.2-0 wrote the
|
||||||
|
// HMAC-SHA256 openssl computes, on 2026-10-17 (security audit, S4).
|
||||||
func FilterSignature(key, queueID string) string {
|
func FilterSignature(key, queueID string) string {
|
||||||
mac := hmac.New(sha256.New, []byte(key))
|
mac := hmac.New(sha256.New, []byte(key))
|
||||||
mac.Write([]byte(queueID))
|
mac.Write([]byte(queueID))
|
||||||
@@ -253,7 +254,7 @@ const (
|
|||||||
// (deploy/antispam/rspamd/multimap.conf): SELFPOST_ALLOW_IP, _FROM, _DOMAIN.
|
// (deploy/antispam/rspamd/multimap.conf): SELFPOST_ALLOW_IP, _FROM, _DOMAIN.
|
||||||
const ListSymbolPrefix = "SELFPOST_"
|
const ListSymbolPrefix = "SELFPOST_"
|
||||||
|
|
||||||
// rspamd 3.11.0, src/plugins/lua/milter_headers.lua, routine x-spamd-result:
|
// rspamd 4.2.2 (as 3.11.0), src/plugins/lua/milter_headers.lua, routine x-spamd-result:
|
||||||
// string.format('default: %s [%.2f / %.2f]', ...), then one
|
// string.format('default: %s [%.2f / %.2f]', ...), then one
|
||||||
// '%s(%.2f)[%s]' per symbol, joined by '; '.
|
// '%s(%.2f)[%s]' per symbol, joined by '; '.
|
||||||
var (
|
var (
|
||||||
|
|||||||
Reference in New Issue
Block a user