You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 23 Next »

Integrating SpamAssassin into Postfix using spamd

The easiest way to integrate postfix and spamassassin is to use spamd in an [http://www.postfix.org/FILTER_README.html after-queue] inspection. This configuration does not allow rejecting messages within the SMTP transaction, so it unfortunately contributes to backscatter email. On the other hand, it has important performance advantages over [http://www.postfix.org/SMTPD_PROXY_README.html before-queue] inspection.

First, edit /etc/postfix/master.cf, find the

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (50)
# ==========================================================================
...
smtp      inet  n       -       n       -       -       smtpd
...

line and just add " -o content_filter=spamassassin" to the end of the line:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (50)
# ==========================================================================
...
smtp      inet  n       -       n       -       -       smtpd -o content_filter=spamassassin
...

Then, go to the end of the file, and add this:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (50)
# ==========================================================================
...
spamassassin
          unix  -       n       n       -       -       pipe
   user=nobody argv=/path/to/spamc -e /path/to/postfix/sendmail -oi -f ${sender} ${recipient}
# make sure it's all on one line or
# start the consecutive lines with a whitespace
# like I did here

Make sure that you have adjusted the path to the spamc and sendmail commands above! (Please note that the path required is Postfix's sendmail and not the standalone package, Sendmail the SMTP server. It will not work if you're not careful about which one is installed). Then, setup spamd to start with the system, and you are ready to go. If you wish to provide spamassassin preferences, change "user=nobody" to a valid system user (except for root, since Postfix will NOT call external programs as root), and add .spamassassin into that user's home directory.

If you use user preferences stored in SQL, you should change "spamassassin" service in master.cf to following:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (50)
# ==========================================================================
...
spamassassin
          unix  -       n       n       -       -       pipe
   flags=Rq user=nobody argv=/path/to/spamc -u ${recipient} -e /path/to/postfix/sendmail -oi -f ${sender} ${recipient}

Notice "-u ${recipient}" added. Otherwise "username" field in database will always appear as user which postfix is invoking spamc(in this example it is 'nobody').

Note that this exact method of invoking SpamAssassin is not very robust. Postfix's pipe error behavior is as follows:

  • If the exec() fails (e.g., /path/to/spamc is incorrect), Postfix will bounce the message.
  • If the command returns EX_OSERR or EX_TEMPFAIL (defined in /usr/include/sysexits.h), Postfix will defer the message.
  • If the command returns EX_OK, Postfix will consider the message done. The command should deliver it; it will be discarded otherwise.
  • If the command returns any other error code, Postfix will bounce the message.

With spamc -e, spamc will return EX_OK when spamd is unavailable but sendmail is working, so Postfix will deliver the message. You probably want it to be deferred instead. This is not possible with spamc directly, but you can do so with a simple script. Replace the lines above with:

spamassassin
          unix  -       n       n       -       -       pipe
   flags=Rq user=nobody argv=/path/to/filter.sh -oi -f ${sender} ${recipient}

and create a filter.sh as follows:

#!/bin/bash
SENDMAIL="/usr/sbin/sendmail -i"
SPAMASSASSIN=/usr/bin/spamc

# Exit codes from <sysexits.h>
EX_TEMPFAIL=75
EX_UNAVAILABLE=69

umask 077

OUTPUT="`mktemp mailfilter.XXXXXXXXXX`"
if [ "$?" != 0 ]; then
    /usr/bin/logger -s -p mail.warning -t filter \
        "Unable to create temporary file."
    exit $EX_TEMPFAIL
fi

# Clean up when done or when aborting.
trap "rm -f $OUTPUT" EXIT TERM

$SPAMASSASSIN -x > $OUTPUT
return="$?"
if [ "$return" = 1 ]; then
    echo "Message content rejected"
    exit $EX_UNAVAILABLE
elif [ "$return" != 0 ]; then
    /usr/bin/logger -s -p mail.warning -t filter \
        "Temporary SpamAssassin failure (spamc returned $return)"
    exit $EX_TEMPFAIL
fi

$SENDMAIL "$@" < $OUTPUT
exit $?

You still must be careful that the script is in the right place and executable or email will bounce. However, this will defer messages on most failures.

A more complex solution is the one proposed in the [http://www.postfix.org/FILTER_README.html FILTER_README file] that comes with the Postfix distribution. See IntegratePostfixViaSpampd for one approach.

Only mail received by SMTP will be scanned with this method, i.e. mail injected with sendmail(1) will not be fed to SpamAssassin.

Alternative

A major problem with the above filter is that Postfix attempts to bounce messages flagged as spam. This is a waste of resources as most spams have fake return addresses. Furthermore, if they fake a return address that doesn't belong to them, you may end up bouncing the message to an innocent 3rd party. Enough of this can result in YOU being on an RBL.

_ Not true. (1) Postfix only bounces messages if you tell it to. I currently use the above filter, and I use a sieve rule to filter X-Spam-Flag: YES into a folder in the same account which I occasionally review. Many people sort mailing lists into separate folders, and this can typically be done with the same mechanism. It also makes it more practical to actually check it every now and then... all spam systems have false positives, so silently blackholing likely spam (potential ham) is far more rude than bouncing it. (2) If you use a proper bounce message (as opposed to those stupid virus filter notifications), there are ways for the innocent party to filter out bounces from messages they didn't send. When I get some time, I'll write one up and link to it. -Slamb_

Instead, we'd like to create a blackhole for spam.

To accomplish this while still using an after-queue filter, the spam may be redirected to another account on the system. Steps to do this are as follows:

1) Modify /etc/postfix/master.cf with:

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

... and then at the end, add:

spamassassin
          unix  -       n       n       -       -       pipe
   flags=Rq user=nobody argv=/path/to/filter.sh -oi -f ${sender} ${recipient}

2) Create filter.sh as follows:

#!/bin/sh

# filter.sh
#
# This script redirects mail flagged as spam to a separate account
# You must first create a user account named "spamvac" to hold the flagged mail

SENDMAIL="/usr/sbin/sendmail -i"
SPAMASSASSIN=/usr/bin/spamc
COMMAND="$SENDMAIL $@"
USER=`echo $COMMAND | awk '{ print $NF }' | sed 's/@.*$//'`
NEW_COMMAND=`echo $COMMAND | awk '{ $6 = "spamvac"; NF = 6; print }'`

# Exit codes from <sysexits.h>
EX_TEMPFAIL=75
EX_UNAVAILABLE=69

umask 077

OUTPUT="`mktemp /tmp/mailfilter.XXXXXXXXXX`"

if [ "$?" != 0 ]; then
    /usr/bin/logger -s -p mail.warning -t filter "Unable to create temporary file."
    exit $EX_TEMPFAIL
fi

# Clean up when done or when aborting.
trap "rm -f $OUTPUT" EXIT TERM

$SPAMASSASSIN -x -E -u $USER > $OUTPUT
return="$?"
if [ "$return" = 1 ]; then
    $NEW_COMMAND < $OUTPUT
    exit $?
elif [ "$return" != 0 ]; then
    /usr/bin/logger -s -p mail.warning -t filter "Temporary SpamAssassin failure (spamc returned $return)"
    exit $EX_TEMPFAIL
fi

$SENDMAIL "$@" < $OUTPUT
exit $?

This causes incoming smtp mail to be checked for spam. If the mail's spam score exceeds the threshold (set in local.cf, or in ~/.spamassassin/user_prefs) then spamc -E returns 1 and the mail is redirected to the spamvac account. Otherwise it is passed on to the intended recipient. Bounces no longer occur except on a true error condition.

  • No labels