Setting up SpamAssassin

I just set up SpamAssassin for my new company. Over the last year or so, I’ve picked up a lot more about doing sysadmin than I suppose I ever figured I’d know. There’s still plenty of very basic stuff I haven’t figured out yet, but I’m slowly glomming things onto my knowledge base. For example, in September or so, I did my first install/config of postfix, qpopper, and mailman. I had managed previously not to have to do much in the way of mail configuration. With the help of Google, I got it all figured out.

This week, I’ve been working on a server that already had postfix installed, but when I set up the accounts on the box, I almost immediately began to get spam, so I figured I’d set up SpamAssassin. It’s really ridiculously easy, though I ran into one problem that even Google couldn’t help me run down a solution to. Basically, you just get SpamAssassin installed and make sure spamd’s running and that spamc is in place. Then you edit /etc/postfix/ to add the following lines:

smtp      inet  n       -       n       -       -       smtpd -o content_filter=spamchk

That goes in the services section and tells smtpd to run mail through the content filter named (below) “spamchk”. The section defining spamchck is as follows:

spamchk   unix  -       n       n       -       -      pipe
    flags=Rq user=filter argv=/usr/local/bin/spamchk -f ${sender} ${recipient}

So smtpd goes to this section and sees that it’s supposed to execute the file /usr/local/bin/spamchk as the user “filter” (which user has to be created and given permission on the script), passing the incoming mail to the script via pipe. You can put any number of things in your spamchk script. For example, I wrote some code for our system here at my soon-to-be-ex-job that scans subject lines of emails for a certain pattern and saves a copy of relevant emails to the file system (looking in the mails for attachments and saving them out elsewhere) and puts some info about the email in a database, tying it to a customer record so that we automatically have a searchable collection of email stored note in personal mail folders but in a spot accessible by all our internal users. For my new install of SpamAssassin, my filter’s pretty simple:


# -----------------------------------------------------------------
# File:        spamchk
# Purpose:     SPAMASSASIN shell-based filter
# Location:    /usr/local/bin
# Usage:       Call this script from (Postfix)
# -----------------------------------------------------------------

# Variables
SENDMAIL="/usr/sbin/sendmail.postfix -i"

# Exit codes from 

# Number of *'s in X-Spam-level header needed to sideline message:
# (Eg. Score of 5.5 = "*****" )

# Clean up when done or when aborting.
trap "rm -f /var/tmp/out.$$" 0 1 2 3 15

# Pipe message to spamc
cat | /usr/bin/spamc -u filter > /var/tmp/out.$$

# Are there more than $SPAMLIMIT stars in X-Spam-Level header? :
if $EGREP -q "^X-Spam-Level: \*{$SPAMLIMIT,}" < /var/tmp/out.$$
  # Option 1: Move high scoring messages to sideline dir so
  # a human can look at them later:
  # mv out.$$ $SIDELINE_DIR/`date +%Y-%m-%d_%R`-$$

  # Option 2: Divert to an alternate e-mail address:
  $SENDMAIL < /var/tmp/out.$$

  # Option 3: Delete the message
  # rm -f /var/tmp/out.$$
  $SENDMAIL "$@" < /var/tmp/out.$$

# Postfix returns the exit status of the Postfix sendmail command.
exit $?

Initially, when I set this (or even any much simpler filter) up, I’d get a loop error sent to my root account telling me that the message had “Too many hops.” I finally figured out that basically, postfix was routing the mail back through sendmail, which was routing it back through postfix, back through sendmail, and so on, until there were too many iterations and the message failed. The fix turned out to be to change the line ‘SENDMAIL=”/usr/sbin/sendmail -i”‘ to ‘SENDMAIL=”/usr/sbin/sendmail.postfix -i”‘. That done, the filter seems to work like a charm.

I probably need to tweak it because right now, it delivers all spam mail to me rather than saving it out or just marking the subject line with a spam marker and delivering it to the intended recipients. That’s the default in the script I found, and I’m not sure it’s going to cut it for my purposes. Now that I’ve gotten over the looping hump, the rest is simple, though.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s