Adding custom rules to Amavis

Why?

Sometimes you just need to highlight specific spamminess, and Amavis hasn’t been able to identify the spam via it’s bayesian methods.

The file to modify

Amavis has its own file that you can use to add custom SpamAssassin rules. I won’t go over all of the complexities of spamassassin rules, but instead will give a simple regex rule example.

The file itself can be found at /var/lib/amavis/.spamassassin/user_prefs

By default it looks like:

# SpamAssassin user preferences file.  See 'perldoc Mail::SpamAssassin::Conf'
# for details of what can be tweaked.
#*
#* Note: this file is not read by SpamAssassin until copied into the user
#* directory. At runtime, if a user has no preferences in their home directory
#* already, it will be copied for them, allowing them to perform personalised
#* customisation.  If you want to make changes to the site-wide defaults,
#* create a file in /etc/spamassassin or /etc/mail/spamassassin instead.
###########################################################################

# How many points before a mail is considered spam.
 required_score         5

# Whitelist and blacklist addresses are now file-glob-style patterns, so
# "friend@somewhere.com", "*@isp.com", or "*.domain.net" will all work.
# whitelist_from        someone@somewhere.com

# Add your own customised scores for some tests below.  The default scores are
# read from the installed spamassassin rules files, but you can override them
# here.  To see the list of tests and their default scores, go to
# https://spamassassin.apache.org/tests.html .
#
# score SYMBOLIC_TEST_NAME n.nn

# Speakers of Asian languages, like Chinese, Japanese and Korean, will almost
# definitely want to uncomment the following lines.  They will switch off some
# rules that detect 8-bit characters, which commonly trigger on mails using CJK
# character sets, or that assume a western-style charset is in use.
#
# score HTML_COMMENT_8BITS      0
# score UPPERCASE_25_50         0
# score UPPERCASE_50_75         0
# score UPPERCASE_75_100        0
# score OBSCURED_EMAIL          0

# Speakers of any language that uses non-English, accented characters may wish
# to uncomment the following lines.   They turn off rules that fire on
# misformatted messages generated by common mail apps in contravention of the
# email RFCs.

# score SUBJ_ILLEGAL_CHARS      0

We want to be adding our rules at the bottom of the file.

Building the custom rule

Each custom rule entry will appear in 3 parts:

  • Keyword The part of the rule we’re going to be setting
  • Rule Name We give a rule name in Screaming Snake Case (Yes, it’s a real case. Check Wikipedia for proof!) This rule name can be unique to you, or can be used to override existing SpamAssasin rule names. You need to make sure that you use the same rule name for each related entry.
  • Content The content of the particular rule setting.

Rule description

Example:

describe PRODUCT_SPAM Uses the same html content page to spam users!

We use the keyword describe to indicate that this will be a rule description.

The rule name here is going to be called PRODUCT_SPAM.

The content is just the description text! In this example, I’m trying to block a new spam campaign that advertises various products.

Score

Example:

score PRODUCT_SPAM 10 10 10 10

We use the keyword score to indicate that we are setting the spam scores for a rule.

I’m using the same rule name, so SpamAssasin knows which rule to modify the scores of.

There are four score entries in a rule’s score line, but only one of these scores is used. The score that’s used depends on how SpamAssassin has been configured. You’re 100% allowed to just put a single score, and if you do so, it’s used in all configurations of SpamAssassin/Amavis.

When there are four scores, each is applied in different circumstances. The score that’s used varies depending on whether SpamAssassin’s bayesian filter is being used and whether network tests are configured.

Bayesian filter not used Bayesian filter used
External tests not used Score #1 Score#3
External tests used Score #2 Score #4

In or example, we’ve manually set a score of 10 for all 4 options, but it could just as easily be written as core PRODUCT_SPAM 10

The match itself

Example:

body PRODUCT_SPAM /\<div class\=3D\"emailp-contentp\"\>/i

We use the keyword body to indicate that our match will be on the email’s body.

I’m using the same rule name, so SpamAssasin knows which rule to use when the match occurs.

We’re using regex to match the mail contents, as I love regex. We place whatever we want within forward slashes (/). Regex modifiers get added after that final slash, and in this case, I’ve used the case insensitive option (i).

The full custom rule

Now that we’ve identified the three sections of the rule, we’d expect to add the following to the bottom of /var/lib/amavis/.spamassassin/user_prefs:

#Product Spam (Oral B, Air Fryers, etc)
body PRODUCT_SPAM /\<div class\=3D\"emailp-contentp\"\>/i
describe PRODUCT_SPAM Uses the same html content page to spam users!
score PRODUCT_SPAM 10

Make sure you add a comment at the top of your section of rules so that you know what your rule is supposed to detect.

Implement the rule

Now, all you need to do is to reload/restart amavis!

Now when I look at my mail logs, I can see the spam tagged:

Jul 1 11:42:45 mailserver amavis[1189040]: (1189040-01) spam-tag, <spammer@example.com> -> <user@example.net>, Yes, score=8.142 tagged_above=-999 required=7 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, PRODUCT_SPAM=10, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01] autolearn=no autolearn_force=no

There’s our rule! PRODUCT_SPAM=10! This email was subsequently marked as Spam, so our rule works properly!

Related Articles