Although there is a lot of documentation regarding Postfix and OpenLDAP I could not find a complete example.

A good starting point when looking for the integration is the LDAP_README of postfix which gives at least some basic information.

In this post I’ll show how I managed to combine Postfix with OpenLDAP.

I had the following requirements for my setups:

  • Host multiple Domains
  • Each user can have one or more addresses, not limited to a single Domain
  • Each user does only have one Login and Mailbox.
  • Support for Catchall

Basics

I assume, that you are using a Postfix with LDAP support (e.g. the default Debian one) and a simple LDAP schema like this:

Structure:

  • Users: ou=Users,dc=foobar
  • Groups: ou=Groups,dc=foobar
  • Domains: ou=Domains,dc=foobar

User-Attributes:

AttributeUsageCardinality
cnLogin1
maildropMain Mailbox1
mailacceptinggeneralidMail Alias*

To simplify the post, I’ll replace some values with variables:

VariableExample
{{ ldap_bind_dn }}cn=postfix,ou=Applications,dc=foobar
{{ ldap_bind_pw }}foobar123
{{ ldap_host }}ldaps://ldap.foobar:636
{{ ldap_base }}dc=foobar
{{ ldap_user_base }}ou=Users,dc=foobar
{{ ldap_domains_base }}ou=Domains,dc=foobar

Setup Postfix

First of all we have to tell Postfix, how logins, aliases, domains and recipients are handled.

For each of those mapping we have to create file in /etc/postfix/ldap:

Logins

The smtpd_sender_login_maps configurations performs a lookup from an incoming email-recipient to a username. Postfix first performs a full lookup on user@domain, then user and then @domain. The later one is also used for catchalls where the mailacceptinggeneralid is set to e.g. @foobar.com

/etc/postfix/ldap/virtual_senders.cf

bind = yes
bind_dn = {{ ldap_bind_dn }}
bind_pw = {{ ldap_bind_pw }}
server_host = {{ ldap_host }}
search_base = {{ ldap_user_base }}
query_filter = (&(objectclass=inetOrgPerson)(mailacceptinggeneralid=%s))
result_attribute = cn

Domains

The virtual_mailbox_domains configurations performs a lookup, if the postfix is responsible for the given domain.

/etc/postfix/ldap/virtual_domains.cf

bind = yes
bind_dn = {{ ldap_bind_dn }}
bind_pw = {{ ldap_bind_pw }}
server_host = {{ ldap_host }}
search_base = {{ ldap_domains_base }}
query_filter = (&(ObjectClass=dNSDomain)(dc=%s))
result_attribute = dc

Recipients

As we only want to accept mail, where we know the recipients (and are responsible for them), the virtual_mailbox_maps configuration is used.

/etc/postfix/ldap/virtual_recipients.cf

bind = yes
bind_dn = {{ ldap_bind_dn }}
bind_pw = {{ ldap_bind_pw }}
server_host = {{ ldap_host }}
search_base = {{ ldap_user_base }}
query_filter = (&(objectclass=postfixUser)(mailacceptinggeneralid=%s))
result_format =%s/
result_attribute = cn

Aliases

Last but not least the virtual_alias_maps setting is used to find the target mailbox (configured in maildrop).

/etc/postfix/ldap/virtual_aliases.cf

bind = yes
bind_dn = {{ ldap_bind_dn }}
bind_pw = {{ ldap_bind_pw }}
server_host = {{ ldap_host }}
search_base = {{ ldap_user_base }}
query_filter = (&(objectclass=inetOrgPerson)(mailacceptinggeneralid=%s))
result_attribute = maildrop

Main.cf

The final part is to modify the main.cf of Postfix to use the configured files.

##
## Virtual config
##
virtual_alias_maps = proxy:ldap:/etc/postfix/ldap/virtual_aliases.cf
virtual_mailbox_domains = proxy:ldap:/etc/postfix/ldap/virtual_domains.cf
virtual_mailbox_maps = proxy:ldap:/etc/postfix/ldap/virtual_recipients.cf
smtpd_sender_login_maps = proxy:ldap:/etc/postfix/ldap/virtual_senders.cf
local_recipient_maps = $virtual_mailbox_maps

virtual_mailbox_limit = 52428800
virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 5000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

virtual_transport = lmtp:unix:private/dovecot-lmtp
mailbox_transport = lmtp:unix:private/dovecot-lmtp