How To Install Postfix, Amavis, ClamAV, and Spamassassin on Debian Linux

Version 1.4 Change log
NOTE: This article is depricated! See the new version for Debian Etch
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 Sarge, so some of the latest additions to this How-To will not be tested for Woody. For more info 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’m running on Sarge (Testing), so if you’re running Stable you’ll need to change some of your apt settings. You’ll have to grab a few packages out of testing to for this install. If you’re running testing you can skip this section.
Add the following to your /etc/apt/sources.list:

deb-src http://debian.oregonstate.edu/debian testing main non-free contrib
deb http://non-us.debian.org/debian-non-US testing/non-US main contrib non-free
deb-src http://non-us.debian.org/debian-non-US testing/non-US main contrib non-free

and to /etc/apt/apt.conf:

APT::Default-Release “stable”;
APT::Cache-Limit 12582912;

What this does is say “pin all packages at stable”

Now run:
apt-get -t “testing” install postfix spamassassin amavisd-new clamav clamav-daemon libmailtools-perl fam libnet-dns-perl

Or if you’re already running on Testing run:
apt-get install postfix spamassassin amavisd-new clamav clamav-daemon libmailtools-perl fam libnet-dns-perl

Say yes if it asks you to remove exim or whatever MTA you may have installed.
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.
apt-get install courier-maildrop

If you want to stick to mbox, you can install Procmail for you delivery agent. I’ll only be using IMAP/IMAPS for MUA connections, I don’t POP. To install Courier’s IMAP and IMAPS servers, issue the following command:
apt-get install courier-imap courier-imap-ssl

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
To make our sub folders run:
maildirmake -f Sent Maildir
maildirmake -f Drafts Maildir
maildirmake -f Templates Maildir
maildirmake -f Junkmail Maildir

Now repeat for /etc/skel. If your existing users are in the mbox 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/

Don’t forget the trailing slash, (“/”). Note to Debian users: the install script for some reason adds an extra comma before localhost on the mydestination line. Typo?

Test out your email and make sure everything works thus far. If all is working properly, bring Maildrop online by finding (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/Maildir/new/ directory.
Now create the file /etc/courier/maildroprc (Appendix B). This will give us global filtering options. Users can customize their own by adding a .mailfilter file in their home directories. To add logging for Maildrop, create the log file and add the directive in maildroprc.
touch /var/log/maildrop
chown root.adm /var/log/maildrop
chmod 642 /var/log/maildrop
echo logfile “/var/log/maildrop” > maildroprc
echo VERBOSE=”5″ >> maildroprc
echo log “========” >> maildroprc

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. Create a new file in the /etc/logrotate.d directory called maildrop, and add the following:
/var/log/maildrop {
weekly
missingok
}

Since I want to use IMAPS and have installed courier-imap-ssl, I need to set up a proper certificate. IMAP over SSL requires a valid, signed, X.509 certificate and one was generated during the install, but it’s bogus. The command “mkimapdcert” generates a self-signed X.509 certificate. To prevent an unfortunate accident, mkimapdcert will not work if imapd.pem already exists, so we’ll make a backup of it and make a new one. In /etc/courier copy imapd.cnf to imapd.cnf.bak, and move imapd.pem to imapd.pem.bak. Edit imapd.cnf to suit you needs. Then to generate the new IMAPS certificate run:
mkimapdcert
Your new imapd.pem must be owned by the daemon user and have no group or world permissions. The mkimapdcert command will enforce this.
You should now have a new imapd.pem file. Restart courier to see the changes.

To run Amavis, you’ll have to setup your amavis.conf file. I suggest reading the Postfix install doc for Amavis, /usr/share/doc/amavisd-new/README.postfix.gz
Amavis is extremely configurable and robust and you should take the time to read the comments to see what fits your needs best. Basically, Amavis is a wrapper that will send each mail through ClamAV and Spamassassin. What I’m going to be doing here is have it check for viruses and spam. If the message is determined to be a virus, it will be deleted, and if it is found to be spam, it will have a flag added to its headers saying that is indeed spam (tag and PASS). 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. Maildrop will look for the spam flag in the headers of the message and if present, will move the message to the Junkmail folder automatically.

Open /etc/amavis/amavisd.conf, locate and change the “$mydomain” section to your domain.
Locate and uncomment these lines to have Amavis work with Postfix:
$forward_method = ‘smtp:127.0.0.1:10025’; # where to forward checked mail
$notify_method = $forward_method; # where to submit notifications

Locate and comment out this line:
#@bypass_spam_checks_acl

To have Spamassassin add it’s header tags to mail, find the @local_domains_acl = lines and change them to the following (don’t forget the period in the parenthisis!):
@local_domains_acl = qw( “.$mydomain” ); # you may want to use qw() to check all in and out
$sa_tag_level_deflt = -999; # add spam info headers if at, or above that level
$sa_tag2_level_deflt = 5.0; # add ‘spam detected’ headers at that level
$sa_kill_level_deflt = 999; # triggers spam evasive actions

Tell Amavis how to handle spam and viruses. Although the RFC says you should let the sender know you’re killing thier mail, it is a waste of bandwidth, as most virus/spam mail is spoofed, so locate and edit 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’re tagging and passing)

I have all mail related messages go to an account called “postmaster”. I want all messages coming to the postmaster account regardless of spam or virus content, so find and change the following lines:
$spam_admin = “postmaster\@$mydomain”;
$spam_lovers{lc(“postmaster\@$mydomain”)} = 1;
$virus_admin = “postmaster\@$mydomain”;

I don’t want to quarantine virused email, and spam will be delivered to the users Junkmail folder, so I locate and changed the following:
$QUARANTINEDIR = undef;
$virus_quarantine_to = undef;
#$spam_quarantine_to = ‘spam-quarantine’;

Other related changes:
$remove_existing_spam_headers = 0;
$sa_local_tests_only = 0; # (default: false)
$SYSLOG_LEVEL = ‘mail.info’;
$log_level = 2;

Be sure to create the /var/log/amavis.log file and chown it to amavis.

If you want Amavis to knock out emails with certain extensions tailor the $banned_filename_re to meet your needs.
Uncomment this:
$spam_lovers{lc(“postmaster\@$mydomain”)} = 1;

To have Amavis pass email to Clam antivirus, comment out all of the antivirus stanzas except for the two clamav stanzas. Also the 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 #(or whatever username you decided to use)
$ /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 disable_dns_lookups=yes

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_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-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

Now add this to your /etc/postfix/main.cf:
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.

Now open /etc/courier/maildroprc and add:
if ( /^X-Spam-Flag: YES/ )
{
log “————————————————————- Spam general. ”
to “Maildir/.Junkmail”
}

This should grab mail tagged by Spamassassin and put it in the user’s Junkmail folder.

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 a 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.

First we’ll have to install the Postfix ssl patch.
apt-get install postfix-tls

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
apt-get install sasl2-bin

To have the the sasl authentication daemon listening where Postfix will be looking for it, we’ll need to edit the init script for saslauthd. Open the /etc/init.d/saslauthd file in your favorite editor and enter the following line in the header (under PWDIR):
PARAMS=”-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.
tsunami:/etc/init.d# mkdir /var/spool/postfix/var/
tsunami:/etc/init.d# mkdir /var/spool/postfix/var/run/
tsunami:/etc/init.d# mkdir /var/spool/postfix/var/run/saslauthd
tsunami:/etc/init.d# chown -R root:sasl /var/spool/postfix/var/
tsunami:/etc/init.d# adduser postfix sasl

To enable saslauthd to start, edit the /etc/default/saslauthd file and add this:
START=yes
MECHANISMS=”pam”

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

You should see:
root 6143 0.0 0.2 5916 1432 ? S 08:53 0:00 /usr/sbin/saslauthd -m /var/spool/postfix/var/run/saslauthd -a pam

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. So if you haven’t already, apt-get install postfix-tls.
To build our certificates we will be using the script /usr/lib/ssl/misc/CA.pl. First we’ll need to create our certificate authority, or CA, so change to the /usr/lib/ssl/misc/ directory and issue the command:
./CA.pl –newca
You’ll be asked some questions about your organization, be sure to answer the line, “Common Name (eg, your name or your server’s hostname):” with the FQDN of your mail server, i.e. mail.yourdomain.com.
Next we’ll create the server cert request that we’ll sign with our new CA. Issue the following command and answer the questions presented:
./CA.pl –newreq-nodes
All that is left is to sign the server certificate request with our new CA with the following command (Answer yes to both questions):
./CA.pl –sign
Make a new directory to hold your new certificates in /etc/postfix called ssl, and copy you new certificates into it:
mkdir /etc/postfix/ssl:
tsunami:/usr/lib/ssl/misc# mkdir /etc/postfix/ssl
tsunami:/usr/lib/ssl/misc# cp newcert.pem /etc/postfix/ssl/
tsunami:/usr/lib/ssl/misc# cp newreq.pem /etc/postfix/ssl/
tsunami:/usr/lib/ssl/misc# cp demoCA/cacert.pem /etc/postfix/ssl/

Be sure to chmod all of your certs to read only and chown them all to root!

Now all that is left is to tell Postfix to use TLS. Add the following stanza to your /etc/postfix/main.cf file:

smtpd_use_tls = yes
#smtpd_tls_auth_only = yes # You should uncomment to prevent people from passing their passwords in the clear!
smtpd_tls_key_file = /etc/postfix/ssl/newreq.pem
smtpd_tls_cert_file = /etc/postfix/ssl/newcert.pem
smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom

And in your /etc/postfix/master.cf file, find and uncomment the following stanza:

# only used by postfix-tls
tlsmgr fifo – – n 300 1 tlsmgr
smtps inet n – n – – smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
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.

Appendix A main.cf
biff = no
broken_sasl_auth_clients = yes
command_directory = /usr/sbin
config_directory = /etc/postfix
content_filter = smtp-amavis:[127.0.0.1]:10024
daemon_directory = /usr/lib/postfix
default_process_limit = 512
home_mailbox = Maildir/
local_destination_recipient_limit = 300
mailbox_command = /usr/bin/maildrop
mailbox_size_limit = 0
message_size_limit = 0
mydestination = mail.tobias.local, tsunami.tobias.local, tobias.local, localhost.tobias.local, localhost
mydomain = tobias.local
myhostname = tsunami.tobias.local
mynetworks = 127.0.0.0/8
myorigin = /etc/mailname
program_directory = /usr/lib/postfix
recipient_delimiter = +
relay_domains = tobias.local
setgid_group = postdrop
smtpd_banner = $myhostname ESMTP $mail_name
smtpd_helo_required = no
smtpd_recipient_restrictions = permit_mynetworks, check_relay_domains, permit_sasl_authenticated
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = 
smtpd_sasl_security_options = noanonymous
smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem
smtpd_tls_cert_file = /etc/postfix/ssl/newcert.pem
smtpd_tls_key_file = /etc/postfix/ssl/newreq.pem
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
smtpd_use_tls = yes
tls_random_source = dev:/dev/urandom
transport_maps = hash:/etc/postfix/transport
Appendix B maildroprc
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
  }