Bounding Unicorns

Mail Configuration

Basics (Exim)

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

Then:

primary_hostname = mail.bsdpower.com

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:

dovecot:
  driver = accept
  local_part_suffix = +* : -*
  local_part_suffix_optional
  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:

dovecot:
  driver = appendfile
  user = vmail
  group = vmail
  mode = 0600
  directory=/home/vmail/users/${lc:$local_part}/
  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:

send:
  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"

References:

  • https://lists.exim.org/lurker/message/20040628.144414.ac656633.html

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 bsdpower.com
  or $sender_address_domain is mail.bsdpower.com
then
  unseen save /home/vmail/sent/$sender_address/
endif

Define sendcopy transport:

sendcopy:
  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}}{bsdpower.com} {${local_part:$reply_address}} {$sender_address_local_part}}
  #directory = /home/vmail/sent/${local_part:$reply_address}
  maildir_format = true
  create_directory = true
  envelope_to_add
  return_path_add

Aliases

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.

wildcard:
  driver = redirect
  domains = wildcard.bsdpower.com
  local_part_prefix = wildcard-
  data = wildcard-target@bsdpower.com

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.

Therefore:

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

Webmail

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 zen.spamhaus.org 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 = zen.spamhaus.org : cbl.abuseat.org

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: http://www.kloth.net/services/dnsbl.php

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:

smarthost:
  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtp_submission
  route_data = mail.bsdpower.com
  no_more

Define submission transport:

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

And define an authenticator for the client side:

plain:
  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:

firewalled:
  driver = manualroute
  domains = firewalled.bsdpower.net : *.firewalled.bsdpower.net
  transport = remote_smtp_submission
  route_list = * firewalled.bsdpower.net

And the transport to go with it:

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

References