Migrating Postfix Smart Host to Active Directory

As part of the Active Directory migration I have to change the smart host I use for email. This server is one of three servers that host mailboxes. This server is also the clearing house for all email into and out of the office.

The current setup has used LDAP for over 10 years. In this case this server also hosts an LDAP replica.

This server is running Debian 8.5 with Postfix 2.11.3, Cyrus-IMAP 2.4.17, and OpenLDAP 2.4.40.

This server is 192.168.0.16, the Samba AD-DC is 192.168.0.24

One point this system has that is missing in Active Directory in an attribute defining the machine hosting a user’s mailbox. This is the “mailHost” attribute that can be found on just about every LDAP server except Microsoft. Without going to the hassle of importing the three attributes and one class into Active Directory I decided to try using the proxyAddresses attribute. Time will tell if this is a workable idea.

Make a copy of changed files before making changes just in case.

LDAP

Edit the /etc/ldap/ldap.conf adding the “TLS_REQCERT allow” entry. This will allow the self-sighed certificate currently used by the Samba AD-DC.

So looking up my account in the AD-DC I use the following command:

ldapsearch -H ldap://192.168.0.24 -D "CN=postfix-connect,CN=Users,DC=samdom,DC=example,DC=net" -w "password" -b "dc=samdom,dc=example,dc=net" -s sub -x -ZZ -LLL -v  '(&(objectClass=user)(!(objectClass=computer))(!(userAccountControl:1.2.840.113556.1.4.903:=2))(cn=ahaines))'

Since I already had LDAP on this server I have to explicitly set all required fields in any ldapsearch commands otherwise it pulls the defaults from the ldap.conf file which do not currently point to the Samba AD-DC. I learned this the hard way after several frustrating minutes. I forgot the “-b” entry and got the following result:

requesting: All userApplication attributes
No such object (32)
Additional information: acl_read: Error retrieving instanceType for base. at ../source4/dsdb/samdb/ldb_modules/acl_read.c:784

Saslauthd

Edit the /etc/saslauthd.conf to use the Samba AD-DC. I’ve already done this with the other servers so it is really simple now:

ldap_servers:           ldap://192.168.0.24
ldap_search_base: cn=Users,dc=samdom,dc=example,dc=net
ldap_filter: (sAMAccountName=%U)
ldap_bind_dn: cn=postfix-connect,cn=Users,dc=samdom,dc=example,dc=net
ldap_bind_pw: password
ldap_use_sasl: no
ldap_referrals: yes
ldap_auth_method: bind
ldap_mech: simple
ldap_start_tls: yes
ldap_tls_check_peer: no

Postfix

This server does not require user authentication for sending emails so no postfix/sasl configuration is required.

It does however have a few LDAP lookups that need to be adjusted.

ldapalias_maps

alias_maps = returns a list of user accounts or addresses that are part of a mail alias. The nisMailAlias is replaced by a Distribution Group in Active Directory, which IMHO is less functional. So the original lookup file is:

server_host=127.0.0.1
query_filter=(&(objectClass=nisMailAlias)(|(cn=%s)(mailLocalAddress=%s)))
search_base=ou=Aliases,dc=example,dc=net
timeout=20
result_attribute=rfc822MailMember
bind=no
server_port=389
scope=one

Based on the work done for the submission server, this file would become:

server_host=192.168.0.24
query_filter=(&(objectclass=group)(mail=%s))
search_base=ou=aliases,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
special_result_attribute=member
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

Using “postmap -q everybody@example.net ldap:ldapalias_maps.cf” gets me a list of the user accounts in the everybody distribution group. It does not get me any third party email addresses so another lookup will be needed. By using the special_result_attribute the system will do a recursive lookup thus supporting nested groups. By changing result_attribute to leaf_result_attribute the group is not included in the results.

ldapalias_maps_member

alias_maps =

server_host=127.0.0.1
query_filter=(&(objectClass=nisMailAlias)(cn=%s))
search_base=ou=Aliases,dc=total-transportation,dc=com
timeout=20
result_attribute=rfc822MailMember
bind=no
server_port=389
scope=one

The difference between this file and the ldapalias_maps file is trivial and I can not remember why I had both of them. I probably do not need this file since every result here is also in the previous file.

ldaptransport_maps

transport_maps = lookup tables with mappings from recipient address to (message delivery transport, next-hop destination). In my case this would be the server hosting the user’s mailbox if not on the current server.

server_host=127.0.0.1
query_filter=(&(|(mail=%s)(mailLocalAddress=%s))(!(mailHost=192.168.0.16)))
search_base=ou=people,dc=example,dc=net
timeout=20
result_attribute=mailHost
result_filter=relay:[%s]
bind=no
server_port=389
scope=one
version=3

This file fetches the mailHost attribute for a user account given an email address. In this case the query looks for mailHost that does not point to this server and returns a “relay:[IP]” to the transport lookup.

Active Directory does not support this natively like most other LDAP servers so a work around is required.

Here is the first attempt:

server_host=192.168.0.24
query_filter=(&(objectclass=user)(|(mail=%s)(proxyAddresses=smtp:%s))(!(proxyAddresses=mailHost:192.168.0.16)))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=proxyAddresses
result_filter=relay:[%s]
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

This first attempt returns every proxyAddresses entry when I only want the one with mailHost in it. It does as expected when the user mailbox is hosted on this server.

A temporary work around is to use a text file listing all the affected addresses. This is a pain to maintain and should not be used long term.

ahaines@example.net           relay:[192.168.0.31]
ahaines@example.com relay:[192.168.0.31]
joe@example.net relay:[192.168.0.31]

ldaplocalvirtual_recipient_maps

virtual_alias_maps = lookup tables that alias specific mail addresses or domains to other local or remote address

server_host=127.0.0.1
query_filter=(&(objectclass=inetOrgPerson)(mailHost=192.168.0.16)(mailLocalAddress=%s))
search_base=ou=people,dc=example,dc=net
timeout=20
result_attribute=uid
bind=no
server_port=389
scope=one
version=3

This look up provides a list of user accounts that match the given email address and have their mailbox reside on this server. The new version of the file would be:

server_host=192.168.0.24
query_filter=(&(objectclass=user)(|(mail=%s)(proxyAddresses=smtp:%s))(proxyAddresses=mailHost:192.168.0.16))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

ldaplocal_recipient_maps

local_recipient_maps = Lookup tables with all names or addresses of local recipients. In this case only the existence of the address is required. This file is only slightly different to ldaplocalvirtual_recipient_maps.

server_host=127.0.0.1
query_filter=(&(objectclass=inetOrgPerson)(mailHost=192.168.0.16)(|(mail=%s)(mailLocalAddress=%s)(uid=%s)))
search_base=ou=people,dc=example,dc=net
timeout=20
result_attribute=uid
bind=no
server_port=389
scope=one
version=3

Since we just need to know if a mailbox exists on the current server, the lookup file would look like this:

server_host=192.168.0.24
query_filter=(&(objectclass=user)(|(mail=%s)(proxyAddresses=smtp:%s)(sAMAccountName=%s))(proxyAddresses=mailHost:192.168.0.16))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

ldaprelay_recipient_maps

relay_recipient_maps = lookup tables with all valid addresses in the domains that match. In this case only the existence of the address is required.

server_host=127.0.0.1
query_filter=(&(objectclass=inetOrgPerson)(!(mailHost=192.168.0.16))(|(mail=%s)(mailLocalAddress=%s)(uid=%s)))
search_base=ou=people,dc=example,dc=net
timeout=20
result_attribute=uid
bind=no
server_port=389
scope=one

This is the same as the ldaplocal_recipient_maps but we negate the mailHost as we want users that do not have their mailbox hosted here.

server_host=192.168.0.24
query_filter=(&(objectclass=user)(|(mail=%s)(proxyAddresses=smtp:%s)(sAMAccountName=%s))(!(proxyAddresses=mailHost:192.168.0.16)))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

Cyrus IMAP

Since my setup is using saslauthd there were no changes to the cyrus-imap configuration.

Conclusion

Other than the mailHost issue this server is ready to go.

Bookmark the permalink.

Comments are closed.