email

Ahoy !
I realized that forgot about half of the Rspamd & Dovecot in my last article, so here is an attempt to correct this misstep.

In this article we'll configure Dovecot's Sieve to understand how Rspamd assign spam score and headers to emails. Then, we'll implement policies to reject or filter spam from Sieve. If needed, we'll use the spamtest and spamtestplus Sieve extensions.

This is the second part of the Dovecot & Rspamd integration setup, talking about mail filtering using Dovecot spamtest. If you missed the first part about Rspamd autolearn integration with Dovecot antispam plugin, you'll find it here.

Dovecot Sieve configuration

First we will configure Dovecot's Sieve to parse Rspamd X‑Spamd‑Result header, so later we may filter from Sieve based on the email's spam "score".

conf.d/90-sieve.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
# Path to a script file or a directory containing script files that need to be
# executed before the user's script. If the path points to a directory, all
# the Sieve scripts contained therein (with the proper .sieve extension) are
# executed. The order of execution within a directory is determined by the
# file names, using a normal 8bit per-character comparison. Multiple script
# file or directory paths can be specified by appending an increasing number.
sieve_before = /usr/local/etc/dovecot/sieve/before.d
#sieve_before2 =
#sieve_before3 = (etc...)
...
# Which Sieve language extensions are available to users. By default, all
# supported extensions are available, except for deprecated extensions or
# those that are still under development. Some system administrators may want
# to disable certain Sieve extensions or enable those that are not available
# by default. This setting can use '+' and '-' to specify differences relative
# to the default. For example `sieve_extensions = +imapflags' will enable the
# deprecated imapflags extension in addition to all extensions were already
# enabled by default.
#sieve_extensions = +notify +imapflags
sieve_extensions = +spamtest +spamtestplus

sieve_spamtest_status_type = score
# Rspamd output looks like: X-Spamd-Result: default: False [12.00 / 15.00]
sieve_spamtest_status_header = X-Spamd-Result: default: [[:alnum:]]+ \[(-?[[:digit:]]+\.[[:digit:]]+) / -?[[:digit:]]+\.[[:digit:]]+\]
sieve_spamtest_max_header    = X-Spamd-Result: default: [[:alnum:]]+ \[-?[[:digit:]]+\.[[:digit:]]+ / (-?[[:digit:]]+\.[[:digit:]]+)\]
...
  1. Define a sieve_before directory (we'll get back to this later).
  2. Load both the spamtest and spamtestplus Sieve extensions.
  3. We set the sieve_spamtest_status_type to score, meaning that we'll have a numeric score.
  4. Then, we infer the score from Rspamd's X‑Spamd‑Result header: sieve_spamtest_status_header is a regexp that should match the email's spam score…
  5. … and sieve_spamtest_max_header a regexp matching what could be the maximum spam score for this email. We will then be able from Sieve to express our filters in percent of spam probability if we want to.

For more infos on Dovecot's spamtest extensions, see the official documentation.

When using Rmilter You need to have extended_spam_headers = yes in the spamd section of rmilter.conf (otherwise you won't have the X‑Spamd‑Result header).

before.d Sieve filters

As configured before, any Sieve script in /usr/local/etc/dovecot/sieve/before.d will be run by Dovecot. This let us write some default Sieve code that will be run on every incoming emails, very handy.

You need to be sure to manually pre-compile the scripts specified by sieve_before after any changes using the sievec tool:

# sudo sievec /usr/local/etc/dovecot/sieve/before.d

a simple example

/usr/local/etc/dovecot/sieve/before.d/no-spam.sieve

1
2
3
4
5
6
7
8
9
10
require "fileinto";
require "imap4flags";

if header :is "X-Spam" "Yes" {
    fileinto "Junk";
    setflag "\\seen";
    stop;
}

/* Other messages get filed into Inbox or to user's scripts */

Here we rely on the X-Spam header, which is the result of the add_header action. This way let us handle the threshold from the Rspamd configuration.

a less simple example

/usr/local/etc/dovecot/sieve/before.d/no-spam.sieve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require "spamtestplus";
require "fileinto";
require "relational";
require "comparator-i;ascii-numeric";
require "imap4flags";

if spamtest :value "gt" :comparator "i;ascii-numeric" :percent "95" {
    discard;
    stop;
} elsif spamtest :value "ge" :comparator "i;ascii-numeric" :percent "50" {
    fileinto "Junk";
    setflag "\\seen";
    stop;
}

/* Other messages get filed into Inbox or to user's scripts */

This example make full use of the spamtest parsing we configured earlier:

  • discard emails with a score superior to 95%,
  • deliver into the Junk folder emails with a score superior or equal to 50%.

Of course YMMV, and this method let you do some crazy stuff with Sieve (tell me if you do!).

That's it for now, have fun!