I stumbled over this post while looking at how other people had set-up virtual domains with Exim4 and Dovecot.

It made me realise I’d only half-implemented a virtual domain solution. The key part is that there is a user (vmail) which owns all the virtual mailboxes and operates on behalf of the virtual domain users. I’ve implemented a slighly different approach to that outlined in the post.

Virtual Domain Structure

The virtual domain mailboxes are held in /home/vmail, structured as follows:

/home/vmail
    /domain1
        /aliases
        /user1
            /mail
         /user2
            /mail
    /domain2
        /aliases
        /user1
            /mail
        /user2
            /mail

The aliases file under each domain is the domain specific alias file, organised exactly as /etc/aliases.

Exim4

The router

This is the modified router configuration for virtual domains, using the above structure to identify a virtual domain user. The domain name must exist as a directory in the /home/vmail account and contain an aliases file to validate the local_part of the address. If the local_part cannot be located in the aliases file, the email will not be accepted for the virtual domain.

# Router for virtual domains
# Use of domain_data to avoid using tainted data
# Data is considered tainted if if comes from the mail

virtual_domains:
	debug_print = "R: virtual_domains for $local_part@$domain"
	driver = accept
	domains = dsearch;/home/vmail
	local_parts = wildlsearch;/home/vmail/${domain_data}/aliases
	transport = virtual_mailboxes

So, the aliases file for example.com say, could have the following contents:

user.alias:   user
^user\\W.*:   user

You’ll note the use of wildlsearch for looking up names in the domain file. In this setup, a mail directed to user@example.com would not be accepted.

The configuration file was named 250_exim4-config_virtual_domain which means it is defined right after the primary configuration for sending mail and before routers for local delivery.

The transport

# Transport for virtual mail boxes
# Use of local_part_data/domain_data to avoid "tainted data"
# Data is considered tainted if it comes from the mail

virtual_mailboxes:
	debug_print = "T: virtual_mailboxes for $local_part$domain"
	driver = appendfile
	file = /var/mail/${local_part_data}@${domain_data}
	user = vmail
	# group must be set to allow exim to write to user's mailbox
	group = mail

The transport directs the delivery to the users mailbox. The mailbox name is in the form name@example.com, and will be owned by vmail:mail.

Note that local_part_data and domain_data are used to avoid taint errors.

Transports are not sensitive to the order of definition, so this transport configuration was place in a file called 30_exim4-config_virtual_mailboxes.

Authentication

The virtual domain users need to be added to the /etc/exim4/passwd file, the format of which is described in the man page for exim4-config_files. User names are in the form user@example.com.

Dovecot

Mailbox configuration

Dovecot has to be configured to use the correct path for the user’s incoming mailbox. This is set in the /etc/dovecot/conf.d/10-mail.conf file.

mail_location = mbox:~/mail:INBOX=/var/mail/%u:LAYOUT=maildir++:\
              INDEX=~/mail/index:CONTROL=~/mail/control

The use of "%u" in the INBOX definition uses the full login name of the virtual user as the mailbox name.

The mail for real local users is still delivered (by Exim4) to /var/mail/user so to allow Dovecot access, a link can be created from user@realdomain to user in the /var/mail directory. Another solution might be to alter the Exim4 transport 30_exim4-config_mail_spool to deliver incoming mail to the same name@domain name format as the virtual domain.

In this latter approach, the command line mail program would need configuring to consult the correct mailbox file. For example, for GNU mail, put this in the users ~/.mail file:

mailbox {
    mailbox-pattern "/var/mail/${user}@domain";
}

Authentication

I use Dovecot’s passwd-file to authenticate users, rather than /etc/passwd. This allows the users of the domain to have their email address as the username. The uid and gid for virtual users should be those of the vmail user. E.g. if both the vmail uid and gid is 1234:

user@domain.com:{CRYPT}password:1234:1234::/home/vmail/domain.com/user

It’s a shame that Dovecot and Exim4 authentication is completely separate.

Updating the system

The new Exim4 configuration template can be created and installed with the following commands:

sudo update-exim4.conf.template -r
sudo update-exim4.conf

Restart Exim4 with:

sudo systemctl restart exim4
sudo systemctl restart dovecot

Update

That comment above, about Dovecot and Exim4 authentication separation, is not quite true. I found the clue in this article. While the example in the article is based on OpenSMTPD and Dovecot, it turns out that they cannot share an authorisation file. The table type of passwd , used in the sample smtpd.conf file is no longer supported by OpenSMTPD.

The exim auth file format is a subset of dovecot’s and exim can successfully use the dovecot auth file. To make this work, permissions on the shared auth file must be set to allow both exim and dovecot to read the file. For example:

# mv /etc/dovecot/auth-users /etc/auth/mail
# chmod 640 /etc/auth/mail
# chown dovecot:Debian-exim /etc/auth/mail