MAS-WP-0005-T11: OpenDKIM+OpenDMARC milters; forged A-R stripped by header_checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 252470@bnt-lap001
Assistant-Session: ad628c8f-4be2-4979-94c9-76c7ed6f8241
This commit is contained in:
tegwick 2026-10-18 22:38:43 +02:00
parent 516657bf6a
commit ab3346d187
4 changed files with 69 additions and 5 deletions

View file

@ -2,7 +2,7 @@
# Build from the repo root: docker build -f deploy/railiance/Dockerfile .
FROM debian:bookworm-slim
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
postfix libsasl2-modules python3 python3-jsonschema ssl-cert ca-certificates && rm -rf /var/lib/apt/lists/*
postfix libsasl2-modules opendkim opendmarc publicsuffix python3 python3-jsonschema ssl-cert ca-certificates && rm -rf /var/lib/apt/lists/*
RUN useradd -r -s /usr/sbin/nologin mas
COPY mas_core /app/mas_core
COPY mas_gateway /app/mas_gateway

View file

@ -67,6 +67,47 @@ if [ -n "$MAS_RELAY" ] && [ -s /etc/mas-relay/username ]; then
else
postconf -e "default_transport=error:5.7.1 outbound mail disabled"
fi
# Sender authentication (spec 14 item 10). The MTA does SPF/DKIM/DMARC, since
# they need DNS; the gateway only reads Authentication-Results carrying our
# authserv-id. Postfix header_checks drop incoming A-R headers claiming that
# id; header_checks do not see milter-added headers, so only the milters'
# own results survive and a sender cannot forge a pass. (OpenDKIM's
# RemoveARFrom did not remove them in testing.) Annotate only: never reject,
# and accept mail if a milter is down. Loopback (the ticket service's
# replies) is skipped. Both milters run in the foreground: daemonized,
# OpenDKIM died on its first connection in this container.
mkdir -p /run/opendkim /run/opendmarc
chown opendkim:opendkim /run/opendkim && chown opendmarc:opendmarc /run/opendmarc
printf '127.0.0.1\n' > /etc/opendkim-peers
cat > /etc/opendkim.conf <<CONF
Mode v
Socket inet:8891@127.0.0.1
AuthservID $DOMAIN
PeerList /etc/opendkim-peers
UserID opendkim
PidFile /run/opendkim/opendkim.pid
Syslog no
CONF
cat > /etc/opendmarc.conf <<CONF
Socket inet:8893@127.0.0.1
AuthservID $DOMAIN
TrustedAuthservIDs $DOMAIN
SPFSelfValidate true
SPFIgnoreResults true
RejectFailures false
PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat
UserID opendmarc
PidFile /run/opendmarc/opendmarc.pid
Syslog false
CONF
opendkim -f -x /etc/opendkim.conf &
opendmarc -f -c /etc/opendmarc.conf &
printf '/^Authentication-Results:[[:space:]]*%s([[:space:];]|$)/ IGNORE\n' "$DOMAIN_RE" \
> /etc/postfix/ar_strip.regexp
postconf -e "header_checks=regexp:/etc/postfix/ar_strip.regexp" "smtpd_milters=inet:127.0.0.1:8891,inet:127.0.0.1:8893" "non_smtpd_milters=" \
"milter_default_action=accept" "milter_protocol=6"
cat >> /etc/postfix/master.cf <<CF
mas unix - n n - - pipe
flags=Rq user=mas argv=/usr/local/bin/mas-gateway

View file

@ -39,9 +39,11 @@ the relay regexp) for a MAS receiving domain:
- pipe to `mas-gateway`;
- optional authenticated relay with a send cap;
- null-sender discard;
- sender authentication: OpenDKIM (verify) + OpenDMARC milters with
`RemoveARFrom` for the host's authserv-id, annotate-only (from
MAS-WP-0005 T11; spec §14 item 10).
- sender authentication: OpenDKIM (verify) + OpenDMARC milters,
annotate-only. Forged A-R headers for the host's authserv-id are
stripped with a Postfix `header_checks` IGNORE rule, because OpenDKIM's
`RemoveARFrom` did not work in testing. The milters run in the
foreground (MAS-WP-0005 T11; spec §14 item 10).
Explain each setting and say why the stock config fails.
## Probe the profile

View file

@ -357,7 +357,7 @@ and case-insensitive ids. Repeat the IONOS forward test with
```task
id: MAS-WP-0005-T11
status: progress
status: done
priority: medium
```
@ -368,6 +368,27 @@ claiming that authserv-id are removed (`RemoveARFrom`). Annotate only;
never reject on DMARC failure. Milter failure means accept
(`milter_default_action = accept`).
- 2026-10-18 done (verified in a local container; live in T13):
- Image packages: opendkim, opendmarc, publicsuffix. OpenDKIM runs in
verify mode; OpenDMARC with `SPFSelfValidate`, `RejectFailures false`
and `TrustedAuthservIDs` set to the domain.
- **Forged A-R stripping is done by Postfix, not OpenDKIM.**
`RemoveARFrom` and `RemoveARAll` did not remove forged
`Authentication-Results: mas.coulomb.social; dmarc=pass` headers. A
`header_checks` regexp IGNORE rule does, and it leaves the milters'
own headers alone, because header_checks do not apply to
milter-added headers. Verified for a forged pass, an upper-case
variant and a foreign id (foreign ids are kept; the gateway ignores
them).
- Daemonized, OpenDKIM died on its first connection ("can't read
SMFIC_OPTNEG reply"), so both milters now run with `-f` in the
background.
- Result for a spoofed `From: test@coulomb.social` sent directly:
`spf=fail`, `dmarc=fail (p=none)`. Our A-R headers sit above our
`Received:` line; anything forged sits below it.
- Note for MAS-WP-0004: use header_checks for A-R stripping and
foreground milters.
## Gateway verdict and reply gating
```task