Intergrate Spam Assassin with Postfix+Courier IMAP

I will share my experience configuring a Linux email server that uses Postfix as MTA, Courier IMAP as IMAP server, and SpamAssassin as spam filter.

Recently, I have been receiving quite a few unsolicited commercial emails (UCE), a.k.a. spams, from my home email server. At work I have experienced with setting up company-wide spam filtering using the excellent SpamAssassin, and we are even running a spam magnet competition by comparing the total number of spam points SpamAssassin assigned to each spam (our marketing guy won, of course)!! At work we are using Postfix 1.1.7 and Cyrus IMAP 2.0 on a Mandrake 8.2 box, and there were quite a few websites on the Internet providing information about how to integrate SpamAssassin into Cyrus' local delivery. However, at home I prefer to use Courier IMAP because it adapts well with my existing Maildir/ system, which has been locally-delivered by Postfix. There aren't many Postfix + Courier IMAP + SpamAssassin documentation/HOW-TOs on the Internet (at the time of writing). Therefore, I decide to share my experiment here.

Updates

  • 10 January 2003: I have since upgraded my home box to Mandrake 9. I've done some modification but things stay mostly the same.
  • 20 August 2003: I am still on Mandrake 9, but I wrote another blog entry on receiving new emails on Courier IMAP's shared folder.

Postfix

I use the standard Postfix package from Mandrake 9. The configuration files are pretty much straight out of box without much tweaking.

  $ rpm -q postfix
  postfix-1.1.11-4mdk

I turn the Real-time Blackhole List (RBL) feature on so that it bounces all the emails from open relays and other black-listed hosts first, before invoking SpamAssassin. These are the configurations added into /etc/postfix/main.cf:

  maps_rbl_domains = relays.ordb.org, relays.osirusoft.com
  smtpd_client_restrictions = reject_maps_rbl
  smtpd_recipient_restrictions = 
    permit_mynetworks, reject_unauth_destination, reject_maps_rbl

Obviously I need to secure my Postfix installation first so that it does not become an open-relay itself.

I need to fire-up procmail to perform the local delivery, because I would like to use procmail to invoke SpamAssassin. Therefore I need to change the Postfix's mailbox_command to invoke procmail on local deliveries. Add the following configuration to /etc/postfix/main.cf as well:

  mailbox_command = /usr/bin/procmail -t /etc/procmailrc-courier

We need to create the procmail RC file /etc/procmailrc-courier a bit later. Command procmail will be invoked using the recepient user's uid, and it will need to deliver to that user's $HOME/Maildir/ directory, so that Courier-IMAP can pick the new mails up later.


Courier-IMAP

My spam approach is sending everyone's spams to a shared IMAP folder. Courier IMAP does support shared IMAP folders, but IMHO it is not as configurable/powerful as Cyrus IMAP's shared folders implementation. Courier IMAP's implementation has some other limitations as well. I am using the standard Courier IMAP packages from Mandrake 8.2. Here's the version information at the moment:

  $ rpm -q courier-imap
  courier-imap-1.4.2-1mdk
Updated Note: Mandrake 9.0 does not come with Courier IMAP package by default, therefore when I upgraded my box from 8.2 to 9.0 it did not get upgraded. However, you can grab Courier IMAP Mandrake packages from their contrib mirrors, which currently has Courier IMAP 1.6.2 at the point of writing (10 January 2003). I am just too lazy to upgrade at the moment. Might try it later.
Updated Note 2: Well. I have finally overcome my laziness and updated to Courier IMAP 1.6.2 (but even that it was out-dated). EVerything still works...

I want the system wide spam to go to a system foilder "shared.System.Spam". I am doing it because there aren't many users on this SMTP server - only 3 users all together - so it is fine to pool all the spams into one single box. However, you can also set up spam folders for individual user, if you have many users on the system, and the spam folder in a relative directory as their INBOX. To configure shared IMAP folder with Courier, you need to edit a file called /etc/maildirshared. Here's my own configuration:

  System  /var/spool/Maildir/System

I create a shared folder called 'System', and point it to this directory /var/spool/Maildir/System. In fact, I also need to create this directory and the Spam directory under it. Read the man page for maildirmake for more details on on to create Maildir manually.

Updated: For more information on setting up shared spam buckets, you might want to look at my another blog entry on another look at shared spam folder under Courier IMAP.

Spam Assassin

Spam Assassin can be downloaded from the Spam Assassin website. I am using version 2.41 on my SMTP box at the moment, but I realise that there might be more recent versions. The set up should hopefully be very similar. I built mine using the tarball, but now you can download and install the package using RPM or Debian format.

After you have installed the Spam Assassin, you might want to modify the default setting. Because we use procmail to invoke Spam Assassin, it is running as recepient's uid and gid. When Spam Assassin runs for the first time as that uid, it checks whether its preference file, $HOME/.spamassassin/user_prefs, exists. If it is not, it will then create that preference file based on the template file it finds. Therefore, to initialise the default preference for each user, you need to modify the master template file. You should be able to find it under /usr/share/spamassassin.

  $ vim /usr/share/spamassassin/user_prefs.template

There are quite a few things you can alter. Spam Assassin assings a hit value to all emails it has scanned, and higher the hit value, more likely it is a spam email. Spam Assassin uses the required_hits value in the preference file to determine whether this email is a spam. If the hit value is higher than required_hits, then 'X-Spam-Status: Yes' will be inserted into email's header. We will check this header later on in procmail to determine where we are sending this email to. Personally I found the default required_hits value (5) is a bit too low, and I've come across quite a bit of false positive. Setting it to 6 or 7 works better for me.


Procmail

I use the default procmail package that comes with Mandrake 9. All the recent procmail should support Qmail's Maildir format, which is also the mailbox format preferred by Courier-IMAP.

  $ rpm -q procmail
  procmail-3.22-3mdk

We need to create a procmail RC file, called /etc/procmailrc-courier. This file will be used by Postfix during local delivery. Here's what I have in my procmail RC file.

  DEFAULT="$HOME/Maildir/"
  SPAMIT="/var/spool/Maildir/System/.Spam/"

  :0 fw
  | /usr/bin/spamassassin -a

  :0
  * ^X-Spam-Status: Yes
  $SPAMIT

You might want to change 'SPAMIT' to your default spam bucket. For example if you are using INBOX.Spam as users' spam box, then you should set SPAMIT to $HOME/Maildir/.Spam/. Remember the trailing slash of the mail box!

Procmail first filters the email through Spam Assassin, and then checks whether 'X-Spam-Status: Yes' exists in the header. It will then deliver the email to that box if spam has been detected.

Conclusion

I have been running this set up (Postfix+Courier IMAP+SpamAssassin) for a few months now, and it catches 90% of spams I have received. SpamAssassin is a great piece of software, and it is amazing sometimes to go through its list of rules when it determines this piece of email is a spam. However, many spams I received are actually in Chinese and Korean, and Spam Assassin's extensive rules on textual analysis do not work well on anything but English at the moment, which is a pitty.