Table of Contents

Virtual mailboxes on Debian Squeeze with Exim4 and Dovecot

You know how it is. Your little mail host has just sat there for a long while, slowly accreting a user here and a user there. They are all full users on the machine, so they have logins and everything, and exim is configured to deliver their mails to ~/Maildir.

And then some dangerous loon suddenly demands that you add mailboxes for people using a completely different domain. They're not really much to do with you, and certainly shouldn't be full-blown users on your host.

Time, then, to add virtual mailboxes to your poor little mail host.

There all sorts of HOWTOs on doing this, of various ages and usefulness. I'm adding this page to the malestrom just to document the configuration changes I did. Or rather, the configuration changes I ended up with. The details of configuration aren't original, but synthesised from too many of the HOWTOs I read to properly credit each.

I'm illustrating this by setting up a new domain example.mod with user tommy.atkins.

Set up virtual mailbox space and configuration

First I created a user and group vmail to own all virtual mailboxes. I make the user a normal user because Dovecot will try to access all virtual mailboxes as that vmail user, and in Dovecot 2.x on Debian the config first_valid_uid is set to 500 to prevent attempts to access daemon mailboxes.

# adduser --home /var/local/vmail --group vmail

and a configuration directory.

# mkdir /etc/vmail

Configuration information for the domain will be under /etc/vmail/<domain>. If that directory doesn't exist, the domain isn't supported.

# mkdir /etc/vmail/example.mod

The general scheme is that we have mailboxes under /var/local/vmail/<domain>/<user>/Maildir. Why Maildir? Because our existing users will continue to get mail in ~/Maildir and it keeps dovecot config a little simpler.

# mkdir -p /var/local/vmail/example.mod
# chown -R vmail:vmail /var/local/vmail

Now add two configuration files. The first, aliases, is a conventional aliases file for the domain.

# cat > /etc/vmail/example.mod/aliases
postmaster: root
webmaster: root
security: root
admin: root
root: guru@example-owner.mod

squaddie: tommy.atkins
^D

The second, passwd contains the account information for the domain. There are two items on a line, username and password hash, separated by a colon. Generate the password has using the dovecotadm pw utility. Just to be on the safe side, we'll ensure the password file isn't world readable and is readable by Dovecot and Exim.

# doveadm pw -s SHA256-CRYPT -p password
{SHA256-CRYPT}$5$TQGxffy9XCxe53vu$L2NWgKJ47w3PoAIj3/IxLJIREA9QSyBKdKDMJlXvn07
# cat > /etc/vmail/example.mod/passwd
tommy.atkins:{SHA256-CRYPT}$5$TQGxffy9XCxe53vu$L2NWgKJ47w3PoAIj3/IxLJIREA9QSyBKdKDMJlXvn07
^D
# chown dovecot:vmail /etc/vmail/example.mod/passwd
# adduser Debian-exim vmail
# chmod 0660 /etc/vmail/example.mod/passwd

Arranging mail delivery

The next step is to configure Exim to deliver to virtual mailboxes.

Exactly how you do this depends on which of Debian's configuration schemes you are using. I am using the conf.d multiple configuration file hierarchy with the config type internet. From here on, I'll show what I ended up with in the Exim configuration file, and indicate where I put each entry in the conf.d hierarchy. Please treat these as an illustration; if you have a setup reasonably close to the standard installed version then I think these will probably work for you, but I can't promise.

The first thing to do is to add the domain to the list of local domains. You need to end up with a configuration file with the domain as part of the domainlist local_domains. I wanted to have the virtual mail domains work automatically, rather than have to add them manually to the list of local domains. I first added a configuration main/00_vmail_config with some definitions:

VMAIL_DELIVERY=dovecot_vmail

VMAIL_DOMAINS=dsearch;/etc/vmail

VMAIL_ALIASES=/etc/vmail/$domain/aliases
VMAIL_PASSWD=/etc/vmail/$domain/passwd

VMAIL_MAILBOX_DIR=/var/local/vmail/$domain

That dsearch;/etc/vmail will expand to a list of the files/directories under /etc/vmail. Which will be the domains to be handled.

I then made a small modification to main/01_exim4-config_listmacrosdefs to add the virtual mail domains to the list of local domains:

# Local modification - add vmail domains to local domains, and provide
# main_local_domains for the non-vmail domains.
domainlist local_domains = MAIN_LOCAL_DOMAINS : VMAIL_DOMAINS
domainlist main_local_domains = MAIN_LOCAL_DOMAINS

(I use main_local_domains elsewhere in my processing of non-virtual mail domains.)

Next I need a router to expand virtual domain aliases. I put this into router/170_vmail_aliases.

vmail_aliases:
  driver = redirect
  domains = VMAIL_DOMAINS
  allow_fail
  allow_defer
  data = ${lookup{$local_part}lsearch{VMAIL_ALIASES}}
  qualify_domain = $domain

qualify_domain = $domain ensures the expanded alias, if any, has the same domain as the original, if the domain is not specified in the alias.

Once that's done, I can think about routing virtual mail users to a transport for delivery, and rejecting messages to unknown users in a virtual mail domain. I put this into router/180_vmail_user.

vmail_user:
  driver = accept
  domains = VMAIL_DOMAINS
  local_parts = lsearch;VMAIL_PASSWD
  transport = VMAIL_DELIVERY

vmail_no_such_user:
  driver = redirect
  domains = VMAIL_DOMAINS
  allow_fail = true
  data = :fail: Unknown user
  more = false

Here I'm accepting the mail on condition that the local part of the address appears in the domain's password file. If it does, the message proceeds to the virtual mail delivery transport. Otherwise, the user does not exist in the virtual mail domain and I can fail delivery and give up.

Now to the transports. I am now delivering all mail using Dovecot deliver. I define a transport for this in transport/30_dovecot_vmail and select it using the VMAIL_DELIVERY definition.

dovecot_vmail:
  driver = pipe
  command = /usr/lib/dovecot/deliver -d $local_part@$domain -f $sender_address -a $original_local_part@$original_domain
  message_prefix =
  message_suffix =
  log_output
  delivery_date_add
  envelope_to_add
  return_path_add
  user = vmail
  temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78

Prior to using Dovecot deliver, I had Exim deliver virtual mail itself, with this transport in transport/30_vmail_home.

vmail_home:
  driver = appendfile
  envelope_to_add
  directory = VMAIL_MAILBOX_DIR/$local_part/Maildir
  maildir_format
  create_directory = true
  user = vmail
  group = vmail
  return_path_add

This transport adds the message to the user's Maildir. It runs as user and group vmail and will create any directories that don't exist.

By the way, when you add a user, it's an idea to either send them a welcome mail to check things are working properly and create their Maildir into the bargain. Otherwise, you should create the Maildir by hand, so that there is something there when they try to read their mail.

When that's done, test your handiwork:

# exim4 -bt tommy.atkins@example.mod
tommy.atkins@example.mod
router = vmail_user, transport = dovecot_vmail

Reading mail

Now we need to modify the Dovecot setup to allow our user to read mail.

Dovecot 1.x

I'm assuming your dovecot.conf already has

mail_location = maildir:~/Maildir

in it, telling Dovecot to find your regular users mail in ~/Maildir.

We need to add the virtual domains to the

auth default {
}

section of the configuration. The first stage is to add the virtual users password files.

passdb passwd-file {
  args = username_format=%n /etc/vmail/%d/passwd
}

Here's a tip. My configuration also has a configuration for PAM

passdb pam {
}

Put the virtual users passwd-file entry before the pam section. If you don't, your virtual usernames will be tried in pam first. Confusion will abound if they succeed. And if they don't, it will look to programs like denyhosts or fail2ban as if a PAM login failed, and you may find access to the host unexpectedly blocked.

Now you need to add a userdb section telling Dovecot how to find the mailbox. You will have a

userdb passwd {
}

section serving your existing users. After that, add a catch-all for the virtual mailbox users.

userdb static {
  args = uid=vmail gid=vmail home=/var/local/vmail/%d/%n
}

This tells Dovecot to use vmail permissions to access the mailbox, and that the user home is at /var/local/main/<domain>/<user>. Dovecot will apply it's existing rule that the mail is to be found in directory Maildir under the user's home, and you are off to the races.

In practice, you'll most probably run into authentication problems. For help sorting them out, add

auth_debug = yes
auth_debug_passwords = yes

to your dovecot.conf while you try and work out what's going wrong.

Dovecot 2.x with Debian conf.d configuration

Again, I'm assuming that 10-mail.conf has

mail_location = maildir:~/Maildir

in it, telling Dovecot to find your regular users mail in ~/Maildir.

I first added a new auth configuration file, auth-vmail.conf.ext.

# Virtual mailbox passwords.
passdb {
  driver = passwd-file
  args = username_format=%n /etc/vmail/%d/passwd
}

# VMail static settings.
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/local/vmail/%d/%n
}

This gives Dovecot the essential password and user info settings.

I then modified 10-auth.conf, adding the new vmail auth at the end of the file.

!include auth-vmail.conf.ext

To debug authentication problems, enable auth_debug and auth_debug_passwords in 10-logging.conf.

Letting your virtual users send mail

Your virtual mail users will probably want to send some mail, too. So you may want to give them access to your Exim server to relay mail. But, of course, you don't want world+dog also relaying their spam through your host.

There's several ways of cracking this nut. I'll just mention that if you do it by allowing authenticated SMTP, I found it easiest to do by handing the authentication over to Dovecot.

Dovecot 1.x

I needed to create the Dovecot authenticator socket by adding

 socket listen {
   client {
     path = /var/run/dovecot/auth-client
     mode = 0666
   }
 }

to the

auth default {
}

section in dovecot.conf. It's commented out by default in Debian. Without other arrangements, Exim needs its mode to be 0666; comments in the Dovecot config suggest this is generally safe.

Then, in the Exim configuration, add auth/30_dovecot_auth containing:

dovecot_plain:
  driver = dovecot
  public_name = PLAIN
  server_socket = /var/run/dovecot/auth-client
  #server_set_id = $auth2
  server_mail_auth_condition = false


dovecot_login:
  driver = dovecot
  public_name = LOGIN
  server_socket = /var/run/dovecot/auth-client
  #server_set_id = $auth2
  server_mail_auth_condition = false

Dovecot 2.x

In 10-master.conf I add a Dovecot authenticator socket with permissions for Exim by adding the following lines in the section service auth.

  unix_listener auth-client {
    mode = 0660
    group = Debian-exim
  }

Then add auth/30_dovecot_auth to the Exim configuration as above.