Migrating Postfix and Dovecot from a MySQL User-Database to Active Directory

When I set up my Active Directory, I wanted to migrate all services at home to authenticate against it. Now that I had the AD running and a few not so critical services used it, it was time finally migrate this one. I don’t have an Exchange-Server at home(nor do I want one) and my colleague who tested Openchange said it is not quite ready yet. So I will have to do without Exchange-extensions for the AD. I will still be able to do the basic management for my mail users in the AD, but I won’t be able to use any of the exchange extensions or tools for it.

Fortunately that’s not such a bad thing, I simply will have to improvise a few times.

The old setup of my Mail server used MySQL as backend for users, aliases, forwards and domains. This is the part that had to change. But I did not want to touch the actual mailboxes in any way and my sieve rules should stay intact too.

I was in luck since there is a lot of documentation on the topic, granted not all of it is complete but it was easy enough to piece everything together. But before actually changing any server settings I had to migrate the contents of my mail database to the AD.

The MySQL Database could be used to handle different Domains, this was never needed and I changed it to use a fixed domain instead. That being said I would still be able to use different domains for E-Mail in the Active Directory.

The most obvious thing I needed to migrate to the Active Directory are the primary E-Mail-Addresses of my users, this was simple: create the user in the AD and then set the E-Mail attribute to his mail address. No exchange attributes needed for that. If you are lazy and your internal Domain name matches your external one, you can also use the userPrincipalName as E-Mail-Address.
The next trick I had to teach my AD was E-Mail aliases. With Exchange extensions this is simple since it is integrated in the GUI. Without them you have 2 options:

1.: use ADSI-Edit or the command line to fill the “otherMAilbox” attribute of the user. It works but I consider it inconvenient, so I went with the other option.

2.: create a distribution Group with a fitting name (pure distribution groups should be universal groups). After creating it, set the alias E-Mail in the group preferences and add the user whose alias it is.

As the name suggests distribution groups can also be used to distribute E-Mails sent to one address to a bunch of users. One downside is without Exchange extension I don’t know if it is possible to send as the distribution group. I only use aliases to forward some system mail like postmaster to my main mailbox, so the downside does not affect me.

The last issue I needed to prepare for was forwards of specific addresses to specific addresses on an internal mailman(which uses a subdomain). I created contacts for with the E-Mail Addresses on the mailman server and distribution groups with the E-Mails it had to the rest of the world. For example if the internal E-Mail of a Mailing list would be “newsletter [at] lists [dot] example [dot] com”, I would create a contact with that E-Mail Address and then I would create a distribution group with the E-Mail “newsletter [at] example [dot] com”. After that I would add the contact to the group. My Postfix is configured to rewrite the domain of all outgoing mails from “lists.example.com” to “example.com”, but it can’t automatically decide whether an E-Mail to “example.com” is an actual Mailbox of “example.com or if it belongs to “lists.example.com”. Once these forward entries are set and configured in the alias maps, it will be able to do that.

With the preparations done I used Samba to join the Mail Server to the domain. With Kerberos authentication working, I was able to enable NTLM and GSSAPI(Kerberos) Authentication in Dovecot. First I added following lines in the beginning of /etc/dovecot/dovecot.conf:

auth_use_winbind = yes
auth_winbind_helper_path = /usr/bin/ntlm_auth

These two lines tell Dovecot to use winbind for authentication and they also tell it where to find winbind. Those two lines should be before the protocols setting. Next I had to change the setting for the maildir. My mailboxes are saved under “/var/mail/domain/username”, both the domain and the username were variables in that scenario. I only have one domain for emails, so making the domain dynamic was not a requirement. Also Kerberos allows authentication with only the username, so making the domain name fixed seemed to be the least trouble. If you need multiple domains, it would be easiest to enforce the userPrincipalNAme for authentication, since that always contains a domain.
My new maildir definition looks like this:

mail_location = maildir:/var/mail/skelleton.net/%Ln

This way i did not need to touch the user Mailboxes that already existed, since the names in the AD matched the users E-Mail. The %Ln instead of %n enforces Lowercase for the mailbox folder. Next I changed the mechanisms under auth_default, to look like this:

mechanisms = plain ntlm gssapi

The mechanism plain is not needed for NTLM or Kerberos authentication, it is actually a pain to get plain to work on Kerberos. But I needed it for my phone and my web-interface. If you are only using Mail clients with support for gssapi or NTLM, it is safer and easier to leave out plain.
As a last step I needed to update my userdb args:

userdb static {
args = uid=5000 gid=5000 home=/var/mail/example.com/%Ln allow_all_users=yes

The only change I made, was replacing the variable for a dynamic domain with the fixed value so that it matches the mail_location entry. The uid and gid I supplied, are for the system mail user under which all email related services run. Since I am still using the AD users as virtual users, it is still necessary to have one system user with access to all mailboxes. The last part is very important, it tells Dovecot, that all users are valid users. This way all users with valid AD credentials will be considered valid users, it also means that all mails given to Dovecot by postfix will be considered as addressed correctly. If a mail for a not existent mailbox arrives, the mailbox will be created.
This was the last setting I had to change, in order to get Dovecot working with Kerberos and NTLM via my AD.

My mobile Mail Client and my Webmail, do not support NTLM or gssapi. For them to function I need the Login mechanism plain. As the name suggests, this mechanism sends the login information unencrypted. So using this over an unencrypted network line is a bad idea. But lacking any alternatives, I set it up. The plain mechanism, requires ad passdb. I choose LDAP since that is the most logical choice for authenticating against the AD.
First I had to uncomment following lines in my /etc/dovecot/dovecot.conf

passdb ldap {
# Path for LDAP configuration file
args = /etc/dovecot/dovecot-ldap.conf

The file /etc/dovecot/dovecot-ldap.conf already existed in my Debian and I simply made the necessary changes:

hosts = ad.example.com
#user allowed to query the AD necessary if you want to use a filter
dn = EXAMPLE\mail
dnpass = securepassword
#auth_ bind will let the AD do the user Authentication. If this is off you will have to match the
#password in your filter and the query user will need domain admin privileges
auth_bind =yes
#define your base you can also use an OU as base for the search in large ADs this could speed up
#the search. Limiting the search could also be used as authorisation, but I do not recommend that.
base = dc=skelleton,dc=net
scope = subtree
# only users matching the filter will be able to log in, my filter is filtering for members the
#group Mailusers. This does not match the Primary group of the user.
pass_filter = (&(ObjectClass=person)(sAMAccountName=%n)(memberOf=cn=Mailusers,ou=Groups,dc=example,dc=ncom))
# if you want all valid users to be able to login, you can use:
#auth_bind_userdn = EXAMPLE\%n
#auth_bind_userdn = %u
# if you set either one of these the pass_filter will be ignored and every valid AD user will be
#able to log into Dovecot. If you use this you won't need a user for the queries.
# Auth_bind will try to authenticate against the AD with the credentials given by the user.

This completes the configuration of Dovecot. After saving the changes I restarted Dovecot and tested for a while.

After all the tests were successful, I still had to update postfix to use the AD, to lookup valid mail recipients. While authentication for sent and received mails already worked through the AD, Postfix still looked up mail recipients and alias mappings in MySQL. Since I already prepared my AD for the alias mappings, adapting Postfix to look up E-Mail recipients in the AD, was only a matter of changing a few configs.
First I went to /etc/postfix and created 2 new config files for the mailbox mapping and the alias mapping. The first file was /etc/postfix/ldap-users and it looked like this:

server_host = ad.example.com
search_base = DC=example,DC=com
version = 3
query_filter = (&(objectclass=person)(|(mail=%s)(othermailbox=%s)))
result_attribute = samaccountname
result_format = %s/Maildir/
bind = yes
bind_dn = EXAMPLE\mail
bind_pw = secretpassword

The file contains the basic definition of the Active directory used, a search query and the user information for the user, that runs the queries. The filter matches the E-Mail Recipient against the mail and othermailbox attribute of a user in the Active directory. A match with either of the them will result in a successful query. The result for the mail folder will be the username of the user, this way the E-Mail address does not have to match the username. It is also possible to filter for the userPrincipalName, since this name is always in the form of an E-Mail-Address.
After the user mapping was done I took care of the alias mapping in the file /etc/postfix/ldap-distribution-groups:

server_host = ad.example.com
search_base = DC=example,DC=com
version = 3
query_filter = (&(objectclass=group)(mail=%s))
leaf_result_attribute = mail
special_result_attribute = member
bind = yes
bind_dn = EXAMPLE\mail
bind_pw = secretpassword

This is very similar to the user mapping, but this time the filter is matching groups with the E-Mail-Address in their mail attribute. The results of the lookup are the E-Mails of the group members.

All that was left to do, was replacing the MySQL configs with the new ldap ones in the main.cf of Postfix.
The relevant entries in my /etc/postfix/main.cf looked like this:

virtual_mailbx_domains = mysl:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf

I had to change virtual mailbox domains and to a fixed domain and point the mailbox and alias maps to the newly created configs:

virtual_mailbox_domains = skelleton.net
virtual_mailbox_maps = ldap:/etc/postfix/ldap-users.cf
virtual_alias_maps = ldap:/etc/postfix/ldap-distribution-groups.cf

After the changes I restarted Postfix, and now my mail system uses the Active Directory for address mapping, authentication and partly authorization .
There is still some work to be done to check for Authorization in several places, but for the moment it is acceptable to assume, that every valid AD user is also an E-Mail user.

4 thoughts on “Migrating Postfix and Dovecot from a MySQL User-Database to Active Directory”

  1. Hi,

    thanks for this nice article. One thin I did not get: how did you migrate the users, i.e. their passwords?

    Thanks in advance,

  2. I had only a small number of users, so I generated secure passwords for them and told them over phone.
    This is not practical for larger environments, but if your users log on with the AD credentials, you could simply let them know that their E-Mail Password is their Computer password from date x.
    If you users don’t log in with that AD, you should try to see

    You could generate the passwords beforehand and distribute them ahead of time, and tell when they will have to use the new password and how they can change the password with the new system.

Leave a Reply

Your email address will not be published. Required fields are marked *