Postfix LDAP Howto

-------------------------------------------------------------------------------

LDAP Support in Postfix

Postfix can use an LDAP directory as a source for any of its lookups: aliases
(5), virtual(5), canonical(5), etc. This allows you to keep information for
your mail service in a replicated network database with fine-grained access
controls. By not storing it locally on the mail server, the administrators can
maintain it from anywhere, and the users can control whatever bits of it you
think appropriate. You can have multiple mail servers using the same
information, without the hassle and delay of having to copy it to each.

Topics covered in this document:

  * Building Postfix with LDAP support
  * Configuring LDAP lookups
  * Example: aliases
  * Example: virtual domains/addresses
  * Other uses of LDAP lookups
  * Notes and things to think about
  * Feedback
  * Credits

Building Postfix with LDAP support

Note 1: Postfix no longer supports the LDAP version 1 interface.

Note 2: to use LDAP with Debian GNU/Linux's Postfix, all you need is to install
the postfix-ldap package and you're done. There is no need to recompile
Postfix.

You need to have LDAP libraries and include files installed somewhere on your
system, and you need to configure the Postfix Makefiles accordingly.

For example, to build the OpenLDAP libraries for use with Postfix (i.e. LDAP
client code only), you could use the following command:

    % ./configure  --without-kerberos --without-cyrus-sasl --without-tls \
        --without-threads --disable-slapd --disable-slurpd \
        --disable-debug --disable-shared

If you're using the libraries from the UM distribution (http://www.umich.edu/
~dirsvcs/ldap/ldap.html) or OpenLDAP (http://www.openldap.org), something like
this in the top level of your Postfix source tree should work:

    % make tidy
    % make makefiles CCARGS="-I/usr/local/include -DHAS_LDAP" \
        AUXLIBS="-L/usr/local/lib -lldap -L/usr/local/lib -llber"

On Solaris 2.x you may have to specify run-time link information, otherwise
ld.so will not find some of the shared libraries:

    % make tidy
    % make makefiles CCARGS="-I/usr/local/include -DHAS_LDAP" \
        AUXLIBS="-L/usr/local/lib -R/usr/local/lib -lldap \
                -L/usr/local/lib -R/usr/local/lib -llber"

The 'make tidy' command is needed only if you have previously built Postfix
without LDAP support.

Instead of '/usr/local' specify the actual locations of your LDAP include files
and libraries. Be sure to not mix LDAP include files and LDAP libraries of
different versions!!

If your LDAP libraries were built with Kerberos support, you'll also need to
include your Kerberos libraries in this line. Note that the KTH Kerberos IV
libraries might conflict with Postfix's lib/libdns.a, which defines dns_lookup.
If that happens, you'll probably want to link with LDAP libraries that lack
Kerberos support just to build Postfix, as it doesn't support Kerberos binds to
the LDAP server anyway. Sorry about the bother.

If you're using one of the Netscape LDAP SDKs, you'll need to change the
AUXLIBS line to point to libldap10.so or libldapssl30.so or whatever you have,
and you may need to use the appropriate linker option (e.g. '-R') so the
executables can find it at runtime.

Configuring LDAP lookups

In order to use LDAP lookups, define an LDAP source as a table lookup in
main.cf, for example:

    alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf

The file /etc/postfix/ldap-aliases.cf can specify a great number of parameters,
including parameters that enable LDAP SSL and STARTTLS. For a complete
description, see the ldap_table(5) manual page.

Example: local(8) aliases

Here's a basic example for using LDAP to look up local(8) aliases. Assume that
in main.cf, you have:

    alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf

and in ldap:/etc/postfix/ldap-aliases.cf you have:

    server_host = ldap.my.com
    search_base = dc=my, dc=com

Upon receiving mail for a local address "ldapuser" that isn't found in the /
etc/aliases database, Postfix will search the LDAP server listening at port 389
on ldap.my.com. It will bind anonymously, search for any directory entries
whose mailacceptinggeneralid attribute is "ldapuser", read the "maildrop"
attributes of those found, and build a list of their maildrops, which will be
treated as RFC822 addresses to which the message will be delivered.

Example: virtual domains/addresses

If you want to keep information for virtual lookups in your directory, it's
only a little more complicated. First, you need to make sure Postfix knows
about the virtual domain. An easy way to do that is to add the domain to the
mailacceptinggeneralid attribute of some entry in the directory. Next, you'll
want to make sure all of your virtual recipient's mailacceptinggeneralid
attributes are fully qualified with their virtual domains. Finally, if you want
to designate a directory entry as the default user for a virtual domain, just
give it an additional mailacceptinggeneralid (or the equivalent in your
directory) of "@virtual.dom". That's right, no user part. If you don't want a
catchall user, omit this step and mail to unknown users in the domain will
simply bounce.

In summary, you might have a catchall user for a virtual domain that looks like
this:

         dn: cn=defaultrecipient, dc=fake, dc=dom
         objectclass: top
         objectclass: virtualaccount
         cn: defaultrecipient
         owner: uid=root, dc=someserver, dc=isp, dc=dom
    1 -> mailacceptinggeneralid: fake.dom
    2 -> mailacceptinggeneralid: @fake.dom
    3 -> maildrop: realuser@real.dom

    1: Postfix knows fake.dom is a valid virtual domain when it looks for this
    and gets something (the maildrop) back.

    2: This causes any mail for unknown users in fake.dom to go to this entry
    ...

    3: ... and then to its maildrop.

Normal users might simply have one mailacceptinggeneralid and maildrop, e.g.
"normaluser@fake.dom" and "normaluser@real.dom".

Other uses of LDAP lookups

Other common uses for LDAP lookups include rewriting senders and recipients
with Postfix's canonical lookups, for example in order to make mail leaving
your site appear to be coming from "First.Last@site.dom" instead of
"userid@site.dom".

Notes and things to think about

  * The bits of schema and attribute names used in this document are just
    examples. There's nothing special about them, other than that some are the
    defaults in the LDAP configuration parameters. You can use whatever schema
    you like, and configure Postfix accordingly.

  * You probably want to make sure that mailacceptinggeneralids are unique, and
    that not just anyone can specify theirs as postmaster or root, say.

  * An entry can have an arbitrary number of mailacceptinggeneralids or
    maildrops. Maildrops can also be comma-separated lists of addresses. They
    will all be found and returned by the lookups. For example, you could
    define an entry intended for use as a mailing list that looks like this
    (Warning! Schema made up just for this example):

        dn: cn=Accounting Staff List, dc=my, dc=com
        cn: Accounting Staff List
        o: my.com
        objectclass: maillist
        mailacceptinggeneralid: accountingstaff
        mailacceptinggeneralid: accounting-staff
        maildrop: mylist-owner
        maildrop: an-accountant
        maildrop: some-other-accountant
        maildrop: this, that, theother

  * If you use an LDAP map for lookups other than aliases, you may have to make
    sure the lookup makes sense. In the case of virtual lookups, maildrops
    other than mail addresses are pretty useless, because Postfix can't know
    how to set the ownership for program or file delivery. Your query_filter
    should probably look something like this:

        query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
        (maildrop="*:*")(maildrop="*/*"))))

  * And for that matter, even for aliases, you may not want users able to
    specify their maildrops as programs, includes, etc. This might be
    particularly pertinent on a "sealed" server where they don't have local
    UNIX accounts, but exist only in LDAP and Cyrus. You might allow the fun
    stuff only for directory entries owned by an administrative account, so
    that if the object had a program as its maildrop and weren't owned by
    "cn=root" it wouldn't be returned as a valid local user. This will require
    some thought on your part to implement safely, considering the
    ramifications of this type of delivery. You may decide it's not worth the
    bother to allow any of that nonsense in LDAP lookups, ban it in the
    query_filter, and keep things like majordomo lists in local alias
    databases.

        query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
        (maildrop="*:*")(maildrop="*/*"))(owner=cn=root, dc=your, dc=com)))

  * LDAP lookups are slower than local DB or DBM lookups. For most sites they
    won't be a bottleneck, but it's a good idea to know how to tune your
    directory service.

  * Multiple LDAP maps share the same LDAP connection if they differ only in
    their query related parameters: base, scope, query_filter, and so on. To
    take advantage of this, avoid spurious differences in the definitions of
    LDAP maps: host selection order, version, bind, tls parameters, ... should
    be the same for multiple maps whenever possible.

Feedback

If you have questions, send them to postfix-users@postfix.org. Please include
relevant information about your Postfix setup: LDAP-related output from
postconf, which LDAP libraries you built with, and which directory server
you're using. If your question involves your directory contents, please include
the applicable bits of some directory entries.

Credits

  * Manuel Guesdon: Spotted a bug with the timeout attribute.
  * John Hensley: Multiple LDAP sources with more configurable attributes.
  * Carsten Hoeger: Search scope handling.
  * LaMont Jones: Domain restriction, URL and DN searches, multiple result
    attributes.
  * Mike Mattice: Alias dereferencing control.
  * Hery Rakotoarisoa: Patches for LDAPv3 updating.
  * Prabhat K Singh: Wrote the initial Postfix LDAP lookups and connection
    caching.
  * Keith Stevenson: RFC 2254 escaping in queries.
  * Samuel Tardieu: Noticed that searches could include wildcards, prompting
    the work on RFC 2254 escaping in queries. Spotted a bug in binding.
  * Sami Haahtinen: Referral chasing and v3 support.
  * Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
    OpenLDAP cache deprecation. Limits on recursion, expansion and search
    results size. LDAP connection sharing for maps differing only in the query
    parameters.
  * Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions
    in external files (ldap:/path/ldap.cf) needed to securely store passwords
    for plain auth.
  * Liviu Daia revised the configuration interface and added the main.cf
    configuration feature.
  * Liviu Daia with further refinements from Jose Luis Tallon and Victor
    Duchovni developed the common query, result_format, domain and
    expansion_limit interface for LDAP, MySQL and PosgreSQL.

And of course Wietse.

