Matt Horan's Blog

Setting up Postfix and Dovecot to play nicely with mutt, mbox, Maildir and FreeBSD

I’m one of two people I’m aware of who still runs their own e-mail servers. I do this for a multitude of reasons, mostly because I love mutt and nothing else quite stacks up to it. Running a local mail server with mutt reading a local inbox is relatively simple. I run Postfix and it gets the job done with relatively little configuration. However, if you’d like to check mail remotely, and not rely on ConnectBot to SSH into your ARP Networks VPS, you’ll have to venture into the world of IMAP servers.

To check mail on the go, I use K-9 Mail. As an Android user, the stock e-mail app leaves much to be desired, and K-9 brings handy features like IMAP IDLE support, which provides push e-mail support.

On the server side, I decided to go with Dovecot. Dovecot is a relatively simple IMAP server which has great Postfix support. I originally went with Dovecot because it integrates with Postfix SASL, and is a great alternative to Cyrus SASL. SASL is important in the context of SMTP servers because it allows remote clients to send e-mail authenticated through your server, therefore not having to be an open relay in order to send e-mail on the go.

As a FreeBSD user, I’ll be providing FreeBSD specific steps for getting Postfix, Dovecot, mutt and K-9 mail playing nicely together. The process isn’t too involved, but I’ll explain the steps and what I know about them along the way.

Getting everything installed is relatively simple:

portsnap fetch update
portmaster mail/postfix mail/dovecot2 mail/mutt

This will install the latest version of Postfix, Dovecot 2 and mutt (non-devel). It’s important to note the dovecot2, as the mail/dovecot port is for Dovecot 1.

I also recommend installing mail/dovecot-sieve as an alternative to procmail. I found procmail locking support to be lacking on FreeBSD, and this is something that is quite important when you have multiple processes accessing your inbox, if you’re using mbox. This is of course less concern with Maildir, however, Sieve is a great alterntive to procmail and can be configured from supported third-party plugins for various mail clients.

make config will prompt with a few questions for the mail/postfix and mail/dovecot2 packages. There are a couple of options for each with are important. For mail/dovecot2, I recommend enabling SSL, otherwise your credentials will be sent in the clear across the Internet. If you’re using a non-PAM based authentication mechanism, other authentication schemes can be used such as CRAM-MD5, however this will only protect your password. All of your mail will still be sent unencrypted. For mail/postfix, you’ll want to be sure to enable the Dovecot 2.x SASL authentication method and SSL/TLS support.

There’re a few configuration changes which need to be made to get everything playing together nicely. I tinkered with this configuration for about a year or so before finally getting it right. For a long while, I noticed that I would sometimes delete a message from my inbox via mutt, but then it would reappear in K-9 mail, and vice versa. I didn’t really mind this at first, but I was afraid that this would ultimately lead to a corrupt mailbox, which it did. I eventually figured out the right combination of settings which got the combination of local mail and IMAP playing nicely, but switching to Maildir instead of mbox will also do the trick.

There are two options for local mail delivery with Dovecot and Postfix. One is dovecot-lda, a simple solution for a small installation. The other is Dovecot’s built-in LMTP sever. Each has advantages and disadvantages. dovecot-lda is relatively simple, and similar in concept to procmail. It’s a process that runs, via Postfix mailbox_command, for local mail delivery. LMTP definitely makes more sense on larger installations, as it runs as a daemon and hooks in with Postfix via mailbox_transport. However, configuration is non-trivial, and on my installation it wasn’t worth the effort.

There are a few caveats with dovecot-lda. If using mbox, dotlocking for /var/mail inbox spools won’t work. This is because dovecot-lda runs as the user being delivered to, and this user typically isn’t in the mail group, and can’t create a dotlock file in /var/mail. One option is to enable the sticky bit on /var/mail, but there are various security implications with this approach. I’ve switched to Maildir, which is better for a variety of reasons. However, if you are using mail spools in shared directories which can’t be written by the user (read: you’re not using Maildir), you’re going to have to set mbox_read_locks = flock and mbox_write_locks = flock in /usr/local/etc/dovecot/conf.d/10-mail.conf. The issues with message deletion were solved by setting mbox_dirty_syncs = no and mbox_lazy_writes = no, which were both yes by default in my installation. Again, these changes are not necessary if you’re running Maildir.

The Dovecot LMTP server is a great alternative to dovecot-lda for large installations. It also plays nicely with /var/mail inbox spools, as the mail_privileged_group setting gives the LMTP process escalated permissions when necessary. However, there is a caveat with the LDA and postifx virtual aliases, which is that the LMTP daemon will look for the fully qualified username, e.g. root@hostname instead of just root. If you’re using the default PAM database, this will of course fail. If you’re using the LMTP transport with mbox, you’ll also need to set client_limit = 1 option inside the service lmtp block in /usr/local/etc/dovecot/conf.d/10-master.conf. As I’m not running LMTP, setup is outside the scope of this post, however all the necessary documentation can be found on the Dovecot wiki.

The key for setting up Maildir, or mbox delivery should you prefer, is the mail_location setting in /usr/local/etc/dovecot/conf.d/10-mail.conf. I have this option set to mail_location = maildir:~/Maildir. As the Maildir is stored in the home directory, there are no shared locking issues to worry about, as the user has control over all the files that need to be created. As stated earlier, if you use an alternative configuration with mbox, mail_location = mbox:~/mail/:INBOX=/var/mail/%u, you’re going to have to be concerned with locking, dirty and lazy writes.

Once locking and mailbox delivery are all set up, you’ll have to tell Dovecot to do its business. You must enable the IMAP protocol by setting protocols = imap in /usr/local/etc/dovecot/dovecot.conf. The default configuration will use PAM authentication by default, which requires setup of the /etc/pam.d/dovecot service. My file looks like this:

auth    required        pam_unix.so
account required        pam_unix.so

If you’ve decided to go down the route of CRAM-MD5 authentication, you’re not going to be able to use PAM authentication, as the password database has to be stored in the clear.

To setup Postfix SASL authentication, you must set up a Dovecot auth listener. This can be done by adding the following to /usr/local/etc/dovecot/conf.d/10-master.conf, inside the service auth block:

# Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }

SSL configuration is outside of the scope of this post, however an ssl_cert and ssl_key must be set in /usr/local/etc/dovecot/conf.d/10-ssl.conf. smtpd_tls_cert_file must be set in /usr/local/etc/postfix/main.cf as well.

To get the Dovecot LDA up and running, you must set postmaster_address in /usr/local/etc/dovecot/conf.d/15-lda.conf. I’ve also set lda_mailbox_autocreate = yes so that saving mail to a new mailbox creates the mailbox automatically. To enable the sieve plugin for the Dovecot LDA, don’t forget to add sieve to the mail_plugins under protocol lda. This is the only thing that needs to be set to enable sieve, with the magic coming (by default) from ~/.dovecot.sieve.

My ~/dovecot.sieve is relatively simple. Here’s an excerpt:

require ["fileinto", "envelope"];

if header :contains "List-Id" "freebsd.org" {
  fileinto "freebsd";
} elsif header :contains "List-Id" "voidnetworks.com" {
  fileinto "void";
}

The path to the user’s dovecot.sieve can be changed in /usr/local/etc/dovecot/conf.d/90-sieve.conf.

Once these changes have been made, Dovecot is ready to go. Just set dovecot_enable="YES" in /etc/rc.conf and start it up via /usr/local/etc/rc.d/dovecot start. Tail /var/log/maillog to check for any configuration errors.

I’m using Postfix virtual aliases for local mail delivery. This allows me to have multiple email accounts at domains I host, all delivered to various local system accounts. It’s a relatively simple solution for a relatively simple setup. Virtual aliases are out of the scope of this post, but I’ll assume you’ve got some sort of local delivery happening with Postfix.

To hook up Postfix delivery to Dovecot, if going down the route of dovecot-lda, simply add mailbox_command = /usr/local/libexec/dovecot/dovecot-lda to the /usr/local/etc/postfix/main.cf. This will have a similar effect to the inline example of using procmail for local delivery.

If going down the route of LMTP, you’ll not only need to get a working userdb mapping for incoming email addresses (virtual aliases are delivered via LMTP at their fully qualified name, e.g. root@hostname), but also set mailbox_transport = lmtp:unix:private/dovecot-lmtp in /usr/local/etc/postfix/main.cf.

I also recommend setting mailbox_size_limit = 0 and message_size_limit = 0 in /usr/local/etc/postfix/main.cf, else you may experience issues with local delivery. Postfix applies limits to the size of a mailbox even when delivered via dovecot-lda. I ran into this issue with mbox delivery, though I’m not sure if it’s also an issue with maildir.

To get Postfix playing nicely with Dovecot SASL, add the following to /usr/local/etc/postfix/main.cf:

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes

smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

You’ll want to merge smtpd_recipient_restrictions with any other custom restrictions you may already have in place. The key is permit_sasl_authenticated, which bypasses reject_unauth_destiontion, preventing your mail server from becoming an open relay.

By default, SASL will be enabled on port 25, accepting plaintext auth (your username and password) across the Internet. I recommend setting up SSL, as with Dovecot. Once you’ve got this set up, you should disable plaintext authentication. This is done by default in Dovecot, except for localhost. To do this in Postfix, you must set smtpd_tls_auth_only = yes. Then you’ll need to enable an alternative, secure port for accepting SASL auhtenticated mail. I’ve set up the submission port (587) to do this in /usr/local/etc/postfix/master.cf:

submission inet n       -       n       -       -       smtpd
  -o smtpd_tls_security_level=may
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

Once this is all set, you should be ready to disable FreeBSD sendmail in favor of Postfix. Add the following to /etc/rc.conf:

sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

postfix_enable="YES"

At this point, you should be ready to start up Postfix via /usr/local/etc/rc.d/postfix. Again, tail /var/log/maillog for any error messages.

Once both Dovecot and Postfix are successfully up and running, you should be able to get your favorite IMAP client connected. You’ll be unable to check mail via IMAP by default without setting up SSL. However, if you don’t care about security, you can set disable_plaintext_auth = no in /usr/local/etc/dovecot/conf.d/10-auth.conf. Setting up SSL is really simple and totally worth it. I use StartCom Free SSL certificates, which work on every device I own.

K-9 mail plays nicely with this configuration, and Dovecot supports IMAP IDLE out of the box. I’m loving push IMAP to my Galaxy Nexus, and I get all the benefits of Gmail without the pain of setting up OfflineIMAP or something similar.