Setting DMARC filter in Spamassassin

September 2, 2020 Roberto Puzzanghera2 comments

  • Thanks to Iulian for the hint. This is a link to his page
  • Take a look here for further DMARC solutions for qmail
  • MXtoolbox: verifying your DMARC record

You can use Spamassassin to apply a DMARC filter by means of the AskDNS plugin. Just add the following to your 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

This means that a DMARC reject (p=reject in the DNS record) will turn into a +10 spam score, DMARC quarantine (p=quarantine) into a +5 spam score and a p=none into a +0.1 spam score.

This is how you may want to set your own DMARC record into your bind zone:

_dmarc.yourdomain.tld. IN TXT "v=DMARC1;p=reject;sp=none;pct=100;rua=mailto:postmaster@yourdomain.tld"

Of course this requires that you already have both SPF and DKIM working as explained before.

If you decide to set a similar DNS record in your DMZ view, it is important that you have set your allowed localnets in spamassassin, for example:

internal_networks 10.0.0/24

otherwise you will probably ban your system or web application mail messages in case you don't sign them.

Comments

Invalid syntax

meta DMARC_REJECT !(DKIM_VALID_AU || SPF_PASS) && __DMARC_POLICY_REJECT

this check is invalid, this -OR- logic in the () reads:

if not (DKIM_VALID_AU -OR- SPF_PASS) AND theres a policy for the domain then reject which means an email with assuming a policy exists (1):

!DKIM_VALID_AU and !SPF_PASS == if !(0 || 0) && 1 == 1 && 1 == 1 == ACTION (GOOD)
DKIM_VALID_AU and !SPF_PASS == if !(1 || 0) && 1 == 0 && 1 == 0 == NO ACTION (BAD)
!DKIM_VALID_AU and SPF_PASS == if !(0 || 1) && 1 == 0 && 1 == 0 == NO ACTION (BAD)
DKIM_VALID_AU and SPF_PASS == if !(1 || 1) && 1 == 0 && 1 == 0 == NO ACTION (GOOD)

if no policy exists (0) we are always NO ACTION (GOOD)

Basically its not failing out and runing the domain's policy for a failure of individual parts.

Test with a quick perl script (play with the 3 variables up top):

#!/usr/bin/perl
$DKIM_VALID_AU = 1;
$SPF_PASS = 1;
$POLICY = 1;

$PRES = int( !($DKIM_VALID_AU || $SPF_PASS) );
$RES = int ( !($DKIM_VALID_AU || $SPF_PASS) && $POLICY );

print "DKIM_VALID_AU=$DKIM_VALID_AU SPF_PASS=$SPF_PASS PAREN RESULT=$PRES RESULT=$RES AKA: ";

if ($RES) {
print "ACTION\n";
} else {
print "NO ACTION\n";
}
POLICY=1 DKIM_VALID_AU=0 SPF_PASS=0 PAREN RESULT=1 RESULT=1 AKA: ACTION
POLICY=1 DKIM_VALID_AU=0 SPF_PASS=1 PAREN RESULT=0 RESULT=0 AKA: NO ACTION
POLICY=1 DKIM_VALID_AU=1 SPF_PASS=0 PAREN RESULT=0 RESULT=0 AKA: NO ACTION
POLICY=1 DKIM_VALID_AU=1 SPF_PASS=1 PAREN RESULT=0 RESULT=0 AKA: NO ACTION

The following will actually work (tested for all cases):

meta DMARC_REJECT !(DKIM_VALID_AU && SPF_PASS) && __DMARC_POLICY_REJECT

if not (DKIM_VALID_AU -AND- SPF_PASS) AND theres a policy for the domain then REJECT

Validate by changing that perl script logic to match:

#!/usr/bin/perl
$DKIM_VALID_AU = 1;
$SPF_PASS = 1;
$POLICY = 1;

$PRES = int( !($DKIM_VALID_AU && $SPF_PASS) );
$RES = int ( !($DKIM_VALID_AU && $SPF_PASS) && $POLICY );

print "DKIM_VALID_AU=$DKIM_VALID_AU SPF_PASS=$SPF_PASS PAREN RESULT=$PRES RESULT=$RES AKA: ";

if ($RES) {
print "ACTION\n";
} else {
print "NO ACTION\n";
}
POLICY=1 DKIM_VALID_AU=1 SPF_PASS=1 PAREN RESULT=0 RESULT=0 AKA: NO ACTION
POLICY=1 DKIM_VALID_AU=0 SPF_PASS=1 PAREN RESULT=1 RESULT=1 AKA: ACTION
POLICY=1 DKIM_VALID_AU=1 SPF_PASS=0 PAREN RESULT=1 RESULT=1 AKA: ACTION
POLICY=1 DKIM_VALID_AU=0 SPF_PASS=0 PAREN RESULT=1 RESULT=1 AKA: ACTION

POLICY=0 DKIM_VALID_AU=1 SPF_PASS=1 PAREN RESULT=0 RESULT=0 AKA: NO ACTION
POLICY=0 DKIM_VALID_AU=0 SPF_PASS=0 PAREN RESULT=1 RESULT=0 AKA: NO ACTION

If any of the conditions are 0 (fail) then the policy is enforced. If everything checks out its ignored. No policy means no action.

Thanks so much for the information about how to set the DMARC check up via AskDNS. Hopefully this correction helps make this method even better.

Reply | Permalink

Invalid syntax

Thank you, fixed

Reply | Permalink