6. Content filtering

Now we have a fully-functional mail server, able to send and receive email and providing remote access to users' mailboxes. However, if we don't want our server to become an immune carrier of computer viruses or to be drowned under a sea of spam, we need to install all the necessary content-filtering tools.

Though Postfix natively supports multiple content inspection mechanisms, the documentation itself encourages the use of external filters and standard protocols because this allows you to choose the best MTA and the best content inspection software for your purpose. Therefore, we will rely on third-party software for content filtering; in particular, we will use SpamAssassin to filter spam, ClamAV to check emails for viruses and Amavisd-new to coordinate it all. Below is the outline of the whole architecture:

Filtering architecture

6.1 SpamAssassin

SpamAssassin is a mature, widely-deployed open source project that serves as a mail filter to identify Spam. SpamAssassin uses a variety of mechanisms including header and text analysis, Bayesian filtering, DNS blocklists, and collaborative filtering databases.

There are quite a few packages we need to install:

After the packages installation, you will find the main SpamAssassin configuration file (local.cf) in the fresh new /etc/mail/spamassassin directory. The configuration phase can be very complex and goes beyond the scope of this document; anyway, you can find all the details in the man page (Mail::SpamAssassin::Conf).

Like Postfix, SpamAssassin has a lot of configuration parameters, although, in most cases, default values can be preserved and only a few parameters need to be overridden:

/etc/mail/spamassassin/local.cf
rewrite_header	Subject	***** SPAM *****
report_safe	1
lock_method	flock
required_score	8.0

6.2 ClamAV

ClamAV is a GPL anti-virus toolkit for UNIX; the main purpose of this software is the integration with mail servers (i.e. attachment scanning). All the antivirus tasks are handled by three processes:

freshclam
which automatically updates the virus definitions, by connecting to one of the ClamAV mirrors; its configuration file is /etc/freshclam.conf;
clamd
a flexible and scalable multi-threaded antivirus daemon; its configuration file is /etc/clamd.conf;
clamscan
a command line antivirus scanner.

The required packages are:

The freshclam.conf configuration file requires only a few parameters:

/etc/freshclam.conf
DatabaseDirectory	/var/db/clamav
DatabaseOwner		_clamav
DNSDatabaseInfo		current.cvd.clamav.net
DatabaseMirror		db.it.clamav.net
DatabaseMirror		database.clamav.net
MaxAttempts		3
checks			24

Now we can update the virus definition database by running the freshclam command. Please make sure you have installed the latest release of ClamAV, or you'll get warning messages about reduced functionality, like the following:

# freshclam
ClamAV update process started at Tue Dec 18 00:35:25 2007
WARNING: Your ClamAV installation is OUTDATED!
WARNING: Local version: 0.90.3 Recommended version: 0.92
DON'T PANIC! Read http://www.clamav.net/support/faq
Downloading main.cvd [100%]main.cvd [ 22%]
main.cvd updated (version: 45, sigs: 169676, f-level: 21, builder: sven)
WARNING: Your ClamAV installation is OUTDATED!
WARNING: Current functionality level = 16, recommended = 21
DON'T PANIC! Read http://www.clamav.net/support/faq
Downloading daily.cvd [100%]
daily.cvd updated (version: 5160, sigs: 8698, f-level: 21, builder: sven)
WARNING: Your ClamAV installation is OUTDATED!
WARNING: Current functionality level = 16, recommended = 21
DON'T PANIC! Read http://www.clamav.net/support/faq
Database updated (178374 signatures) from db.it.clamav.net (IP: 193.206.139.37)
#

The reduced "functionality level" means that you may not be able to use all the available virus signatures and, consequently, fail to detect the latest viruses. To automatically update the database, we simply have to schedule freshclam in crontab every hour (preferably not on the hour, just to avoid traffic peaks):

16 * * * * /usr/local/bin/freshclam >/dev/null 2>&1

Also the /etc/clamd.conf configuration file needs editing only very few parameters:

/etc/clamd.conf
DatabaseDirectory	/var/db/clamav
LocalSocket		/var/clamav/clamd.sock
User			_clamav
[...]

Now we can run clamd:

# touch /var/log/clamd.log
# chown _clamav /var/log/clamd.log
# clamd
Running as user _clamav (UID 539, GID 539)

and add the following lines to /etc/rc.local(8) to start it on system boot:

/etc/rc.local
if [ -x /usr/local/sbin/clamd ]; then
    echo -n ' clamd'
    [ -S /var/clamav/clamd.sock ] && rm -f /var/clamav/clamd.sock
    /usr/local/sbin/clamd >/dev/null 2>&1
fi

6.3 Amavisd-new

Amavisd-new is a high-performance interface between mailer (MTA) and content checkers. We will configure it to bind to port 10024 on the loopback interface, where Postfix will forward all incoming e-mails. If the e-mail successfully passes all the checks, it will be forwarded back to Postfix, listening on localhost port 10025; otherwise, mails may be deleted or quarantined and the administrator and recepients may be notified.

The following is the list of the required packages:

The installation procedure creates a new user and group called _vscan; however, the easiest way to get Amavisd-new to cooperate with ClamAV, is to run them both under the same user (_clamav). The configuration file is /etc/amavisd.conf, which is actually a perl script (so pay attention to the semi-colons at the end of the lines!); below are the options you will most likely want to tweak:

/etc/amavisd.conf
# COMMONLY ADJUSTED SETTINGS:

$max_servers = 2;
$daemon_user  = '_clamav';     # Run under the same user as ClamAV
$daemon_group = '_clamav';     # Run under the same group as ClamAV

$mydomain = 'kernel-panic.it';

$MYHOME   = '/var/amavisd';
$TEMPBASE = "$MYHOME/tmp";   # Working directory, needs to be created manually
$ENV{TMPDIR} = $TEMPBASE;
$QUARANTINEDIR = '/var/clamav/quarantine';

[...]

# Leave only ClamAV uncommented
@av_scanners = (
  ['ClamAV-clamd',
    \&ask_daemon, ["CONTSCAN {}\n", "/var/clamav/clamd.sock"],
    qr/\bOK$/, qr/\bFOUND$/,
    qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ],
);

[...]

# Leave only ClamAV uncommented
@av_scanners_backup = (
  ['ClamAV-clamscan', 'clamscan',
    "--stdout --disable-summary -r --tempdir=$TEMPBASE {}", [0], [1],
    qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ],
);

1;

After manually creating Amavisd-new's working directory (/var/amavisd/tmp), we can start the daemon in debug mode (i.e. in foreground), just to check any errors:

# mkdir /var/amavisd/tmp
# chown -R _clamav:_clamav /var/amavisd/
# /usr/local/sbin/amavisd debug
Dec 18 22:07:11 mail.kernel-panic.it /usr/local/sbin/amavisd[24429]: starting.
/usr/local/sbin/amavisd at mail.kernel-panic.it amavisd-new-2.3.2 (20050629), Unicode aware
Dec 18 22:07:11 mail.kernel-panic.it /usr/local/sbin/amavisd[24429]: user=,
EUID: 0 (0);  group=, EGID: 0 31 20 5 4 3 2 0 (0 31 20 5 4 3 2 0)
Dec 18 22:07:11 mail.kernel-panic.it /usr/local/sbin/amavisd[24429]: Perl version               5.008008
[...]

Now we can configure the system to start Amavisd-new on boot:

/etc/rc.local
if [ -x /usr/local/sbin/amavisd ]; then
    echo -n ' amavisd'
    /usr/local/sbin/amavisd >/dev/null 2>&1
fi

The last step is to update Postfix configuration to enable interfacing between Postfix and Amavisd-new. To achieve this, we have to add a couple of services to the /etc/postfix/master.cf(5) configuration file: one to forward all incoming emails to Amavid-new, and the other to get emails back again:

/etc/postfix/master.cf
smtp-amavis unix -	-	-	-	2  smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
    -o max_use=20

127.0.0.1:10025 inet n	-	-	-	-  smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks_style=host
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks

Finally, we need to tell Postfix to start forwarding all the emails it receives to amavisd-new for content inspection and reload the configuration.

# postconf -e 'content_filter=smtp-amavis:[127.0.0.1]:10024'
# postfix reload
postfix/postfix-script: refreshing the Postfix mail system