Personal mail server

Posted on March 4, 2014
Tags: Debian, Fetchmail, SpamAssassin, Dovecot, Sieve, Maildir, Emacs, Gnus, iOS, Exim

I used to use Fetchmail and Gnus with its mail splitting capabilites and nnml backend to handle my private mail setup for many years. This worked pretty fine since I am used to accessing my home server through SSH. I have Gnus running inside of a GNU screen session. So I can check mail from remote by using a SSH terminal.

This works fine as long as I have a good SSH terminal on a desktop or laptop computer. However, it does not work very well on tablets or smaller mobile devices.

Additionally, mail splitting has become a performance burden over the years. I do not want to wait for Gnus to sort incoming mail into different folders while checking for new mail. That is something which should have been done in the background already, and thats what we are going to cover with the setup described below.

So I had to change my simple setup to accomodate for the new trends in mobile computing.

The obvious core of such a setup is an IMAP server which receives and stores your mail such that different clients can access it. So the time of my Gnus nnml storage are definitely over. Mail is no longer stored in and by my mail client.

Dovecot

While there are several IMAP server solutions out there, I find Dovecot fits my needs quite nicely.

I’ve decided to store my mail in Maildir format in ~/Maildir. I prefer storing data like that in the home directory to avoid having to backup separate files from /var/. Maildir also features some index files which should help performance in the long run.

Incoming mail will solely be delivered by fetchmail and should be checked for spam. While I can probably configure Exim to run SpamAssassin on mails before delivering them to Dovecot, there is a much more elegant solution: the dovecot local delivery agent (LDA). /usr/lib/dovecot/deliver takes mail from standard input and performs Sieve filtering and updates the mail indexes. We will call this executable more or less directly from fetchmail.

Incoming mail from mailing lists will be sorted into different folders using Sieve. Dovecot needs to be told to enable the sieve plugin and to create new folders on demand.

/etc/dovecot/local.conf:

disable_plaintext_auth = yes
mail_location = maildir:~/Maildir
lda_mailbox_autocreate = yes
lda_mailbox_autosubscribe = yes
protocol lda {
  mail_plugins = sieve
}

Sieve scripts are actually quite intuitive once you have a template to start from.

~/.dovecot.sieve:

require "fileinto";

if exists "X-Spam-Flag" {
  # Store spam tagged by SpamAssassin into dedicated Spam folder
  if header :contains "X-Spam-Flag" "YES" {
    fileinto "Spam";
  }
} elsif exists "X-Cron-Env" {
  # Store mails from Cron daemon in dedicated folder
  fileinto "cron";
} elsif exists "List-Id" {
  # File list-mail into dedicated folders, matching on List-Id
  if header :contains "List-Id" "boost-users.lists.boost.org" {
    fileinto "boost-users";
  } elsif header :contains "List-Id" "brltty.mielke.cc" {
    fileinto "brltty";
  } elsif header :contains "List-Id" "debian-accessibility.lists.debian.org" {
    fileinto "debian-accessibility";
  } elsif header :contains "List-Id" "debian-devel-announce.lists.debian.org" {
    fileinto "debian-devel-announce";
  } elsif header :contains "List-Id" "debian-devel.lists.debian.org" {
    fileinto "debian-devel";
  } elsif header :contains "List-Id" "spirit-general.lists.sourceforge.net" {
    fileinto "spirit-general";
  }
  # ...
}

SpamAssassin

Since I want automatic classification of spam messages, I use SpamAssassin. Just install spamassasin and enable spamd in /etc/default/spamassassin:

ENABLE=1

We will use spamc in the Fetchmail configuration.

Fetchmail

My ~/.fetchmailrc is a straightforward list of some mailboxes to fetch mail from. I use the mda directive to skip the MTA and send mail through SpamAssassin and deliver it to Dovecot via its LDA mechanism.

~/.fetchmailrc:

set daemon 1200 # Poll at 10 minute intervals
poll blind.guru protocol IMAP: ssl;
# ... add more sources here ...

mda "/usr/bin/spamc -u %T -e /usr/lib/dovecot/deliver -d %T"

To avoid spreading access information in too many configuration files I am using the ability of Fetchmail to use netrc to retrieve account passwords.

~/.netrc:

machine blind.guru login mlang password <hidden>

Gnus

I am using Gnus to read mail, newsgroups and RSS feeds since many years now. It would be quite a mouthful to explain all the customizations I am using by now. But there is one very important bit in the context of this article: How to access the IMAP server? In my setup, Emacs and therefore Gnus is running on the same machine as the IMAP server. So I can avoid authentication at all. This configuration will avoid unnecessary password prompts or caching.

In ~/.emacs or ~/.gnus:

(setq gnus-secondary-select-methods '((nnimap "localhost"
                                       (nnimap-stream shell)))
      nnimap-shell-program "/usr/lib/dovecot/imap")

With this you should be able to subscribe to your IMAP folders from within Gnus with ease.

Sorting incoming mails into folders is now performed by the IMAP server through Sieve scripts. Instead of changing Gnus’ configuration I now edit ~/.dovecot.sieve when I subscribe to a new mailing list. If you add a new Sieve rule for a mailing list and the associated folder does not exist yet, Dovecot will autocreate it, very convenient.

Mobile devices

Now all that is left is a way for your mobile devices to read and eventually send mail. This is very much dependant on your network setup, so I am not going to go into any detail here. If you are accessing your mail setup from a tablet in your local network you might get away without tinkering with your router configuration. If you want to read/send mail on the go you need some way to get to your external IP. Either it is stable enough or you need some dynamic DNS service. You will definitely want to forward IMAP and maybe SMTP ports from your router to your home server. If you don’t have an existing SMTP server for your mobile device that accepts your outgoing mails you can also set one up yourself and deliver outgoing mails from your mobile device to the world with Exim or qmail.

I am personally using Exim since I am going with the default MTA for Debian. Configuring Exim to take mail from iOS devices was as simple as enabling an appropriate authentication method and adding an account to /etc/exim4/passwd. I have to admit though that I don’t particularily like Exim’s configuration files. That is why I ended up using dovecot’s LDA in the first place.