return to is this even a language?

My mail setup

02 Aug 2013

Modeled after http://www.adomas.org/2006/08/postfix-dovecot/, but updated to Debian Wheezy. It’s mostly a documentation for myself, but if Google brought you here, maybe it’s useful to you?

1. Install postfix

apt-get install postfix heirloom-mailx

With these two installed, try receiving mail at root@FQDN. It should appear in /var/mail/root. Most likely you need to add your FQDN in /etc/postfix/main.cf by updating the mydestination variable to something like:

mydestination = yrden.de, localhost.customer.vlinux.de, localhost

Similarly, try sending mail

echo "test" | mail -s testsubject old_mail_address

on the new server, to see if everything works. I’ve found it to be very helpful to have a tail -f /var/log/mail.log window open, so I can see error messages right as they appear.

2. Create self-signed certificate

openssl req -new -x509 -days 3650 -nodes \
  -out /etc/ssl/certs/mail.yrden.de.pem \
  -keyout /etc/ssl/private/mail.yrden.de.key

Everything is optional, except “Common Name”. It should be the FQDN as announced in the DNS. In my case, that’s mail.yrden.de.

3. TLS on Postfix

Again, edit /etc/postfix/main.cf and modify the following lines. They are present in the default file, but use the snake-oil certificate.

smtpd_tls_cert_file=/etc/ssl/certs/mail.yrden.de.pem
smtpd_tls_key_file=/etc/ssl/private/mail.yrden.de.key

Restart Postfix: postfix reload

4. SMTP auth with Postfix

Setting up SASL

apt-get install sasl2-bin libsasl2-2

Modify /etc/default/saslauthd so that START and OPTIONS settings look like

START=yes
# …
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"

Next, create the appropriate directories

cd /var/spool/postfix/
mkdir -p /var/spool/postfix/var/run/saslauthd
chown -R root.sasl /var/spool/postfix/var/run/saslauthd

and allow postfix to access the saslauthd:

adduser postfix sasl

Restart saslauthd now: service saslauthd restart. It should have created additional sockets in the specified directory. It should look similar to:

$ ps -ef | grep saslauthd
root     24996     1  0 10:03 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root     24997 24996  0 10:03 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root     24999 24996  0 10:03 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root     25000 24996  0 10:03 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root     25001 24996  0 10:03 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 5
root     26174     1  0 10:50 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root     26175 26174  0 10:50 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root     26176 26174  0 10:50 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root     26177 26174  0 10:50 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5
root     26178 26174  0 10:50 ?        00:00:00 /usr/sbin/saslauthd -a pam -c -m /var/spool/postfix/var/run/saslauthd -n 5


$ ls -lh /var/spool/postfix/var/run/saslauthd
total 972K
-rw------- 1 root root    0 2013-07-31 10:50 cache.flock
-rw------- 1 root root 963K 2013-07-31 10:52 cache.mmap
srwxrwxrwx 1 root root    0 2013-07-31 10:50 mux
-rw------- 1 root root    0 2013-07-31 10:50 mux.accept
-rw------- 1 root root    6 2013-07-31 10:50 saslauthd.pid

Setting up Postfix

Directory /etc/postfix/sasl should exist by now. Create a file /etc/postfix/sasl/smtpd.conf with content pwcheck_method: saslauthd:

echo "pwcheck_method: saslauthd" > /etc/postfix/sasl/smtpd.conf

Append the following to /etc/postfix/main.cf and reload postfix:

smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes

smtpd_recipient_restrictions =
   reject_non_fqdn_recipient,
   reject_unknown_recipient_domain,
   permit_sasl_authenticated,
   permit_mynetworks,
   reject_unauth_destination

Testing the setup

It should now be possible to auth yourself against the SMTP server. First, generate the auth string, which we will require in a moment. Replace username and password with an existing one on your server:

perl -MMIME::Base64 -e 'print encode_base64("username\0username\0password");' 
# example:
perl -MMIME::Base64 -e 'print encode_base64("shibe\0shibe\0muchsecurewow");'

Next, try telnet localhost 25. It should look something like this:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 vsrv79318.customer.vlinux.de ESMTP Postfix (Debian/GNU)
EHLO mail.yrden.de                                               ## type this
250-vsrv79318.customer.vlinux.de
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-AUTH DIGEST-MD5 NTLM CRAM-MD5 LOGIN PLAIN
250-AUTH=DIGEST-MD5 NTLM CRAM-MD5 LOGIN PLAIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN <<base64 string created above>>                       ## type this
235 2.7.0 Authentication successful
QUIT                                                             ## type this
221 2.0.0 Bye

If you get a warning in /var/log/mail similar to

warning: SASL authentication failure:
  cannot connect to saslauthd server: Permission denied

you have not added user postfix to the sasl group.

Don’t continue until you receive an “Authentication successful” message. Restart the service and have a look at /var/log/mail.log for hints.

Make misconfiguration harder

By default postfix will also accept connections and auths without any encryption. You can set postfix to ignore such requests, so you will notice if you misconfigured your client. Add the following lines to /etc/postfix/main.cf and set them approrpiately:

smtpd_tls_auth_only = yes
smtpd_tls_security_level = may

The latter is stricter than the former. Setting smtpd_tls_security_level = encrypt implies smtpd_tls_auth_only = yes. According to the Postfix documentation the latter also replaces the obsolete parameters

smtpd_use_tls = yes
smtpd_enforce_tls = yes

(deprecated in Postfix 2.3 and later). Important: in previous versions I had set this to encrypt instead of may. This requires the sending server to support TLS as well. However, certain servers don’t and thus the mail delivery fails. One famous (and sad) example is GitHub.

5. Installing Dovecot

apt-get install dovecot-imapd

You may want to enable some workarounds for broken clients, namely Thunderbird. Do so by adjusting the imap_client_workarounds in /etc/dovecot/conf.d/20-imap.conf to

imap_client_workarounds = tb-extra-mailbox-sep tb-lsub-flags

Next, restart dovecot (service dovecot restart) and see if IMAP works:

mutt -f imap://username@hostname/

Obviously, mutt will complain about certificate errors, but you should be able to see the test mails you sent yourself earlier.

6. SSL in Dovecot

Edit /etc/dovecot/conf.d/10-ssl.conf and update the following lines so that they point to the certificates created earlier. E.g.

ssl_cert = </etc/ssl/certs/mail.yrden.de.pem
ssl_key = </etc/ssl/private/mail.yrden.de.key

Most likely you also want to enforce TLS, so that you can’t misconfigure your client to use plain auth. So, set

ssl = required

in the same file and uncomment

disable_plaintext_auth = yes

in /etc/dovecot/conf.d/10-auth.conf. Note that this will still allow PLAIN to be used, but only over an SSL connection or from local adresses.

7. Changing to Maildirs

I wanted to be able to automatically sort into folders. Maildirs seem to be better equipped for that. So let’s change the format to Maildirs.

The Debian Wiki says to run this command:

postconf -e "home_mailbox = Maildir/"

However you can also simply edit /etc/postfix/main.cf. The mails will now be stored in ~user/Maildir (e.g. /root/Maildir for root). Again, test that you can receive mail properly, either by using mutt -f ~/Maildir or by doing some cat-magic.

If this doesn’t work out of the box, ensure the Maildir directory is created and has the correct permissions. If it doesn’t, change into the user’s home directory and run:

cd ~user
mkdir -p Maildir
mkdir -p Maildir/cur
mkdir -p Maildir/new
mkdir -p Maildir/tmp
chown -R user:user Maildir

Dovecot tries to auto-detect where your mails are stored by default, but in Debian it doesn’t auto-detect Maildirs. Furthermore, it is recommended to set the location manually anyway, so it works for new users as well. See the Dovecot wiki for more information.

To set the location edit /etc/dovecot/conf.d/10-mail.conf, find the mail_location = line and set it to mail_location = maildir:~/Maildir

8. Testing everything in Thunderbird

Now, restart Dovecot and Postfix once more and test the setup in Thunderbird. If you didn’t use any strange names, Thunderbird will be able to auto detect everything.

If you can’t access your inbox the /var/log/mail.log shows something like Error: Opening INBOX failed: Mailbox doesn't exist: INBOX ensure that you have created the Maildir folders and set the mail_location to the correct path in Dovecot’s configuration (see step 7).

Your basic setup is now complete. You should be able to send and receive mails. Everything else that follows are add-ons to your current setup.

9. Aliases

Plussing

Postfix supports “plussing” by default, i.e. mails to user+anything@ will be put into user’s inbox. This is a fairly effective way to sort mail and detect who sends you spam. However, more often than I’d like adress validation schemes do not allow +. Therefore, I changed the character to - by editing recipient_delimiter = in /etc/postfix/main.cfg.

Real Aliases

You might want to have adresses for your first, last and nickname. All should end up in the same inbox. To do so, edit /etc/aliases and add a new line for each aliases you want to have. The format is newalias: existing_inbox_user_name

After you saved the file, run newaliases so that Postfix picks up the changes.

10. Mail filtering with Sieve

Setting up Sieve

apt-get install dovecot-sieve

Enable the plugin by uncommenting and changing this line in

# /etc/dovecot/conf.d/15-lda.conf
mail_plugins = $mail_plugins sieve

Instruct Postfix to hand all mail to Dovecot, so sieve can do its magic. Add this to:

# /etc/postfix/main.cf
mailbox_command = /usr/lib/dovecot/deliver

With these changes, mails to root now need to be directed to a real user. See section 9 for details.

If you changed the recipient delimiter in Postfix, also update:

# /etc/dovecot/conf.d/90-sieve.conf
recipient_delimiter = -

Restart dovecot and postfix: service dovecot restart; service postfix restart

General hints about filters

Tutorials on the file format:

Test your filters by storing a mail with all headers in a file. Next, run this command: sieve-test ~/.dovecot.sieve ~/stored-mail.txt

Example filter to sort away own mail

I’ve setup my clients to send a blind-copy to one of my addresses in order to keep track of what I’ve sent. This usually works better than having the client copy the message to the Sent-folder because it only uses my upstream once.

If you haven’t changed the default configuration, the filters will be put into ~user/.dovecot.sieve and the log will be available at ~user/.dovecot.sieve.log. This section assumes you are logged in as the user. If you work as root, ensure you have the correct permissions and owners on all touched files.

First, ensure the target folder exists: mkdir -p ~/Maildir/.Sent

Next, edit ~/.dovecot.sieve to look like:

require ["fileinto", "imap4flags"];

if address :is "Delivered-To" "MAIL-ADDRESS-YOU-BCC-TO" {
  setflag "\\Seen";
  fileinto "Sent";
  stop;
}

The require line loads plugins needed for this recipe. These are shipped by default, so you don’t need to install additional packages.

“address” is a special command that extracts the mail address from a header (e.g. consider headers like “Stefan Breunig some@mail.address”). So the if matches all mails whose Delivered-To header has the mail address MAIL-ADDRESS-YOU-BCC-TO.

The actions applied to the matched mails are pretty self explanatory:

11. Fail2ban

fail2ban is a daemon that monitors log files for failed logins. If too many occur, that IP address is banned for a certain amount of time. By default an exponential back-off scheme is used. You can read details in /etc/fail2ban/jail.conf, although you should put your configuration into /etc/fail2ban/jail.local.

In my case, /etc/fail2ban/jail.local looks like this:

[ssh]
enabled = true

[postfix]
enabled = true

[sasl]
enabled = true

[dovecot]
enabled = true

12. SpamAssassin

Setting up SpamAssassin

apt-get install spamassassin spamc

Set user, group and permissions:

groupadd spamd
useradd -g spamd -s /bin/false -d /var/log/spamassassin spamd
mkdir /var/log/spamassassin
chown spamd:spamd /var/log/spamassassin

Next, edit /etc/default/spamassassin so that:

ENABLED=1
# …
SAHOME="/var/log/spamassassin/"
OPTIONS="--create-prefs --max-children 2 --username spamd -H ${SAHOME} -s ${SAHOME}spamd.log"

Start SpamAssassin to see if there are any errors in the configuration files: service spamassassin start.

Setting up Postfix

In order to pipe mail through SpamAssassin, simply edit /etc/postfix/main.cf so that:

mailbox_command = /usr/bin/spamc -e /usr/lib/dovecot/deliver

To test SpamAssassin works, send yourself a test mail. It should have several new headers:

X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on
  vsrv79318.customer.vlinux.de
X-Spam-Level:
X-Spam-Status: No, score=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW,SPF_PASS,
  TVD_SPACE_RATIO autolearn=unavailable version=3.3.2

Notice the autolearn=unavailable. The most likely reason is that SpamAssassin can’t write to the user’s home directory to store files. To fix this, run:

mkdir -P ~USER/.spamassassin
chown -R USER:spamd .spamassassin
chmod -R g+w .spamassassin

autolearn should work now and the warning messages in /var/log/spamassassin/spamd.log should be gone.

Training the filter

If you have some spam available move it to a separate folder (e.g. Junk in ~/Maildir/.Junk).

Next, run this for spam:

sa-learn --spam -u spamd --dir /home/USERNAME/Maildir/.Junk/* -D

and this for ham:

sa-learn --ham -u spamd --dir /home/USERNAME/Maildir/.INBOX/* -D

Move spam to junk folder

If you have setup sieve above, you can automatically move spam to the junk folder by using this recipe:

if exists "X-Spam-Flag" {
  if header :contains "X-Spam-Flag" "NO" {
  } else {
    setflag "\\Seen";
    fileinto "Junk";
    stop;
  }
}

13. More Anti-Spam measures

Some mail can be rejected right away, for instance:

Update your /etc/postfix/main.cf to read:

smtpd_helo_required = yes
smtpd_helo_restrictions =
   permit_mynetworks,
   reject_invalid_helo_hostname,
   reject_non_fqdn_helo_hostname
smtpd_sender_restrictions =
   permit_mynetworks,
   reject_non_fqdn_sender,
   reject_unknown_sender_domain
smtpd_recipient_restrictions =
   reject_non_fqdn_recipient,
   reject_unknown_recipient_domain,
   permit_sasl_authenticated,
   permit_mynetworks,
   reject_unauth_destination

Finally, don’t forget to postfix reload. If you just copied and pasted these lines in, postfix may warn you that you overwrite previous configuration entries. They should match each other, so only keep one!

14. IPv6

To enable IPv6 in Postfix, set inet_protocols = all in /etc/postfix/main.cf. You can also set a specific address via smtp_bind_address6 = 1234:DEAD:BEEF:8::1234. Update /etc/network/interfaces accordingly and also set the AAAA record for the smtp.FQDN in your DNS entries.

Google decided to block all mails delivered via IPv6 if there is no rDNS entry for your v6 IP to your MX record (mail.yrden.de in my case). Depending on your hosting solution, you may need to setup a bind server yourself. Most likely you can add rDNS entries yourself in the web interface provided by your hoster though. If in doubt, ask them first.

To enable IPv6 in Dovecot, uncomment #listen = *, :: in /etc/dovecot/dovecot.conf. Like above, update your interfaces file and set the AAAA record for imap.FQDN.

Restart both services.

15. Enabling Forward Secrecy

To enable Forward Secrecy you need to add a Diffie-Hellman key to your private key file created in step 2. To do so, run: openssl dhparam -rand - 2048 >> /etc/ssl/private/mail.yrden.de.key

Postfix

Update your /etc/postfix/main.cf file to read:

smtpd_tls_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, MD5, DES, 3DES, DES-CBC3-SHA, RC4-SHA, AES256-SHA, AES128-SHA
smtp_tls_protocols = !SSLv2, !SSLv3, TLSv1
smtpd_tls_mandatory_protocols = TLSv1
smtp_tls_note_starttls_offer = yes
smtpd_tls_received_header = yes
smtpd_tls_eecdh_grade = strong
tls_preempt_cipherlist = yes

smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1

The logging is obviously optional, but it helps to see if your client is properly configured to use modern encryption.

Dovecot

Dovecot automatically uses the modern ciphers with clients that support it (e.g. Thunderbird). Unfortunately many mobile clients (e.g. Kaiten Mail) behave badly and choose old ciphers even though they support new ones. To alleviate this, you can configure Dovecot to not use the old ciphers by updating /etc/dovecot/conf.d/10-ssl.conf:

ssl_cipher_list = ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS

If you want to log the ciphers used for Dovecot as well, modify /etc/dovecot/conf.d/10-logging.conf so that the login_log_format_elements option reads (note the added %k in the end):

login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"

Testing

After restarting both Postfix and Dovecot, run these commands on your server:

openssl s_client -starttls smtp -connect localhost:25
openssl s_client -starttls imap -connect localhost:143

Both should output the following “or better”:

SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384

If you enabled logging, the same ciphers should appear in your /var/log/mail.log file as well.

Conclusion

Setting up mail is just like building legos: You never find the pieces you need and it takes much longer than anticipated. Good luck!