exim smtp time spamasassin based rejection

Exim and SpamAssassin can work very well together to screen incoming email during the SMTP receive phase and reject messages without them ever having to get close to a user. Although the borderline spam cases are the most annoying and give rise to false positives, there is another class of spam which is so totally bogus I wonder why they even bother to send it although it does give credence to the concept that spamming itself is no longer profitable – bot-herding and supplying delivery services to spammers is the new market, and the I-can-get-rich-quick-too spammers are themselves being ripped off.

To do this I wanted to use two methods – a clear and generous site-wide limit that will apply in all cases, and a per-user option to have a more aggressive approach to refusals. Using a recent (4.69) version of Exim my virus and spam checking is done in the acl_smtp_data ACL, so adding this snippet will implement a site-wide refusal for all email scoring more than 25:

  deny   message = This message scored $spam_score spam points and will not be delivered.
         spam = nobody:true
         condition = ${if >{$spam_score_int}{250}{1}{0}}

Note that Exim doesn’t do floats, and SA scores have one decimal place, so the limit is multiplied by ten to keep it an integer.

Next, I want to allow a per-recipient limit, but only if the message is addressed to one person. This comes after the bulk scoring in my configure file, but could just as easily come first:

  deny   message = This message scored $spam_score spam points and will not be delivered.
         spam = nobody:true
         condition = ${if and {{={$rcpt_count}{1}} \
                      {>{$spam_score_int}{${lookup{$recipients}lsearch{/etc/exim/sa_refusal}{$value}{250}}}}} \
                      {1}{0}}

You then have a nice and simple file (called /etc/exim/sa_refusal above) with a recipient on the left and the desired limit on the right, eg:

[email protected]:     500
[email protected]:   100
[email protected]:           152

A failed lookup will return 250 just as with the site wide limit, although you may wish to change the lsearch to a wildcard lookup and have the global limit for a single recipient email in the sa_resfual file too, but that is left as an exercise for the reader.

You may also wish to apply the site-wide filter as an else clause to the per-recipient rule – it’d certainly be cleaner programmatically but I fear that the current $if line is hard enough to read as-is and I don’t want any more { or } in there.

There are two things you may wish to change: 25 is not a magic number, but one that I found to be way above any email that had been subject to a false positive in SA (I’ve got a few years worth of data, so in theory no legit mail will ever score this highly), and the status reporting via eximstats reports lots of lines rather than an aggregate of offending spam:

         1   This message scored 18.6 spam points and will not be delivered.
         1   This message scored 18.8 spam points and will not be delivered.
         1   This message scored 21.3 spam points and will not be delivered.
         1   This message scored 21.6 spam points and will not be delivered.
         1   This message scored 24.5 spam points and will not be delivered.
         1   This message scored 25.1 spam points and will not be delivered.

Just change the message= lines to not refer to the $spam_score and things will be summarised more neatly.

Comments are Disabled