SpamAssassin local.cf DMARC configuration recognizes reject and none, but not quarantine

For SpamAssassin 3.4.2 I've added the below configuration ruleset to local.cf with the intent of providing a spam score for failed DMARC tests.

The DMARC and SPF Authentication-Results headers are created by opendmarc and the DKIM Authentication-Results header by opendkim.

The expected output is a score for all three header results added to the X-Spam-Report header.

The actual output is only for a domain that has p=reject or p=none with the appropriate score added to the X-Spam-Report header, but for p=quarantine nothing is added to the X-Spam-Report header even though the DMARC, DKIM and SPF Authentication-Results headers have been added as expected.

Added to /etc/spamassassin/local.cf:

ifplugin Mail::SpamAssassin::Plugin::AskDNS
askdns __DMARC_POLICY_NONE _dmarc._AUTHORDOMAIN_ TXT /^v=DMARC1;.*\bp=none;/
askdns __DMARC_POLICY_QUAR _dmarc._AUTHORDOMAIN_ TXT /^v=DMARC1;.*\bp=quarantine;/
askdns __DMARC_POLICY_REJECT _dmarc._AUTHORDOMAIN_ TXT /^v=DMARC1;.*\bp=reject;/

meta DMARC_REJECT !(DKIM_VALID_AU || SPF_PASS) && __DMARC_POLICY_REJECT
score DMARC_REJECT 10
meta DMARC_QUAR !(DKIM_VALID_AU || SPF_PASS) && __DMARC_POLICY_QUAR
score DMARC_QUAR 5
meta DMARC_NONE !(DKIM_VALID_AU || SPF_PASS) && __DMARC_POLICY_NONE
score DMARC_NONE 0.1
endif # Mail::SpamAssassin::Plugin::AskDNS

What is wrong with the configuration?


Solution 1:

The configuration you refer to is copied from Random Thoughts blog on DMARC / Spamassassin / Qmail. This blog post gives AskDNS as a third option if you can't use OpenDMARC for some reason. As you are already using OpenDMARC, you can directly use its Authentication-Results header in SpamAssassin. Modified from David Jones:

header DMARC_PASS Authentication-Results =~ /mail\.example\.com; dmarc=pass/
describe DMARC_PASS DMARC check passed
score DMARC_PASS -0.1

header DMARC_FAIL Authentication-Results =~ /mail\.example\.com; dmarc=fail/
describe DMARC_FAIL DMARC check failed
score DMARC_FAIL 5.0

header DMARC_NONE Authentication-Results =~ /mail\.example\.com; dmarc=none/
describe DMARC_NONE DMARC check neutral
score DMARC_NONE 0.1

If you have implemented OpenDMARC to connection-stage reject the messages that fail DMARC checks with p=reject, your SpamAssassin will never see any messages falling to the fourth category, but here it is, just for completeness:

header DMARC_FAIL_REJECT Authentication-Results =~ /mail\.example\.com; dmarc=fail \(p=reject/
describe DMARC_FAIL_REJECT DMARC check failed and the sending domains says to reject this message
score DMARC_FAIL_REJECT 10.0

Because the Authentication-Results headers can also be forged, I wouldn't give high negative score based on them alone. Therefore, the most useful rule here is the DMARC_FAIL, firing on p=quarantine. In my opinion, that could also give score more than the 5.0 in this example, as the decision is already made by the sending domain.

If the above example configuration is not suitable, here is an additional configuration and scoring option:

header DMARC_PASS Authentication-Results =~ /mail\.example\.com; dmarc=pass/
describe DMARC_PASS DMARC check passed
score DMARC_PASS -0.1

header DMARC_NONE Authentication-Results =~ /mail\.example\.com; dmarc=none/
describe DMARC_NONE DMARC record not found
score DMARC_NONE 0.1

header DMARC_FAIL_NONE Authentication-Results =~ /mail\.example\.com; dmarc=fail \(p=none/
describe DMARC_FAIL_NONE DMARC check failed (p=none)
score DMARC_FAIL_NONE 2.0

header DMARC_FAIL_QUARANTINE Authentication-Results =~ /mail\.example\.com; dmarc=fail \(p=quarantine/
describe DMARC_FAIL_QUARANTINE DMARC check failed (p=quarantine)
score DMARC_FAIL_QUARANTINE 5.0

header DMARC_FAIL_REJECT Authentication-Results =~ /mail\.example\.com; dmarc=fail \(p=reject/
describe DMARC_FAIL_REJECT DMARC check failed (p=reject)
score DMARC_FAIL_REJECT 10.0