Jul 092007

Virus scanning

This article outlines how to setup virus scanning using AMaViS (A Mail Virus Scanner), ClamAV, and Postfix. The actions describe below are particular to a FreeBSD system and are applicable to other operating systems by altering the path to the configuration files, and adjusting for other OS-specific issues. What? Virus scanning on non-windows? Well, yes. My mail server happens to be running FreeBSD. It also happens to have many clients which are running Windows. Let’s just stop the viruses before they get past my mail servers. Thank you. 🙂 This article is written as a reminder to me for the next time I configure virus scanning with amavisd. It is very high level.


The two main things I install are clamav and amavis. I have written about Postfix previously. This article concentrates on amavis and clamav. Clamav is a virus scanner used by amavis. Both run as daemons. Postfix can be configured to use a content scanner and pass the email it receives to amavis for validation. In turn, amavis contacts clamav to inspect the email. To install the clamav, issue the following command:
cd /usr/ports/security/clamav
make install clean
Similarly, for amavis, I did this:
cd /usr/ports/security/amavisd-new
make install clean
I used the default configuration settings for both ports.

Allowing things to run

The following configuration items in /etc/rc.conf allow the daemons to run:

amavisd_ram="512m"  # Optionally enable amavisd tmp ram disk with: (example 512k)

Permissions and groups

I added clamav to the vscan group by editing /etc/group. The following output shows what to expect. If your results differ, there is something wrong (assumimg you’re running on FreeBSD 6.2 or similar).
# id clamav
uid=106(clamav) gid=106(clamav) groups=106(clamav),6(mail),110(vscan)
# id vscan
uid=110(vscan) gid=110(vscan) groups=110(vscan)

amavis configuration

In /usr/local/etc/amavisd.conf, change the values for these settings:
Search through the file for those settings and adjust them as appropriate for your environment.

Starting things up

To start the daemons, issue these commands:
/usr/local/etc/rc.d/clamav-freshclam start
/usr/local/etc/rc.d/clamav-clamd start
/usr/local/etc/rc.d/amavisd start
After starting amavis, should see something like this in /var/log/messages:
starting.  /usr/local/sbin/amavisd at nyi.example.org amavisd-new-2.5.2 (20070627), Unicode aware
Perl version               5.008008
Module Amavis::Conf        2.091
Module Archive::Zip        1.20
Module BerkeleyDB          0.31
Module Compress::Zlib      2.005
Module Convert::TNEF       0.17
Module Convert::UUlib      1.09
Module DBD::Pg             1.49
Module DBI                 1.52
Module DB_File             1.814
Module Digest::MD5         2.36
Module Digest::SHA1        2.11
Module IO::Socket::INET6   2.51
Module MIME::Entity        5.420
Module MIME::Parser        5.420
Module MIME::Tools         5.420
Module Mail::Header        1.74
Module Mail::Internet      1.74
Module Mail::SpamAssassin  3.002001
Module Net::DNS            0.60
Module Net::Server         0.96
Module Razor2::Client::Version 2.84
Module Time::HiRes         1.9707
Module URI                 1.35
Module Unix::Syslog        0.100
Amavis::DB code      loaded
Amavis::Cache code   loaded
SQL base code        NOT loaded
SQL::Log code        NOT loaded
SQL::Quarantine      NOT loaded
Lookup::SQL code     NOT loaded
Lookup::LDAP code    NOT loaded
AM.PDP-in proto code loaded
SMTP-in proto code   loaded
Courier proto code   NOT loaded
SMTP-out proto code  loaded
Pipe-out proto code  NOT loaded
BSMTP-out proto code NOT loaded
Local-out proto code loaded
OS_Fingerprint code  NOT loaded
ANTI-VIRUS code      loaded
ANTI-SPAM code       loaded
ANTI-SPAM-SA code    loaded
Unpackers code       loaded
Found $file            at /usr/local/bin/file
No $dspam,             not using it
No $altermime,         not using it
Internal decoder for .mail
Internal decoder for .asc 
Internal decoder for .uue 
Internal decoder for .hqx 
Internal decoder for .ync 
Found decoder for    .F    at /usr/local/bin/unfreeze
Found decoder for    .Z    at /usr/bin/uncompress
Found decoder for    .gz   at /usr/bin/gzip -d
Found decoder for    .bz2  at /usr/bin/bzip2 -d
Found decoder for    .lzo  at /usr/local/bin/lzop -d
Found decoder for    .rpm  at /usr/local/bin/rpm2cpio.pl
Found decoder for    .cpio at /bin/pax
Found decoder for    .tar  at /bin/pax
Found decoder for    .deb  at /usr/bin/ar
Internal decoder for .zip 
Found decoder for    .7z   at /usr/local/bin/7zr
Found decoder for    .rar  at /usr/local/bin/unrar
Found decoder for    .arj  at /usr/local/bin/arj
Found decoder for    .arc  at /usr/local/bin/arc
Found decoder for    .zoo  at /usr/local/bin/zoo
Found decoder for    .lha  at /usr/local/bin/lha
Found decoder for    .cab  at /usr/local/bin/cabextract
No decoder for       .tnef tried: tnef
Internal decoder for .tnef
Found decoder for    .exe  at /usr/local/bin/unrar; /usr/local/bin/lha; /usr/local/bin/arj
Found secondary av scanner ClamAV-clamscan at /usr/local/bin/clamscan
Creating db in /var/amavis/db/; BerkeleyDB 0.31, libdb 4.1

Postfix configuration

You have to tell Postfix to talk to amavis. This done by adding this /usr/local/etc/postfix/main.cf:
content_filter = smtp-amavis:[]:10024
max_use = 10
This indicates that Postfix will find a content filter on the local host at port 10024. Then, we must tell Postfix to accept mail on port 10025, the port on which amavis will send inject the scanned email back into the mail delivery process. This directive is added this to /usr/local/etc/postfix/master.cf:
# AMaVIs interface
smtp-amavis unix -      -       n       -       2       smtp
    -o smtp_data_done_timeout=1200
    -o disable_dns_lookups=yes inet n  -       n       -       -       smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
Then you need to restart Postfix (stop, and then start).

Problems I found and fixed

If you see:
amavis[37819]: (37819-01) (!!)WARN: all primary virus scanners failed, considering backups
… the fix is simple. That means amavis can’t talk to any of the virus scanners. Well. Why not? After doing a diff between a working system and my system, I found the problem… ummm, I forgot to tell amavis about clamav…. *blush*. The settings are right there, in /usr/local/etc/amavisd.conf, waiting to be uncommented:
# ### http://www.clamav.net/
   \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd"],
   qr/\bOK$/, qr/\bFOUND$/,
   qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ],
After adding the above directive, a restart of amavis shows:
Internal decoder for .tnef
Found decoder for    .exe  at /usr/local/bin/unrar; /usr/local/bin/lha; /usr/local/bin/arj
Using primary internal av scanner code for ClamAV-clamd
Found secondary av scanner ClamAV-clamscan at /usr/local/bin/clamscan
Creating db in /var/amavis/db/; BerkeleyDB 0.31, libdb 4.1
I have bolded the important line.

The good stuff

Here are a few hints to be sure your mail is being scanned.

Watch your mail logs.

Look for the mail being received, pass onto amavis, then put back into the mail stream. It looks something like this.
incoming mail is received
postfix/smtpd[11082]: connect from toxic.example.net[]
postfix/smtpd[11082]: AACA050830: client=toxic.example.net[]
postfix/cleanup[11084]: AACA050830: message-id=<20070708223029.622FCDA9A9@toxic.example.net>
postfix/qmgr[44283]: AACA050830: from=<testing@example.net>, size=499, nrcpt=1 (queue active)
postfix/smtpd[11082]: disconnect from toxic.example.net[]
mail is passed to amavis
postfix/smtpd[11088]: connect from localhost[]
postfix/smtpd[11088]: 70FFD5083E: client=localhost[]
postfix/cleanup[11084]: 70FFD5083E: message-id=<20070708223029.622FCDA9A9@toxic.example.net>
postfix/qmgr[44283]: 70FFD5083E: from=, size=960, nrcpt=1 (queue active)
postfix/smtpd[11088]: disconnect from localhost[]
amavis scans the email
amavis[7809]: (07809-03) Passed CLEAN, [] <testing@example.net> -> <dan@localhost.example.org>, Message-ID: <20070708223029.622FCDA9A9@toxic.example.net>, mail_id: dDcmg7cMFqri, Hits: 1.285, size: 499, queued_as: 70FFD5083E, 681 ms
the mail passed to amavis is removed from the queue
postfix/smtp[11085]: AACA050830: to=<dan@localhost.example.org>, orig_to=<dan@langille.org>, relay=[]:10024, delay=0.89, delays=0.19/0.01/0/0.69, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 70FFD5083E)
postfix/qmgr[44283]: AACA050830: removed
scanned mail is delivered to the user
postfix/local[11089]: 70FFD5083E: to=<dan@localhost.example.org>, relay=local, delay=0.05, delays=0.01/0.02/0/0.02, dsn=2.0.0, status=sent (delivered to command: exec /usr/local/bin/procmail -t -a ${EXTENSION})
postfix/qmgr[44283]: 70FFD5083E: removed


The following is from the headers of the above email:
X-Original-To: dtm@bast.example.org
Delivered-To: dan@bast.example.org
Received: from nyi.example.org (nyi.example.org [])
	by bast.example.org (Postfix) with ESMTP id BCD4CB87D
	for ; Sun,  8 Jul 2007 18:30:31 -0400 (EDT)
Received: from localhost (localhost [])
	by nyi.example.org (Postfix) with ESMTP id 29CED50841
	for ; Sun,  8 Jul 2007 18:31:25 -0400 (EDT)
X-Virus-Scanned: amavisd-new at example.org
X-Spam-Flag: NO
X-Spam-Score: 2.735
X-Spam-Level: **
X-Spam-Status: No, score=2.735 tagged_above=2 required=6.2 tests=[AWL=-1.450,
Received: from nyi.example.org ([])
	by localhost (nyi.example.org []) (amavisd-new, port 10024)
	with ESMTP id Roxtp5UAQ8d1 for ;
	Sun,  8 Jul 2007 18:31:24 -0400 (EDT)
Received: by nyi.example.org (Postfix, from userid 1001)
	id 808D550840; Sun,  8 Jul 2007 18:31:24 -0400 (EDT)
X-Original-To: dan@localhost.example.org
Delivered-To: dan@localhost.example.org
Received: from localhost (localhost [])
	by nyi.example.org (Postfix) with ESMTP id 70FFD5083E
	for ; Sun,  8 Jul 2007 18:31:24 -0400 (EDT)
X-Virus-Scanned: amavisd-new at example.org
Received: from nyi.example.org ([])
	by localhost (nyi.example.org []) (amavisd-new, port 10024)
	with ESMTP id dDcmg7cMFqri for ;
	Sun,  8 Jul 2007 18:31:23 -0400 (EDT)
Received: from toxic.example.net (toxic.example.net [])
	by nyi.example.org (Postfix) with ESMTP id AACA050830
	for ; Sun,  8 Jul 2007 18:31:23 -0400 (EDT)
Received: by toxic.example.net (Postfix, from userid 1206)
	id 622FCDA9A9; Sun,  8 Jul 2007 18:30:29 -0400 (EDT)
To: dan@example.org
Message-Id: <20070708223029.622FCDA9A9@toxic.example.net>
Date: Sun,  8 Jul 2007 18:30:29 -0400 (EDT)
From: testing@example.net (Dan Langille)
X-PMFLAGS: 33554560 0 1 P6UKIA7Z.CNM                        

That is what you can expect from the email headers if you are scanning them through amavis.

Defense in depth

Sure, you might not have to deal with viruses on your chosen OS. But amavis/clamav is a good approach if you prefer to scan. Some complain that virus scanning is processor intensive. I suggest using pf and spamd to vastly reduce the amount of spam that gets to your mail server. Enjoy.