Adding a little bit of SpamAssassin into Postfix, Dovecot, Sieve soup

July 21, 2012

I was always prefering "stay in the shadows" policy in terms of email address. I have two secondary emails (for spam I want to read, and for spam I want to be sent into oblivion i.e. for "Register NOW to download this 2KB file" sites). My primary e-mail was well guarded and given only to living people. Until one company decided to show it to the whole world by putting it into WHOIS database for my domain. Before I reacted, it was too late. And my little mail server needed one more extension.

I wanted to integrate it somehow with my existing configuration - so, when message is parsed by spam filter, it needs to be filtered further by sieve. Thanks to that, I can have full control over spam and even categorize it (yeah, I'm a picky bastard ;)). Thankfully SpamAssassin can do this, so I didn't have to look further. I also decided to inlcude Pyzor, Razor and DCC. No mercy!

Installing packets

But first - necessary packets. Thankfully, debian has everything out of box, except DCC.

apt-get install spamc spamassassin pyzor razor

Next is DCC, which has to be build manually:

groupadd dcc
useradd -g dcc -s /bin/false -d /var/dcc dcc
wget http://www.dcc-servers.net/dcc/source/dcc-dccproc.tar.Z
tar xzvf dcc-dccproc.tar.Z
cd dcc-dccproc-1.3.142
./configure --with-uid=dcc
make
make install
chown -R dcc:dcc /var/dcc
ln -s /var/dcc/libexec/dccifd /usr/local/bin/dccif

Setting up SpamAssassin

The next step is to configure spamd to run as a daemon:

groupdd spamd
useradd -g spamd -s /bin/false -d /var/lib/spamassassin spamd
mkdir -p /var/lib/spamassassin
chown spamd:spamd /var/lib/spamassassin -R

/etc/default/spamassassin

# Spamassassin home
SAHOME="/var/lib/spamassassin"

# Where imap stores user emails
USERACCOUNTS="/home/vmail"

# Change to one to enable spamd
ENABLED=1

# Options
# See man spamd for possible options. The -d option is automatically added.

# SpamAssassin uses a preforking model, so be careful! You need to
# make sure --max-children is not set to anything higher than 5,
# unless you know what you're doing.
# For -A use the IP address of spamc client (probably IP of primary interface)
OPTIONS="--create-prefs -x --max-children 3 --username spamd --helper-home-dir ${SAHOME} -s ${SAHOME}/spamd.log --virtual-config-dir=${USERACCOUNTS}/%l@%d/spamassassin -A 192.168.1.1"

[...]

The virtual-config-dir allows us to have separate user preferences and bayes databases for each virtual user. The next thing is to edit /etc/spamassassin/local.cf file:

/etc/spamassassin/local.cf

[...]
# Save spam messages as a message/rfc822 MIME attachment instead of
# modifying the original message (0: off, 2: use text/plain instead)
report_safe 0
[...]

use_dcc 1
dcc_path /usr/local/bin/dccproc

use_pyzor 1
pyzor_path /usr/bin/pyzor

use_razor2 1
razor_config /etc/razor/razor-agent.conf

Afterwards, edit /etc/spamassassin/v310.pre and check that the DCC, Razor and Pyzor plugins are enabled (DCC is disabled by default). After that, the only thing left is to update SA databases and start it:

sa-update --no-gpg
/etc/init.d/spamassassin start

Setting up postfix transport

From the postfix side all you have to do is change transport (I suggest to create a new one - then, when something goes wrong you can easily switch back to old working configuration) or mailbox_command. For transport, the magic line looks like this:

/etc/postfix/master.cf

dovecot-spam   unix  -       n       n       -       -       pipe
    flags=DRhu user=vmail:vmail argv=/usr/bin/spamc -u ${recipient} -e /usr/lib/dovecot/deliver -d ${recipient}

And this is the line I've been looking for. SpamAssassin takes message, pushes it through his intestines, adds headers, and output is pushed further to deliver command. From that point, it can be taken by Sieve. To make this work, don't forget to change the transport!

/etc/postfix/main.cf

mailbox_transport = dovecot-spam

Restart postfix:

/etc/init.d/postfix restart

And finally... Sieve

After all of that, all you have to do is configure Sieve. For example like that (Junk is a folder which Thunderbird traditionally uses for spam, you can change it of course):

.dovecot.sieve

if header :contains "X-Spam-Flag" ["YES"] { fileinto "Junk"; stop; }

To test it, just send an email to protected account and look into the headers. You should see a SpamAssassin magic added there. To test filtering, use GTUBE message format - your email should land in Junk.

Conclusion

I think this configuration will suit my needs. Probably it will get some adjustments over time (like intelligent filters in SA and so on), and - when I start to trust it fully - instead of moving into the Junk all spam will be deleted. But for now, everything works like a charm :) If you experience any problems, first look into /var/lib/spamassassin/spamd.log file - there is big chance, that you'll find your answers there.

Sources