Step 5/12 of docs/plans/inbound-antispam-panel.md.
Store: AddSpamListEntry, ListSpamListEntries, DeleteSpamListEntry.
internal/inbound/lists.go validates and normalises an entry - sender
address, sender domain, client address or network (canonical text, an
IPv4-mapped address as IPv4, /0 refused) - and writes the lists as six
map files under /data/antispam (ANTISPAM_DIR), each atomically, 0644 in
a 0755 directory: the filter is another user in another container. All
six are written on every change and on every start with the relay on,
empty ones included. An entry whose maps could not be written is taken
back out of the table. The writer refuses a stored value with
whitespace, a newline or # in it.
The plan's allow.map / deny.map became one file per list and entry
type: to rspamd an IP map and a sender map are different map types.
deploy/antispam/rspamd/multimap.conf holds the rules; the fragment
mounts it and ./data/antispam into the sidecar. Deny is a prefilter
reject, allow a symbol weighing -100. Tried in a throwaway rspamd 3.11
container on the test server:
- with allow as a prefilter accept, an address on both lists was
accepted and an IP on both was refused - the rule that ran first
decided; with the weight, deny won in all five mixed cases;
- IPv4, IPv6, address (any case), domain (not its subdomains) match;
- a rewritten file is reread in about ten seconds, no restart;
- files that appear after rspamd started are loaded the same way.
No page edits the lists yet (step 6).
Checked on the test server, uncommitted tree: go vet ./... ok,
go test ./... ok (22 packages), make e2e ok - with the relay on
/data/antispam is 755 and holds the six maps, 644, empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
37 lines
1.6 KiB
YAML
37 lines
1.6 KiB
YAML
# Optional anti-spam sidecar for inbound relay (docs/plans/inbound-relay.md).
|
|
#
|
|
# SelfPost does not ship or start an anti-spam engine. This fragment brings up
|
|
# rspamd next to SelfPost and points the inbound smtpd milter at it. Merge it
|
|
# only when you want filtering on port 25:
|
|
#
|
|
# docker compose -f docker-compose.yml -f antispam/docker-compose.antispam.yml up -d
|
|
#
|
|
# 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.
|
|
#
|
|
# Pin the rspamd tag the same way the SelfPost image is pinned; bump it
|
|
# deliberately. Default milter action is fail-open (accept) so a down sidecar
|
|
# does not block backup-MX. Set INBOUND_ANTISPAM_MILTER_ACTION=tempfail to
|
|
# defer instead.
|
|
|
|
services:
|
|
selfpost:
|
|
environment:
|
|
INBOUND_RELAY_ENABLE: "${INBOUND_RELAY_ENABLE:-true}"
|
|
INBOUND_ANTISPAM_MILTER: "${INBOUND_ANTISPAM_MILTER:-inet:antispam:11332}"
|
|
INBOUND_ANTISPAM_MILTER_ACTION: "${INBOUND_ANTISPAM_MILTER_ACTION:-accept}"
|
|
depends_on:
|
|
- antispam
|
|
|
|
antispam:
|
|
image: rspamd/rspamd:3.11
|
|
restart: unless-stopped
|
|
# No host ports: only SelfPost talks to the milter on the compose network.
|
|
volumes:
|
|
# The verdict headers SelfPost's inbound journal reads.
|
|
- ./antispam/rspamd/milter_headers.conf:/etc/rspamd/local.d/milter_headers.conf:ro
|
|
# The allow and deny lists kept in the panel: the rules, and the files
|
|
# SelfPost writes them to.
|
|
- ./antispam/rspamd/multimap.conf:/etc/rspamd/local.d/multimap.conf:ro
|
|
- ./data/antispam:/etc/rspamd/selfpost:ro
|