Bounding Unicorns

Mail Configuration

Basics (Exim)

First I need to set up forward and reverse DNS for the IP of the mail server.


primary_hostname =

Maildir delivery to virtual users

All mail users are virtual, that is, do not map to Unix user accounts. I want mail to be stored in Maildirs.

Create a Unix user for the virtual mail users:

useradd vmail

In order to prohibit world read access to mailboxes, mailnull user (for Exim) must be added to vmail group. Otherwise Exim will fail sender verification when sending mail to remote machines. After this change Exim must be restarted.

Define the router:

  driver = accept
  local_part_suffix = +* : -*
  condition = ${if exists{/home/vmail/users/$local_part} {yes} {no} }
  transport = dovecot

The dovecot router should be placed in front of the local users one.

Define the transport:

  driver = appendfile
  user = vmail
  group = vmail
  mode = 0600
  maildir_format = true
  mode_fail_narrower = false
  envelope_to_add = true
  return_path_add = true

Outgoing mail

To send mail, I need to use the submission port (587) and set up authentication.

Submission port (Exim)

daemon_smtp_ports = 25 : 587

SMTP authentication (Exim)

# Allow any client to use TLS.

tls_advertise_hosts = *

# Specify the location of the Exim server's TLS certificate and private key.

tls_certificate = /usr/local/etc/exim/ssl/cert.pem
tls_privatekey = /usr/local/etc/exim/ssl/privatekey.pem

Now define an authenticator:

  driver = plaintext
  server_set_id = $auth2
  server_condition = ${if eq{$auth3}{${lookup{$auth2}lsearch{/usr/local/etc/exim/passwd}}}}
  server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
  server_prompts = :
  public_name = PLAIN

Sender override

By default, messages submitted via the submission protocol are modified by Exim to have the user that is used for authentication as the sender. This manifests in the sender user being in the envelope from header and also "on behalf of" addition to the originating email address.

In my case the user I use for authentication is not the user I send email from, therefore for this particular user I want to tell Exim to leave my original sender.

untrusted_set_sender = {if eq {$sender_ident}{send} {*} {}}
local_from_check = {if eq {$sender_ident}{send} {false} {true}}

Here, send is the user I authenticate as when sending mail.

Removing Sender header / "On behalf of"



Store copies of sent mail

No matter how I send mail, I want to save the sent messages on the mail server.

The solution is based on this post and this one. There is a difference between storing copies of submitted mail and mail that is being delivered. In case of a mail to multiple recipients, I want to make sure there is only one copy of it saved. Using a transport with unseen option operates on per-delivery basis, and therefore creates multiple copies when mailing multiple recipients. System filter creates a single copy.

First define a system filter:

system_filter = /usr/local/etc/exim/systemfilter
system_filter_directory_transport = sendcopy

System filter itself:

# Exim filter

if $sender_address_domain is
  or $sender_address_domain is
  unseen save /home/vmail/sent/$sender_address/

Define sendcopy transport:

  driver = appendfile
  user = vmail
  group = vmail
  mode = 0600
  #directory = /home/vmail/sent/$sender_address_local_part
  directory = /home/vmail/sent/${if eq {${domain:$reply_address}}{} {${local_part:$reply_address}} {$sender_address_local_part}}
  #directory = /home/vmail/sent/${local_part:$reply_address}
  maildir_format = true
  create_directory = true


For this I simply use the system alias file, /etc/aliases.

I cycle email addresses used for registration. For example, reg2012 for an account created in 2012. In 2013 I would update the email address if I still use the account. All current aliases are forwarded to reg, all expired/unused aliases are forwarded to reg-old.

An infinite address pool for testing

Useful for developers testing web applications.

  driver = redirect
  domains =
  local_part_prefix = wildcard-
  data =

IMAP access

This will be done with dovecot.

Dovecot password file permissions

On my system, /usr/local/etc/dovecot/dovecot.passwd is accessed by Dovecot with dovecot user and dovecot as the only group.


chown root:dovecot /usr/local/etc/dovecot/dovecot.passwd
chmod 0640 /usr/local/etc/dovecot/dovecot.passwd


None yet. All (working) webmail solutions I found use php and I am not excited to run php on the same system that handles my mail. Maybe on a separate system with access to selected accounts only.

Spam filtering

I use blacklist. This kills a lot of the mail but not everything. The remaining amount of spam is rather manageable at this point.

deny message = rejected because $sender_host_address is blacklisted by $dnslist_domain\n$dnslist_text
dnslists = :

As my "pretty" email address appears in various public locations, it will always be receiving spam. I could work around this issue by cycling my public email address, but this would present an inconvenience to people emailing me.

Blacklist check:

Email client

I use claws-mail. It works well and is lightweight.

IMAP folders (Dovecot)

It looks like the documentation for this is in Maildir format page and Maildir++ description. The short version is a folder with name Foo is stored a a subdirectory with name .Foo under the maildir directory, and there is a marker file called maildirfolder in .Foo.

Mail submission from firewalled machines

It turns out to be much easier to set up a local Exim to submit to the Internet server than to repeatedly configure SMTP for each user account. The downside to having a functional local MTA is that I need to be careful not to send real people email by accident.

Most of this configuration was taken from here with this used as initial inspiration.

First, comment out the dnslookup driver.

Then, create a smarthost driver in its place:

  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtp_submission
  route_data =

Define submission transport:

  driver = smtp
  port = 587
  hosts_require_tls = *
  hosts_require_auth = *

And define an authenticator for the client side:

  driver = plaintext
  public_name = plain
  client_send = ^smtplogin^smtppassword

Here I just hardcoded login and password which works sufficiently well.

Mail delivery to firewalled machines

This is handy for a server that cannot receive mail on port 25 due to a firewall.

First, an MX record is configured for my "normal" server.

On the normal server, I have the following router:

  driver = manualroute
  domains = : *
  transport = remote_smtp_submission
  route_list = *

And the transport to go with it:

  driver = smtp
  port = 587
  # If the public server is multihomed, specify which IP address to use
  # for connecting.
  #interface =