How To Install Postfix, Dovecot, Amavis, ClamAV, and Spamassassin (Etch)

by Tobias Rice

Version 1.0

Note: This how-to works as is under the LDAP how-to here:
http://www.fatofthelan.com/articles/articles.php?pid=24

Note: This article covers some basic Spam filtering. It is not the place for detailed UBE handling. I will soon be outlining a detailed How-To just for spam. I am also now focusing on Debian Etch, so if you need to install on Sarge or Woody please see the past versions of this article. For more information on Postfix, check out this book, too.

INTRODUCTION

For the sake of clarity, I’ll be using “tobias.local” for my domain, and 123.123.123.123 for my IP address. These values should obviously be changed to your settings. Although this describes the install process on Debian Linux, it should be the same on any flavor of Linux minus the Debian-centric commands like “apt-get”. This may be a rather terse guide, like a step by step to email, so if you’re wanting more theory or explanation, read your man pages or see a more verbose guide like one of these:
http://www.firstpr.com.au/web-mail/RH90-Postfix-Courier-Maildrop-IMAP/
http://www.workaround.org/articles/ispmail-sarge/
http://postfix.state-of-mind.de/patrick.koetter/smtpauth/
…which are excellent, but a little different from what I’ll be doing here. Also, a big thanks goes out to Scott Kveton for his help.
In the end, I want a system that allows my users to have several predefined mail folders, i.e. Sent, Drafts, Trash, Templates, and one of them being a Junkmail folder where spam will automatically be delivered.

INSTALLATION

I’ll assume you have already done a base install of Etch and we’ll start from there.
The first thing we’ll do is edit our apt sources file to allow us to use the most current and up to date versions of ClamAV and Spam Assassin.
Add the following to your /etc/apt/sources.list:

#For clamav and spam assassin
deb http://ftp2.de.debian.org/debian-volatile etch/volatile main
deb-src http://ftp2.de.debian.org/debian-volatile etch/volatile main

Now run:
apt-get update && apt-get install postfix spamassassin spamc amavisd-new clamav clamav-daemon libmailtools-perl fam libnet-dns-perl

Answer yes if you’re asked to remove exim or whatever MTA you may have installed. You will be taken through several Postfix installation screens where you’ll be asked for details about your installation. For the FQDN of my server I will enter “mail.tobias.local” and this server will be used as an “Internet site”.
Postfix can deliver mail to mbox or Maildir formatted mailboxes by itself, but since I want to do a little more mail processing, I am going to install an additional delivery agent (think Maildrop or Procmail). Since I’m going to be using the Maildir format instead of mbox, I’ll be installing Courier Maildrop as the delivery agent. If you want to stick to mbox, you can install Procmail for you delivery agent.
NOTE: There are two versions of Maildrop; A stand alone version and a version that is to be used if you’ll be using Courier’s IMAP deamon which I will NOT be using.
apt-get install maildrop

INITIAL CONFIGURATION

Before we get too far along, we need to make sure Postfix is running properly, then we’ll add more functionality later. All of Postfix’s configuration files are located in /etc/postfix. If you open /etc/postfix/main.cf (Appendix A) in your favorite text editor, you should see your domain information in here from the Postfix install scripts you answered during the setup. We also need to setup our Maildir mail boxes with the “maildirmake” command. We’ll do this for existing users and also in /etc/skel so new users automatically get the same setup. To setup our Maildir, change directories to your home and run:
maildirmake Maildir
maildirmake -f Sent Maildir
maildirmake -f Drafts Maildir
maildirmake -f Templates Maildir
maildirmake -f Junkmail Maildir
If you’re going to use the email training system you will need to create the two following directories as well:
maildirmake -f Junkmail.mark_as_good Maildir
maildirmake -f Junkmail.mark_as_spam Maildir
Now repeat for /etc/skel and any users who already existed and won’t have the directories automatically created. If your existing users are in the mbox format and you’d like to switch them to the Maildir format you can convert them to Maildir with the mb2md command.
apt-get install mb2md

To have Postfix deliver to Maildir, add the following line to your main.cf:

home_mailbox = Maildir/

NOTE: Don’t forget the trailing slash, (“/”).

Test out your email and make sure everything works thus far. If all is working properly, bring Maildrop online by editing (or adding) the line to your main.cf file:

mailbox_command =

and changing it to

mailbox_command = /usr/bin/maildrop

or if you are planning on using Procmail, enter:

mailbox_command = /usr/bin/procmail -a "$EXTENSION"

and issue the “postfix reload” command. Send a test mail and “tail” your /var/log/syslog. You should see something like this:
postfix/local[3067]: 52239CF00: to=, orig_to=, relay=local, delay=0, status=sent (“|/usr/bin/maildrop”)

You should also have an email in the user’s /home/someuser/Maildir/new/ directory.
Now create the file /etc/maildroprc. This will give us global logging and filtering options, but most of the filtering will be done per user by the user’s .mailfilter file located in their home directory and will be the mechanism that calls Spam Assassin. We’ll create a basic .mailfilter in /etc/skel so all new users will automatically have it. See Appendix B for an example .maildir file that will call Spam Assassin and contains several mail processing examples.
You will need to edit /etc/maildroprc and uncomment “DEFAULT=”$HOME/Maildir”” to enable Maildrop to deliver to Maildir mailboxes.
My /etc/maildroprc only contains the following:

DEFAULT="$HOME/Maildir"
logfile "/var/log/maildrop"
VERBOSE="5"

You will need to create the log file for Maildrop to write to with the following commands:
touch /var/log/maildrop
chown root.adm /var/log/maildrop
chmod 642 /var/log/maildrop

Now send an email and you should see the details in the /var/log/maildrop file.
You’ll also want to add Maildrop’s log file to logrotate. So create a new file in the /etc/logrotate.d directory called maildrop, and add the following:

/var/log/maildrop {
weekly
missingok
}

NOTE: Do not forget to create the /etc/skel/.mailfilter file (Appendix B) or mail will not be checked for spam!
DOVECOT INSTALLATION
To enable uses to check their email you’ll need to install some sort of IMAP/POP daemon. Although there are several capable daemons available I will be setting up Dovecot since I think it is the most secure. To install Dovecot, issue the following command:
apt-get install dovecot-imapd dovecot-common

Dovecot is fairly easy to setup. You simply need to tell it what protocols to run, what type of mailbox the system uses, and where the user’s mail directories are located.
I will only be running IMAP/IMAPS because I like to keep all of my user’s mail centralized for simplicity’s sake and to enable an easy backup strategy. Find the “protocols =” statement in /etc/dovecot/dovecot.conf and change to:

protocols = imap imaps

Since I require that all user’s credentials be passed via an encrypted channel I will only allow IMAPS to be used which is secured by SSL. Dovecot already has provisions for SSL, you need only to uncomment these lines:

ssl_cert_file = /etc/ssl/certs/dovecot.pem
ssl_key_file = /etc/ssl/private/dovecot.pem

NOTE: These certificates already exist and are for example only. You will need to generate you own certificates for production!
Although I do use IMAP (not encrypted) for local access via web based email, I do not allow any remote connections to IMAP and enforce the use of IMAPS by only allowing connections to port 993 with my firewall. I strongly suggest you do the same.
And finally we need to configure Dovecot to access email in the proper location and in the correct format. Find and edit the “mail_location” parameter in /etc/dovecot/dovecot.conf to match this:

mail_location = maildir:~/Maildir

ANTIVIRUS INSTALLTION

To enable email to be checked for malicious code we will be installing Amavis.
Amavis is extremely configurable and robust and you should take the time to read the documentation to see what fits your needs best. Basically, Amavis is a wrapper that can check email for a variety of rules and block or pass it as well as pipe each email through other anti-virus or anti-spam applications. Amaivis is a global application that we’ll use to check for malicious code by piping our mail through Clam Anti-virus. We could use Amavis to call Spam Assassin but I want each user to have control over how SA is applied which cannot be done if used globally.
Amavis works by being inline with Postfix. We will configure Postfix to pass all mail to Amavis which passes all mail to ClamAV. If the mail is clean Amavis will pass it back to Postfix which will continue to deliver the mail. If ClamAV determines that the mail is infected or malicious Amavis can quarantine or delete it.

To configure Amavis look in /etc/amavis/conf.d directory and make changes to the 50-user file which takes precedence over the other files and will not be changed by future upgrades/changes to Amavis.
You may want a header inserted into each email to show that Amavis is working properly so add this to the /etc/amavis/conf.d/50-user file:

# Leave empty (undef) to add no header
$X_HEADER_LINE = "$myproduct_name at $mydomain";

Although the RFC says you should let the sender know you’re killing their mail, it is a waste of bandwidth, as most virus/spam mail is spoofed. Tell Amavis how to handle the unwanted content by adding these lines:

$final_virus_destiny = D_DISCARD; # (defaults to D_BOUNCE)
$final_banned_destiny = D_REJECT; # (defaults to D_BOUNCE)
$final_spam_destiny = D_PASS; # (defaults to D_REJECT but we handle elsewhere)

You MUST enable Amavis to use antivirus by uncommenting the following section in /etc/amavis/conf.d/15-content_filter_mode :

@bypass_virus_checks_maps = (
\%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);

ClamAV user needs access to the messages to be able to check them so add it to the Amavis group:
adduser clamav amavis
Start Amavis and check for typos. To see any error messages and debugging info, I suggest running Amavis for the first time interactively and keep it attached to the terminal by issuing the following comands:
su – amavis
/usr/local/sbin/amavisd debug

From another window check that it is listening on a local SMTP port 10024 (default):

--> $ telnet 127.0.0.1 10024

Trying 127.0.0.1...

Connected to 127.0.0.1.

Escape character is '^]'.
220 [127.0.0.1] ESMTP amavisd-new service ready
--> quit
221 Bye

Connection closed by foreign host.

Now add the following to the end of your /etc/postfix/master.cf file:

smtp-amavis unix -      -       n     -       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  -       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 smtpd_data_restrictions=reject_unauth_pipelining

-o smtpd_end_of_data_restrictions=

-o mynetworks=127.0.0.0/8

-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

Now add this to your /etc/postfix/main.cf to plug Postfix into Amavis:

content_filter = smtp-amavis:[127.0.0.1]:10024

What all of this does is add a SMTP proxy. Mail comes into Postfix and is handed off to Amavis on port 10024. Amvais will then process the mail and hand it back to Postfix on port 10025.

Reload Postfix. At this point, you should have a working installation that filters mail for viruses and spam. The only step left is to setup SMTP authentication so users can relay mail through the server without fear of spammers using it as an open relay.

SMTP AUTH

To enable users to relay messages through the server, they must authenticate with the server. Although there are several ways to do this, I like using PLAIN or LOGIN for compatibility reasons. Since we’ll be using the PLAIN or LOGIN mechanisms for SMTP AUTH, usernames and passwords are sent over the internet in plaintext. To keep anyone sniffing our traffic from seeing our credentials, we need to encrypt our traffic with Transport Layer Security, or TLS. We’ll setup all of this up in two steps. The first is configuring Postfix for SMTP authentication. The second step is setting up TLS for encrypting the users credentials.
To have Postfix authenticate users, we’ll pass the authentication to SASL and let it do the actual query against the shadow file via PAM. Create the /etc/postfix/sasl/smtpd.conf file and enter the following:

pwcheck_method: saslauthd
mech_list: PLAIN LOGIN

Now install the sasl apps:
apt-get install libsasl2-modules sasl2-bin

To have the the sasl authentication daemon listening where Postfix will be looking for it, we’ll need to tell saslauthd the specifics. Open the /etc/default/saslauthd file in your favorite editor and edit the file to reflect the following statements:

start=yes
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"

Now we’ll have to make the directory we just added in the previous step, chown it so Postfix can use it, and add the Postfix user to the sasl group.
mkdir -p /var/spool/postfix/var/run/saslauthd
chown -R root:sasl /var/spool/postfix/var/
adduser postfix sasl

Since Postfix is chrooted execute the following line to allow authentication to work in the new location:
ln -s /var/spool/postfix/var/run/saslauthd /var/run/saslauthd

Start saslauthd and check that is running. Issue the following command:
ps waux | grep saslauthd

You should see something similar to this:
root 3292 0.0 0.0 7336 1424 ? Ss Feb07 0:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
NOTE: Many people have had problems with sasl not working correctly. This is almost always due to sasl not shuting down or acceptting the new settings. If you have problems with sasl, kill -9 all of its PID’s or simply reboot.
See this: http://www.fatofthelan.com/forums/viewtopic.php?t=86

To check that sasl is indeed working, use the testsaslauthd command with your username and password:
testsaslauthd -u username -p password -f /var/spool/postfix/var/run/saslauthd/mux

If everything is setup correctly, you should see:
0: OK “Success.”

To have Postfix use sasl, you need to add this (preserving the spaces and commas!) to your main.cf:

smtpd_recipient_restrictions =

permit_sasl_authenticated,

permit_mynetworks,

reject_unauth_destination
smtpd_sasl_auth_enable = yes

smtpd_sasl_security_options = noanonymous

smtpd_sasl_local_domain =

broken_sasl_auth_clients = yes

Restart Postfix, I think you have to actually restart postfix for this? Reload didn’t seem to work for me. To test, you’ll need to telnet to Postfix and issue some commands, but since some of these are in base 64, you’ll need to generate the command and copy the resultes. Enter this exactly like it is below, but with your username and password! Don’t leave out the 0’s!!
perl -MMIME::Base64 -e ‘print encode_base64(“username\0username\0password”);’

It should return a string like this:
dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=

Now telnet to Postfix and enter the following case sensitive commands :
telnet tobias.local 25
EHLO tobias.local

Now add your base64 encoded string after AUTH PLAIN:
AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=

You SHOULD see 235 Authentication successful.
Enter “quit” to exit the telnet session.

Your test should look something like this:

telnet localhost 25
Trying 123.123.123.123...
Connected to somedomai.net.
Escape character is '^]'.
220 somedomain.net ESMTP Postfix
EHLO fatofthelan.com
250-somedomain.net
250-PIPELINING
250-SIZE
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME
AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=
235 Authentication successful
quit
221 Bye
Connection closed by foreign host.

If this isn’t working go back and double check your settings.

Next we’ll be installing certificates and tls support to encrypt user information.

SETTING UP TLS

As stated previously, we’ll be using the PLAIN or LOGIN mechanisms for SMTP AUTH, so usernames and passwords are sent over the internet in plaintext. To keep anyone sniffing our traffic from seeing our credentials, we need to encrypt our traffic with TLS. Again we’ll be using the default certificates that were installed with Postfix and you will need to replace for production service.
Once you have everything working you’ll want to ensure that authentication will only happen encrypted by adding this statement to your /etc/postfix/main.cf in the TLS Parameters:

smtpd_tls_auth_only = yes

And in your /etc/postfix/master.cf file, find and uncomment the following stanza which allows for TLS and SMTPS:

smtps     inet  n       -       -       -       -       smtpd

-o smtpd_tls_wrappermode=yes

-o smtpd_sasl_auth_enable=yes

-o smtpd_client_restrictions=permit_sasl_authenticated,reject
# Uncomment for smtps on port 587

# 587       inet  n       -       n       -       -       smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes

Reload Postfix. If you telnet to Postfix and issue the EHLO domain.tld command, you should now also see the “250-STARTTLS” line, meaning that Postfix is now taking requests via TLS.

SPAM ASSASSIN
Spam Assassin will check inbound email and try to determine if it is spam. Maildrop will send email to Spam Assassin’s daemon, spamc, via each user’s .mailfilter file and look for a spam tag to decide where to put the message. If it is marked as spam we will put it into the Junkmail folder instead of the inbox. The reason for this because spam filtering is far from perfect. I want to tag the suspected spam as such and let the end user make the final call. To enable the Spam Assassin daemon to run you will need to edit /etc/defaults/spamassassin to enable it:

ENABLED=1

And if you would like it to auto update it’s rules set this:

CRON=1

Again, the user’s .mailfilter file is the mechanism that feeds the mail to spamc, so be sure the /etc/skel/.mailfilter as well as each user’s contain at least the following:

if ( /^X-Spam-Flag: YES/ )
{
log "------------------------------------------------------------- Spam general. "
to "Maildir/.Junkmail"
}

See the appendix for an example .mailfilter file with some extra options.
To train Spam Assassin to become more accurate you should move misidentified email to the appropriate junkmail subfolders and the handle_SA_misjudgements.sh move the email to the proper place and train the system how to be smarter. You will need to add the following script to /usr/bin and add a reference to each user’s crontab. Here’s the script:
vi /usr/bin/handle_SA_misjudgements.sh

#!/bin/bash

#

# $Id: handle_bogofilter_misjudgements.sh,v 1.1 2003/07/14 17:37:53 ssm Exp $

#

## Small shell script to complement bogofilter in my .procmailrc file

## Stig Sandbeck Mathisen <ssm@fnord.no>, 2003-06-02

## Modified by Jason Laster <jason@fasterlaster.com>, 2004-06-23
## Program paths

SA_LEARN=/usr/bin/sa-learn

SPAMFILES=$HOME/Maildir/.Junkmail.mark_as_spam

GOODFILES=$HOME/Maildir/.Junkmail.mark_as_good

MAILER=/usr/bin/maildrop
## Get options

while getopts npv flag

do

case $flag in

p)      false_positives=$(find $GOODFILES/{cur,new} -type f);;

n)      false_negatives=$(find $SPAMFILES/{cur,new} -type f);;

v)      verbose=true;;

?)      echo "Usage: $0 -vpn"; exit 2;;

esac

done
# Handle false positives (mail misidentified as spam)

for mail_file in ${false_positives}

do

$SA_LEARN --ham --no-sync  "${mail_file}" &&

${MAILER} < "${mail_file}" &&

rm -f "${mail_file}" &&

if [ -n "${verbose}" ]

then

echo "${mail_file} marked as good, and mailed via maildrop"

fi

done
# Handle false negatives (spam misidentifed as mail)

for mail_file in ${false_negatives}

do

$SA_LEARN --spam --no-sync  "${mail_file}" &&

rm -f "${mail_file}" &&

if [ -n "${verbose}" ]

then

echo "${mail_file} marked as spam, and removed"

fi

done

Here is the line to add via crontab (crontab -e -u <user>):
# this will reclassify spam and ham
00 06 * * 1-7 /usr/bin/handle_SA_misjudgements.sh -pn > /dev/null 2>&1

Appendix A main.cf
Add later
Appendix B .mailfilter
logfile "/var/log/maildrop"

VERBOSE="5"

log "========"
if (    /^X-Spam-Flag: YES/                   \ # Watch out for header line added by Spamassassin.

)

{

log "------------------------------------------------------------- Spam general. "

to "Maildir/.Junkmail"

#DELTAG=1

}

#if (    /^List-Id: General discussion list about AMaViS <;amavis-user.lists.sourceforge.net>;/

)

# {

#  log "------------------------------------------------------------- amavis. "

#    to "Maildir/.MailingLists.Amavis"

#DELTAG=1

#}

# To send a copy elsewhere...

#if (    (/someuser@somedomain.com/ ) \

#   )

#{

#       cc "! user1@somedomain.com"

#       cc "! user2@somedomain.com"

#}