*Note: An updated and much easier to read version of this guide is available on the “ev-15 wiki”:http://wiki.ev-15.com/debian:mail_system.*

To everyone who has been clamoring for an update, you got what you wished for.

Be careful what you wish for.

Introduction
I wanted to run a complete mail server for myself and any of my friends or family who would rather have a regular mail account rather than one from one of the free web based services. I decided to encrypt and authenticate connections for both sending and receiving mail, and to handle receiving mail with IMAP, rather than POP. The final setup can handle large amounts of both incoming and outgoing mail and is easy to use.

Accounts
I had two basic options when selecting how to handle accounts. Either every valid mail account could be tied to a valid user account on the mail server, or I could set up a backend such as an SQL or LDAP database to handle authentication that wouldn’t be tied to the server.

Based on my needs and relatively small number of potential users, I decided to go with mail delivery to local user accounts, while retaining support for virtual domains. This way I can support any domain with an MX record pointing to the mail server, and not just my own domain, but mail will be delivered to and authenticated against local user accounts.

Software
Since I have a host on the internet that is running Debian Sarge (Testing), I decided to use that as the host system, and tailored the rest of the system around that. All packages in use except Squirrelmail are straight out of the Debian package management system.

The other software used is:
* Postfix (MTA), and TLS patch.
* Spamassassin (Spam Filter)
* Cyrus-SASL (Authentication)
* Cyrus-IMAP (IMAP and SIEVE server)
* Apache (For webmail)
* Squirrelmail (Webmail system)
* Plugins: AvelSIEVE (SIEVE script creator for Squirrelmail)

Before We Begin
I started by adding a couple of sarge mirrors to /etc/atp/sources.list. The specific servers might change but that isn’t important. Before installing software make sure the current system is up to date:

server# apt-get update
server# apt-get upgrade

Installing Software
Now everything should be ready to go. The default MTA on debian, Exim, should be uninstalled by apt automatically when I install postfix. Everything else should be new software.

Start with postfix:

server# apt-get install postfix postfix-tls

This installs a postfix system that is ready to be setup for TLS (SSL) encrypted connections. I chose reasonable defaults to the questions the debian installer asks. Anything that needs to be changed can be changed later without much trouble. At this point local delivery probably works, but the system is far from complete. I didn’t worry about it at this point, since the way the system will be set up I also need to tie postfix into SASL and IMAP, and probably send it to Spamassassin for filtering as well.

Next I install all the needed packages from project Cyrus, namely the SASL library, the Cyrus admin and client programs, and the Cyrus IMAP daemon. Although it is available, I will not install the corresponding Cyrus POP3 server. This is an IMAP only setup.

server# apt-get install libsasl2 libsasl2-modules sasl2-bin
server# apt-get install cyrus21-admin cyrus21-clients cyrus21-common cyrus21-imapd

I also want to do spam filtering with Spamassassin, so let’s grab that package as well.

server# apt-get install spamassassin spamc

Later on, when I get to SIEVE script creation and webmail options, I’ll cover Apache and Squirrelmail. For now, and to get the server working the way we want it, everything is ready to go.

Configuring Software
Now that our main set of software is installed, we get to configure it so that everything works together.

I started with configuring Cyrus-SASL for authentication in general. Both our SMTP server, postfix, and our IMAP server, Cyrus-IMAP, will authenticate using another piece of daemon software, saslauthd. Saslauthd will handle the actual authentication of users. With this in mind, I’ll start first by configuring SASL to authenticate against the system accounts. This is done using PAM.

First, configure the daemon by editing /etc/default/saslauthd
The two lines that need to be uncommented for this system are
START=yes
MECHANISMS=”pam”

If you want to use another authentication mechanism, this is where you would set it. My system authenticates against user accounts, so I’ll use pam as my method.

I want to test the daemon quickly to make sure that it authenticates properly, so let’s start it up.

server# /etc/init.d/saslauthd start

Now test it using a known working user account.

server# testsaslauthd -u username -p password

If it works, you should see
0: OK “Success.”
as a result. If so, saslauthd can talk to pam and authenticate users. Now shut it back down

server# /etc/init.d/saslauthd stop

and edit the file /etc/init.d/saslauthd. Add the line

PARAMS=”-m /var/spool/postfix/var/run/saslauthd”

to the various variable declarations near the top of the file. Postfix runs in a chroot so we end up moving the actual authd location inside this chroot. Now to keep everything else running, we’ll make a link to where other programs will expect the location of the mux.

server# rmdir /var/run/saslauthd
server# ln -s /var/spool/postfix/var/run/saslauthd /var/run/saslauthd

Now start the daemon back up and things should be ok.

The next step is to plug postfix into this. Edit /etc/postfix/main.cf and add the following to the end of the file

#SASL Auth Settings
smtpd_sasl_local_domain =
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

Also edit /etc/postfix/sasl/smtpd.conf and put in the lines
pwcheck_method: saslauthd
mech_list: PLAIN LOGIN

At this point you can choose to reload postfix and test if auth is at the very least enabled.

server# /etc/init.d/postfix reload
server# telnet localhost 25
Trying 127.0.0.1…
Connected to localhost.
Escape character is ‘^]’.
220 dev.ev-15.com ESMTP Postfix (Debian/GNU)

Type in
EHLO domain.com

If you get something like this:
250-mail.domain.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME

Then auth is at least enabled in the mail server.

Postfix should now tie authentication into itself, using saslauthd. You’ll note that the methods we set up were only PLAIN and LOGIN, both of which are plain text based. To enable one of the shared secret methods, such as CRAM-MD5 or DIGEST-MD5, a different authentication method would need to be used. To avoid delivering these plain passwords over the internet, we’ll wrap everything up in an encrypted layer using TLS.

Before we get too far with this, to use TLS or SSL we need certificates. There are generally two classes of certificates. If you need implicitly trusted certificates that you can use and let your customers be comfortable with, you’ll probably want to go to a CA and purchase them. If you’re like me on the other hand, and trust yourself, you can create self-signed certificates pretty easily. They will throw up warnings in mail clients, but if you trust yourself the certificates can usually be permanently accepted.

My main goal here is encryption, and I’m not too worried about the trust issues, so let’s get on to creating the files.

Start with making a new CA
server# cd /usr/lib/ssl/misc
server# ./CA.pl -newca
Answer the questions as they come with reasonable information. The value for CN (Common Name) should be the hostname of your server.

Now make the server certificate request.
server# ./CA.pl -newreq-nodes

Now sign it
server# ./CA.pl -sign

Good, you have your required SSL/TLS certificate files. Copy the files to /etc/ssl/certs
server# cp newcert.pem /etc/ssl/certs/
server# cp newreq.pem /etc/ssl/certs/
server# cp demoCA/cacert.pem /etc/ssl/certs/

Now add the SSL/TLS information to /etc/postfix/main.cf
#TLS(SSL) Information
smtpd_use_tls = yes
#smtpd_tls_auth_only = yes
smtpd_tls_key_file = /etc/ssl/certs/newreq.pem
smtpd_tls_cert_file = /etc/ssl/certs/newcert.pem
smtpd_tls_CAfile = /etc/ssl/certs/cacert.pem
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom

Also go into /etc/postfix/master.cf and uncomment the following lines:
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

This will enable the standard secure smtpd port in addition to port 25.

Later we’ll uncomment the line server#smtpd_tls_auth_only = yes so that users are required to encrypt their connections. For now we’ll leave it alone. To test, telnet in again, as above, and see if the line “250-STARTTLS” is there. If so you should be good to go on TLS connections. Now let’s get IMAP set up.

IMAP Setup
I’m using Cyrus-IMAP, largely because it is high performance and I prever SIEVE to maildrop scripts. One thing to note about this package is that it takes the “black box” approach to IMAP mail delivery. Mail is never delivered to a user’s home folder, but rather to a set of dedicated cyrus folders. If you would rather mail either be delivered to maildirs or mbox files, you would be better off using Courier- or UW-IMAP servers. If you’re like me, however, and only read mail over IMAP (with a lot of graphical mail clients, mutt, or Squirrelmail), Cyrus is a better bet. SIEVE scripts are fantastic, too, and later we’ll hook an easy way to create them into Squirrelmail.

Let’s first set what programs cyrus will be running. Open the file /etc/cyrus.conf for editing. I usually go with regular IMAP, secure IMAP, and SIEVE. It might not hurt to turn off regular IMAP if you want to force SSL on all connections. Under the SERVICES section, uncomment the line
imaps cmd=”imapd -s -U 30″ listen=”imaps” prefork=0 maxchild=100
and comment out the pop3 line, since we are going for IMAP only.

For LMTP I stick with the socket. The line for sieve should already be uncommented, but check it just to be sure. Everything else in the file can stay the way it was.

Now open the file /etc/imapd.conf. Most of the settings can be left the way they are, but we have a few more to set. As always, read the comments for the various settings in the file. I usually uncomment forcing lowercase recipients. sieveusehomedir should be set to false so that our remote sieve management will work.

For my setup i make sure that the following are set:
admins: cyrus
allowplaintext: yes
sasl_mech_list: PLAIN
sasl_pwcheck_method: saslauthd

Then I set up the SSL information
tls_cert_file: /etc/ssl/certs/newcert.pem
tls_key_file: /etc/ssl/certs/newreq.pem
tls_ca_file: /etc/ssl/certs/cacert.pem
tls_ca_path: /etc/ssl/certs

And leave the rest of the file alone. We should actually be almost done with the initial setup now.

Restart cyrus
server# /etc/init.d/cyrus21 restart

To do administrataion of the cyrus server we’ll need to be logged on as the user cyrus, so set a password for cyrus and then su to the cyrus user.
server# passwd cyrus
server# su cyrus

Now add an IMAP user. Pretty soon here we’ll be able to test the system. Usernames are prefaced with user., so to create my account I would use the command cm user.scblock.
Log into the cyrus admin tool and create a new mail user to match a local delivery name that postfix knows. Do this for each local account that receives mail, or postfix will throw errors.
$ cyradm localhost
cyradm> cm user.username
cyradm> quit

-This section is depreciated-
Now we have a new, working IMAP user. This user can probably log into IMAP right now without any trouble. They won’t get any mail, however, until we link postfix to cyrus. Let’s do that now. Edit the file /etc/postfix/master.cf and add the following line to the bottom section:
cyrus unix – n n – – pipe
flags= user=cyrus argv=/usr/sbin/cyrdeliver -e -r ${sender} -m ${extension} ${user}
If you break this string up over more than one line be sure to start trailing lines with whitespace.

Now edit /etc/postfix/main.cf and add the line:
mailbox_transport = cyrus
-End depreciated-

Make sure that a line similar to
lmtp unix cmd=”lmtpd” listen=”/var/run/cyrus/socket/lmtp”
is uncommented in /etc/cyrus.conf, then edit /etc/postfix/main.cf and add the line
mailbox_transport = lmtp:unix:/var/run/cyrus/socket/lmtp

Create the lmtp group and add postfix to that group

server# addgroup lmtp
server# adduser postfix lmtp

Now fix the socket directory permissions and restart both mail servers
server# dpkg-statoverride –force –update –add cyrus lmtp 750 /var/run/cyrus/socket
server# /etc/init.d/postfix restart
server# /etc/init.d/cyrus21 restart

Tail the log file when you send a new mail to make sure things are going OK.

Cyrus should now be linked to postfix. All mail from postfix will be handed off to the Cyrus server for delivery. This will fail if postfix tries to deliver mail for a user Cyrus doesn’t know about, so make sure that when you add new users to the mail system that you also create a mailbox for them with cyradm.

At this point the major steps have been done, and mail delivery should work as-is. Let’s go over a few more things with the postfix configuration, and then make sure the mail setup is working.

I have mail for more than one domain going to this mail server, so I want to add the second domain to postfix. Based on the guide at http://www.postfix.org/VIRTUAL_README.html, I created the file /etc/postfix/virtual and populated it with entries like the following:
user1@domain1.com  realuser1
user2@domain1.com  realuser2
user3@domain2.com  realuser1

If you want all mail for a domain to go to a single user, put a line like
@domain1.com  realuser
in the file.

The left side maps to the email address people will send mail to, and the right side maps to the local system account that mail will be delivered to (through Cyrus IMAP). Once this file has been created, it needs to be hashed for postfix to use, so run the postmap command to do this

server# postmap /etc/postfix/virtual

Now edit /etc/postfix/main.cf again and add the following lines
#Virtual Domain Settings
virtual_alias_domains = domain1.com, domain2.com
virtual_alias_maps = hash:/etc/postfix/virtual

This tells postfix which domains to deliver to, and what file to use to check where to deliver the mail. It’s pretty simple to get going. Reload postfix again and things should work.

In fact, if you have a regular user on the system and have added their mailbox with cyradm, you should be ready to log in to both send and receive mail. If you get any errors check your config files again carefully. Tailing the output of /var/log/mail.log can be extremely helpful when tracking down problems. One issue I have had is that the location postfix looks for to check against saslauthd moves around seemingly at random, from a chroot style path to the regular path (/var/spool/postfix/var/run/saslauthd vs. /var/run/saslauthd), and I can’t figure it out, so I start one copy of saslauthd with the init script and another using the -m command to locate another mux file in the postfix root. This should be taken care of properly, but works for now.

EDITED: symlink to /var/spool/postfix/var/run/saslauthd to /var/run/saslauthd seems to take care of things.

EDITED AGAIN: this symbolic link will not persist across reboots, resulting in failure of cyrus imap to authenticate. The steps to correct this follow:
==
# dpkg-statoverride –remove /var/run/saslauthd
# dpkg-statoverride –add –update root sasl 710 /var/spool/postfix/var/run/saslauthd
==

Now edit /etc/init.d/saslauthd. Change PWDIR to /var/spool/postfix/var/run/saslauthd, and PIDFILE to “/var/spool/postfix/var/run/${NAME}/saslauthd.pid”. Then go down to the start) case and add the lines
rm -rf /var/run/saslauthd
ln -s /var/spool/postfix/var/run/saslauthd /var/run/saslauthd
after ‘test -z “$dir” || createdir $dir’.

Everything should now come up properly after reboot.

If mail works in both the outgoing and incoming directions, then you are a hero and should go congratulate yourself with a beer. It took me forever to get this far the first time.

More Fun
We have a working mail system now, but we can have even more fun with it. Let’s start with spam. We all hate spam, so why not filter it? We’ll use spamassassin to do it. Let’s get it going.

Spam Filtering
We installed the package earlier, so now let’s enable the daemon and start it up. Edit the file /etc/default/spamassassin and change ENABLED=0 to ENABLED=1, and then start the daemon up.

server# /etc/init.d/spamassassin start

Now let’s pipe all the mail that comes into postfix through spamassassin. Edit /etc/postfix/master.cf again and add the line
spamassassin
unix – n n – – pipe user=filter argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
to the bottom of the file. Go back up near the top and change the smtp line from
smtp inet n – – – – smtpd
to
smtp inet n – – – – smtpd -o content_filter=spamassassin

You’ll notice that we’re using the user “filter” here, so add the user to your system with the adduser script. Otherwise postfix will complain and no mail will be delivered. We’ll be able to safely disable the login of filter once everything is working.

The only problem with this setup is that if spamd dies mail won’t be delivered, but we’ll worry about that at another time. Run a test mail to the system to see if spam related headers are being added. If so, you’re good to go on the spam filtering backend, and can move on to webmail.

Webmail
While I don’t like webmail-only services, I do like having webmail access to my mail accounts when I don’t have an IMAP client handy, so let’s set up apache and squirrelmail.

Apache comes from the Debian package system again.

server# apt-get install apache apache-ssl libapache-mod-php4

This will get and install an ssl-enabled Apache server with php support. Give reasonable answers to the configuration dialogs.

Now edit /etc/apache/httpd.conf to your liking. I left almost everything except webmaster and server names settings alone. Do the same for /etc/apache-ssl/httpd.conf. You should be able to connect to your server by a regular or encrypted connection at this point.

Download the latest stable version of squirrelmail from http://www.squirrelmail.org and untar it somewhere in your server root (I chose /var/www/mail, which corresponds to http://mail.server.com/mail). Move into the folder and run the configure script as root. This will set up your squirrelmail options, which are quite straightforward. It’s really just an IMAP client. If you want secure IMAP or SMTP, the ports you want are 993 and 465, respectively.

See if you can log in and check your mail. If so, you’re good to go.

SIEVE Filters
SIEVE is a simple, yet powerful way to filter mail on the server side. In this setup it is part of the Cyrus IMAP server package, and runs on port 2000. I waited this long to introduce it, because I find the nicest way to build sieve scripts is with the AvelSIEVE plugin for Squirrelmail. First download the plugin from the squirrelmail site and untar in in the plugins folder. Copy the sample configuration to the real file and edit it to your configuration. If you’re like me you didn’t have to change anything.

server# cp config_sample.php config.php

Now run the squirrelmail configure script again and select plugins to view the list of available plugins. Type the number of the avelsieve plugin to enable it. Save your prefs and exit, and go back to your squirrelmail web page to test it.

Click on the new filters link on the top bar. If you get an error about connecting, check /etc/cyrus.conf. If you get a connection refused error test sieve using sivtest. If you can connect to localhost but not your machines dns name, you’ll have to edit that config file to fix the problem. I removed the “localhost” parameter from the sieve init line.

If you get a dialog that will let you add a new rule, congratulations. You’re set. I usually create rules based on the X-Spam-Flag header to move mail to INBOX.Junk, and a few others to move mailing lists to their respective folders. Always make sure you save your rules before you leave the page though, or they will not take effect.

The nice thing about the sieve filters is that even though I set them up through squirrelmail, they run at the IMAP server level, and all mail you get through IMAP is filtered according to your rules.

Well, that’s it. You should have a working mail server that is fully authenticated and encrypted. Congratulations. If you want to adjust other things, there is a lot of documentation available on the net if you’re willing to search. I plan to eventually hook a virus filter up to my postfix chain, but that is another project for another day.

 

15 Responses to “Installing and configuring a mail system for Debian Sarge”

  1. You know…strange as it is, this is inspiring and tempting me to set a mail server up myself. I’ve never actually gotten one running to my expectations.

  2. sam says:

    mail server fuck yeah

  3. Courtney says:

    I like kitties.

  4. Ross says:

    didn’t read

  5. Liz says:

    cliffs notes plz?

  6. granx says:

    STEVE: awesome lesson, I might bum from you or just use this tutorial for my own. I think you should remember, for the future, that people assume you are making up for inadequacies when you write a really long blog.

  7. Ismet Kursunoglu, MD says:

    Great tutorial, thank you. I want to do this but use Openldap (slapd) for user mangement/login via sasl. I have tried using some of the config help from

    http://hannibal.solstice.nl/version-2.0/on_debian/hannibal-3.0_on-debian-sarge_2004-04-02.html.orig
    but am now pretty confused on how to properly tie in the ldap portion. They also use Amavis and the clamav filters. Of course their examples include lots of other functionality that I am not going to be using.

    I plan on adding lots of users and don’t want to create local user accounts. I had looked in MySQL with Courier, but gave up after so many days.

    I like the idea of starting out with Ldap given it’s powerful adaptability and scale.

    Any ideas or pointers? Could I interest you in taking the plunge with an additional tutorial that covers some thing like this – i.e. running on Sarge.

    thanks again.

  8. Mike says:

    This is exactly what I’ve been looking for, but I’m having a problem. Right after this config change:

    server# rmdir /var/run/saslauthd
    server# ln -s /var/spool/postfix/var/run/saslauthd /var/run/saslauthd

    When I try to restart the sasl daemon, I get the following error:

    mkdir: cannot create directory `/var/run/saslauthd’: File exists

    Any help with this would be GREATLY appreciated.

  9. Steve says:

    Mike,

    I’ve figured out what I feel is a much easier way to deal with this issue.
    I created a bind mount between /var/run/saslauthd and
    /var/spool/postfix/var/run/saslauthd by adding the following line to
    /etc/fstab and creating the /var/spool/postfix/var/run/saslauthd
    directory:

    /var/run/saslauthd /var/spool/postfix/var/run/saslauthd none rw,bind 0 0

    I’m pretty sure I left everything else about saslauthd alone at that
    point. The end result is that the saslauthd directory is located at
    /var/run/sasluthd, the default configuration, and that location is
    mounted inside the postfix chroot as well.

    This is the method I would recommend, rather than mucking about with the
    symbolic links.

    If you’d still like to do it the way I first figured out, you’ll have to
    do a little bit of editing in the init script for saslauthd.

    Look at the much-easier-to-read version of this guide, at
    http://wiki.ev-15.com/debian:mail_system#sasl
    and read starting at “Finish SASL Configuration”

    You’ll see where I mention adding a PARAMS= line to the file, changing
    PWDIR and PIDFILE, and adding a couple lines to force removal of the
    /var/run/saslauthd directory and creation of the symbolic link.

    You’ll also see “Update #2″ where I recommend the bind mount method. I
    just added this today, though I should have added it a while ago.

    Let me know how things go.

  10. gorion says:

    Very nice, I’m impressed and satisfied. Although I woudn’t mix so much with certs as you did. Woudn’t it be easier to make these things using simple three step combo commands with openssl? Anyways, very good work. Greets, Paul.

  11. jan says:

    hey steve,

    great article!

    if it isn’t too much to ask, can you include a mysql lookup instead of systems users.

    TIA

  12. steve says:

    Jan, I did manage to kludge on mysql lookups to the existing system using libpam-mysql to meet expanded needs, but I did a terrible job of documenting the process. I generally didn’t make any changes to postfix, but instead changed smtp and imap in /etc/pam.d according to the configuration instructions for pam-mysql shown in /usr/share/doc/libpam-mysql/Readme

    Users in mysql are in a simple table with the following fields: id, login, hashed_passwd. I used the encrypt() function in mysql, which simply calls the system encrypt. By default encrypt() uses a 2 character salt and generates a DES hash, but if you give an MD5 style salt it will generate an MD5 password hash instead. If you use the builtin md5() function mysql can handle it fine but pam will not.

    I know that postfix can use mysql for a whole host of lookups but I haven’t ever actually configured any of those things. Maybe I’ll try it sometime in the future but I don’t have a non-production machine to play with at the moment.

  13. jan says:

    i don’t have a non production machine either that’s why i can’t play with the configuration, anyways thanks for the info. i’ve bookmarked your blogsite and will visit it often in case you decided to post a new howto (“,)

    more power to you steve and God Bless.

  14. Iulian says:

    After install I can’t send and receive mail.
    I see the following error:
    Jun 29 03:31:32 proline postfix/lmtp[9507]: F33727DD09: to=, relay=none, delay=1, status=deferred (connect to /var/run/cyrus/socket/lmtp[/var/run/cyrus/socket/lmtp]: No such file or directory)

    When I try to send email to gmail,this is the error:
    Jun 25 23:50:57 195 postfix/smtp[5560]: connect to mail-smtp-in.l.google.com[64.233.183.27]: Connection refused (port 25)

    What is wrong ?

Leave a Reply