Another method (for different needs, of course): On our site, we just put qmail-scanner in the qmail queue. All that needs to be done ("all?" I spent hours on this :P) is apply the QMAILQUEUE patch to qmail, and install qmail-scanner by following its instructions:

qmail-scanner works with spamassassin and clamscan and can do primitive attachment checks (i.e., "block all .exe/.bat/.vbs attachments"). The way we have it set up, it just puts an 'X-Spam-Status: Yes/No' in the message's header, and users can set their mail clients to use rules based on that.

The downside: If you're running a very large site and only expect a few people to use the virus-scanning abilities of spamassassin, you're going to be burning lots of processor cycles. Our site is relatively small and we need qmail-scanner anyway to block "dangerous" attachments, so the waste of CPU isn't an issue for us.

Adam Hooper

Tom Walsh wrote:

There was some discussion a while back about making SA work with vpopmail.

Most solutions I saw used .qmail-username or .qmail-default which wasn't the
right method for us. Below is a quick write up on how to use SA+clamscan
from a .qmail file in the users maildir. We also pull user preferences for
SA from SQL, so the line we use to call SA might not be what you want.
Delivery and hand off for scanning is handled by maildrop.

First and foremost, make certain that the user vpopmail has a valid shell...
this is very important as vdeliermail will run anything in the .qmail as the
user vpopmail, provided the application doesn't do a setuid/setgid, which
maildrop does not do. (That alone cost me 3 hours to troubleshoot.)

For each user you want to enable SA and virus scanning put the following in
a .qmail file in the users directory:

| /var/qmail/bin/preline /usr/local/bin/maildrop

Make sure that this file has been chmod'ed to 600 (u+rw) and is owned by
vpopmail:vchkpw otherwise it will not be run.

The .mailfilter listed above contains (some of this script has come from
another list member, but I forgot his name, if you contact me I will give
credit where credit is due), it must also be chmod'ed to 600 with owner
vpopmail:vchkpw :

import EXT
import HOST
VHOME=`/usr/home/vpopmail/bin/vuserinfo -d [EMAIL PROTECTED]

# Check for Spam if it is smaller than 250KB
if($SIZE < 262144)
        xfilter "/usr/local/bin/spamc -d -t 20 -f -u [EMAIL PROTECTED]"
if ((/^X-Spam-Flag:.*YES/))
        `/bin/test -d $VHOME/Maildir/.Spam`
        if( $RETURNCODE == 1 )
                `/var/qmail/bin/maildirmake $VHOME/Maildir/.Spam;
/usr/sbin/chown -R vpopmail:vchkpw $VHOME/Maildir/.Spam`
        to "$VHOME/Maildir/.Spam/"

# If it isn't Spam, then we scan for Virus if it is smaller than 2MB in
size... anything larger... they are on their own
if($SIZE < 2000000)
        xfilter "/usr/home/vpopmail/domains/"
if ((/^X-Virus-Status:.*INFECTED/))
        `/bin/test -d $VHOME/Maildir/.Virus`
        if ( $RETUNRCODE == 1 )
                `/var/qmail/bin/maildirmake $VHOME/Maildir/.Virus;
/usr/sbin/chown -R vpopmail:vchkpw $VHOME/Maildir/.Virus`
        to "$VHOME/Maildir/.Virus/"

#If it isn't Spam or Virus, then deliver normally
to "$VHOME/Maildir/"

The specific lines of interest are the xfilter lines. We use spamc/spamd to
offload the very CPU intensive process of spam scanning to another machine
on the private network. That is what the -d directive is for which tells SA
which IP to connect to for spamd...

The file is a wrapper for the clamscan binary. We need to do
this because of the incompatibility between how clamscan operates and how
maildrop expects an xfilter program to operate. maildrop expects any message
it sends out to an xfilter program to be returned to it via stdout. The
problem is that the clamscan binary only returns the results of the scan,
not the message, so we have to create a shell script to pass the altered
message back to maildrop via stdout, also we use the shell script to alter
the exit code of clamscan (0 if clean and 1 if infected) to be compatible
with what maildrop expects. maildrop expects the application to return a
exit code of 0, so we have to alter it.

You will need bash in order to use this.

# Created by Tom Walsh
# slim at

MSG=$(/bin/cat /dev/stdin) # Is there a better way to do this?
SCAN=$(echo "$MSG" | /usr/local/bin/clamscan - --stdout --disable-summary)
VIRUS=$(echo "$SCAN" | awk '{print $2}')
SUBJECT=$(echo "$MSG" | /usr/local/bin/reformail -x Subject:)

if [ "$EXIT" == "1" ]; then
 MSG=$(echo "$MSG" | /usr/local/bin/reformail -a"X-Virus-Status:
INFECTED" -i"Subject: $(echo "$SUBJECT")")
 MSG=$(echo "$MSG" | /usr/local/bin/reformail -a"X-Virus-Status: CLEAN")

echo "$MSG"

exit 0

And just for completeness... I have included our spamd config line to let
you know how to pull settings from SQL:

/usr/local/bin/spamd -a -d -q -x -m 50 -u spamd -i -A -A

The -i directive tells spamd to listen on IP, by default it only
listens on
The -A directives tell spamd which IPs to accept connections from.

You also need to odify your file to include the settings for
connecting to the SQL server.... All of that is covered in the README for

I hope that helps somebody... We are going to be ramping up the load on the
SA box shortly to see how well it scales... We are considering doing load
balancing via two SA boxes and a psuedo-random IP selector script that will
feed a variable $IP to the .mailfilter script above... something like:


xfilter "/usr/local/bin/spamc -d $IP -t 20 -f -u [EMAIL PROTECTED]"

If anybody has any comments or suggestions I would be willing to hear
them... I am currently writing up a howto to post to the web soon, but it is
rather FBSD specific I am afraid...

As a side note with regard to spamd reading the settings from SQL... spamd
makes a lookup on the [EMAIL PROTECTED], but also makes a lookup on GLOBAL
and @GLOBAL, so you can have a global preference for anybody that doesn't
have an entry in the SQL table... A very nice feature.


Tom Walsh
Network Administrator

Reply via email to