Home | History | Annotate | Line # | Download | only in README_FILES
      1 s!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "https://
      2 www.w3.org/TR/html4/loose.dtd">
      3 
      4 PPoossttffiixx LLDDAAPP HHoowwttoo
      5 
      6 -------------------------------------------------------------------------------
      7 
      8 LLDDAAPP SSuuppppoorrtt iinn PPoossttffiixx
      9 
     10 Postfix can use an LDAP directory as a source for any of its lookups: aliases
     11 (5), virtual(5), canonical(5), etc. This allows you to keep information for
     12 your mail service in a replicated network database with fine-grained access
     13 controls. By not storing it locally on the mail server, the administrators can
     14 maintain it from anywhere, and the users can control whatever bits of it you
     15 think appropriate. You can have multiple mail servers using the same
     16 information, without the hassle and delay of having to copy it to each.
     17 
     18 Topics covered in this document:
     19 
     20   * Building Postfix with LDAP support
     21   * Configuring LDAP lookups
     22   * Example: aliases
     23   * Example: virtual domains/addresses
     24   * Example: expanding LDAP groups
     25   * Other uses of LDAP lookups
     26   * Notes and things to think about
     27   * Feedback
     28   * Credits
     29 
     30 BBuuiillddiinngg PPoossttffiixx wwiitthh LLDDAAPP ssuuppppoorrtt
     31 
     32 These instructions assume that you build Postfix from source code as described
     33 in the INSTALL document. Some modification may be required if you build Postfix
     34 from a vendor-specific source package.
     35 
     36 Note 1: Postfix no longer supports the LDAP version 1 interface.
     37 
     38 Note 2: to use LDAP with Debian GNU/Linux's Postfix, all you need is to install
     39 the postfix-ldap package and you're done. There is no need to recompile
     40 Postfix.
     41 
     42 You need to have LDAP libraries and include files installed somewhere on your
     43 system, and you need to configure the Postfix Makefiles accordingly.
     44 
     45 For example, to build the OpenLDAP libraries for use with Postfix (i.e. LDAP
     46 client code only), you could use the following command:
     47 
     48     % ./configure  --without-kerberos --without-cyrus-sasl --without-tls \
     49         --without-threads --disable-slapd --disable-slurpd \
     50         --disable-debug --disable-shared
     51 
     52 If you're using the libraries from OpenLDAP (https://www.openldap.org),
     53 something like this in the top level of your Postfix source tree should work:
     54 
     55     % make tidy
     56     % make makefiles CCARGS="-I/usr/local/include -DHAS_LDAP" \
     57         AUXLIBS_LDAP="-L/usr/local/lib -lldap -L/usr/local/lib -llber"
     58 
     59 If your LDAP shared library is in a directory that the RUN-TIME linker does not
     60 know about, add a "-Wl,-R,/path/to/directory" option after "-lldap".
     61 
     62 Postfix versions before 3.0 use AUXLIBS instead of AUXLIBS_LDAP. With Postfix
     63 3.0 and later, the old AUXLIBS variable still supports building a statically-
     64 loaded LDAP database client, but only the new AUXLIBS_LDAP variable supports
     65 building a dynamically-loaded or statically-loaded LDAP database client.
     66 
     67     Failure to use the AUXLIBS_LDAP variable will defeat the purpose of dynamic
     68     database client loading. Every Postfix executable file will have LDAP
     69     database library dependencies. And that was exactly what dynamic database
     70     client loading was meant to avoid.
     71 
     72 On Solaris 2.x you may have to specify run-time link information, otherwise
     73 ld.so will not find some of the shared libraries:
     74 
     75     % make tidy
     76     % make makefiles CCARGS="-I/usr/local/include -DHAS_LDAP" \
     77         AUXLIBS_LDAP="-L/usr/local/lib -R/usr/local/lib -lldap \
     78                 -L/usr/local/lib -R/usr/local/lib -llber"
     79 
     80 The 'make tidy' command is needed only if you have previously built Postfix
     81 without LDAP support.
     82 
     83 Instead of '/usr/local' specify the actual locations of your LDAP include files
     84 and libraries. Be sure to not mix LDAP include files and LDAP libraries of
     85 different versions!!
     86 
     87 If your LDAP libraries were built with Kerberos support, you'll also need to
     88 include your Kerberos libraries in this line. Note that the KTH Kerberos IV
     89 libraries might conflict with Postfix's lib/libdns.a, which defines dns_lookup.
     90 If that happens, you'll probably want to link with LDAP libraries that lack
     91 Kerberos support just to build Postfix, as it doesn't support Kerberos binds to
     92 the LDAP server anyway. Sorry about the bother.
     93 
     94 If you're using one of the Netscape LDAP SDKs, you'll need to change the
     95 AUXLIBS line to point to libldap10.so or libldapssl30.so or whatever you have,
     96 and you may need to use the appropriate linker option (e.g. '-R') so the
     97 executables can find it at runtime.
     98 
     99 If you are using OpenLDAP, and the libraries were built with SASL support, you
    100 can add -DUSE_LDAP_SASL to the CCARGS to enable SASL support. For example:
    101 
    102          CCARGS="-I/usr/local/include -DHAS_LDAP -DUSE_LDAP_SASL"
    103 
    104 CCoonnffiigguurriinngg LLDDAAPP llooookkuuppss
    105 
    106 In order to use LDAP lookups, define an LDAP source as a table lookup in
    107 main.cf, for example:
    108 
    109     alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf
    110 
    111 The file /etc/postfix/ldap-aliases.cf can specify a great number of parameters,
    112 including parameters that enable LDAP SSL or STARTTLS, and LDAP SASL. For a
    113 complete description, see the ldap_table(5) manual page.
    114 
    115 EExxaammppllee:: llooccaall((88)) aalliiaasseess
    116 
    117 Here's a basic example for using LDAP to look up local(8) aliases. Assume that
    118 in main.cf, you have:
    119 
    120     alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf
    121 
    122 and in ldap:/etc/postfix/ldap-aliases.cf you have:
    123 
    124     server_host = ldap.example.com
    125     search_base = dc=example, dc=com
    126 
    127 Upon receiving mail for a local address "ldapuser" that isn't found in the /
    128 etc/aliases database, Postfix will search the LDAP server listening at port 389
    129 on ldap.example.com. It will bind anonymously, search for any directory entries
    130 whose mailacceptinggeneralid attribute is "ldapuser", read the "maildrop"
    131 attributes of those found, and build a list of their maildrops, which will be
    132 treated as RFC822 addresses to which the message will be delivered.
    133 
    134 EExxaammppllee:: vviirrttuuaall ddoommaaiinnss//aaddddrreesssseess
    135 
    136 If you want to keep information for virtual lookups in your directory, it's
    137 only a little more complicated. First, you need to make sure Postfix knows
    138 about the virtual domain. An easy way to do that is to add the domain to the
    139 mailacceptinggeneralid attribute of some entry in the directory. Next, you'll
    140 want to make sure all of your virtual recipient's mailacceptinggeneralid
    141 attributes are fully qualified with their virtual domains. Finally, if you want
    142 to designate a directory entry as the default user for a virtual domain, just
    143 give it an additional mailacceptinggeneralid (or the equivalent in your
    144 directory) of "@fake.dom". That's right, no user part. If you don't want a
    145 catchall user, omit this step and mail to unknown users in the domain will
    146 simply bounce.
    147 
    148 In summary, you might have a catchall user for a virtual domain that looks like
    149 this:
    150 
    151          dn: cn=defaultrecipient, dc=fake, dc=dom
    152          objectclass: top
    153          objectclass: virtualaccount
    154          cn: defaultrecipient
    155          owner: uid=root, dc=someserver, dc=isp, dc=dom
    156     1 -> mailacceptinggeneralid: fake.dom
    157     2 -> mailacceptinggeneralid: @fake.dom
    158     3 -> maildrop: realuser (a] real.dom
    159 
    160     1: Postfix knows fake.dom is a valid virtual domain when it looks for this
    161     and gets something (the maildrop) back.
    162 
    163     2: This causes any mail for unknown users in fake.dom to go to this entry
    164     ...
    165 
    166     3: ... and then to its maildrop.
    167 
    168 Normal users might simply have one mailacceptinggeneralid and maildrop, e.g.
    169 "normaluser (a] fake.dom" and "normaluser (a] real.dom".
    170 
    171 EExxaammppllee:: eexxppaannddiinngg LLDDAAPP ggrroouuppss
    172 
    173 LDAP is frequently used to store group member information. There are a number
    174 of ways of handling LDAP groups. We will show a few examples in order of
    175 increasing complexity, but owing to the number of independent variables, we can
    176 only present a tiny portion of the solution space. We show how to:
    177 
    178  1. query groups as lists of addresses;
    179 
    180  2. query groups as lists of user objects containing addresses;
    181 
    182  3. forward special lists unexpanded to a separate list server, for moderation
    183     or other processing;
    184 
    185  4. handle complex schemas by controlling expansion and by treating leaf nodes
    186     specially, using features that are new in Postfix 2.4.
    187 
    188 The example LDAP entries and implied schema below show two group entries
    189 ("agroup" and "bgroup") and four user entries ("auser", "buser", "cuser" and
    190 "duser"). The group "agroup" has the users "auser" (1) and "buser" (2) as
    191 members via DN references in the multi-valued attribute "memberdn", and direct
    192 email addresses of two external users "auser (a] example.org" (3) and
    193 "buser (a] example.org" (4) stored in the multi-valued attribute "memberaddr". The
    194 same is true of "bgroup" and "cuser"/"duser" (6)/(7)/(8)/(9), but "bgroup" also
    195 has a "maildrop" attribute of "bgroup (a] mlm.example.com" (5):
    196 
    197          dn: cn=agroup, dc=example, dc=com
    198          objectclass: top
    199          objectclass: ldapgroup
    200          cn: agroup
    201          mail: agroup (a] example.com
    202     1 -> memberdn: uid=auser, dc=example, dc=com
    203     2 -> memberdn: uid=buser, dc=example, dc=com
    204     3 -> memberaddr: auser (a] example.org
    205     4 -> memberaddr: buser (a] example.org
    206 
    207          dn: cn=bgroup, dc=example, dc=com
    208          objectclass: top
    209          objectclass: ldapgroup
    210          cn: bgroup
    211          mail: bgroup (a] example.com
    212     5 -> maildrop: bgroup (a] mlm.example.com
    213     6 -> memberdn: uid=cuser, dc=example, dc=com
    214     7 -> memberdn: uid=duser, dc=example, dc=com
    215     8 -> memberaddr: cuser (a] example.org
    216     9 -> memberaddr: duser (a] example.org
    217 
    218          dn: uid=auser, dc=example, dc=com
    219          objectclass: top
    220          objectclass: ldapuser
    221          uid: auser
    222     10 -> mail: auser (a] example.com
    223     11 -> maildrop: auser (a] mailhub.example.com
    224 
    225          dn: uid=buser, dc=example, dc=com
    226          objectclass: top
    227          objectclass: ldapuser
    228          uid: buser
    229     12 -> mail: buser (a] example.com
    230     13 -> maildrop: buser (a] mailhub.example.com
    231 
    232          dn: uid=cuser, dc=example, dc=com
    233          objectclass: top
    234          objectclass: ldapuser
    235          uid: cuser
    236     14 -> mail: cuser (a] example.com
    237 
    238          dn: uid=duser, dc=example, dc=com
    239          objectclass: top
    240          objectclass: ldapuser
    241          uid: duser
    242     15 -> mail: duser (a] example.com
    243 
    244 Our first use case ignores the "memberdn" attributes, and assumes that groups
    245 hold only direct "memberaddr" strings as in (3), (4), (8) and (9). The goal is
    246 to map the group address to the list of constituent "memberaddr" values. This
    247 is simple, ignoring the various connection related settings (hosts, ports, bind
    248 settings, timeouts, ...) we have:
    249 
    250         simple.cf:
    251             ...
    252             search_base = dc=example, dc=com
    253             query_filter = mail=%s
    254             result_attribute = memberaddr
    255         $ postmap -q agroup (a] example.com ldap:/etc/postfix/simple.cf \
    256             auser (a] example.org,buser (a] example.org
    257 
    258 We search "dc=example, dc=com". The "mail" attribute is used in the
    259 query_filter to locate the right group, the "result_attribute" setting
    260 described in ldap_table(5) is used to specify that "memberaddr" values from the
    261 matching group are to be returned as a comma separated list. Always check
    262 tables using postmap(1) with the "-q" option, before deploying them into
    263 production use in main.cf.
    264 
    265 Our second use case instead expands "memberdn" attributes (1), (2), (6) and
    266 (7), follows the DN references and returns the "maildrop" of the referenced
    267 user entries. Here we use the "special_result_attribute" setting from
    268 ldap_table(5) to designate the "memberdn" attribute as holding DNs of the
    269 desired member entries. The "result_attribute" setting selects which attributes
    270 are returned from the selected DNs. It is important to choose a result
    271 attribute that is not also present in the group object, because result
    272 attributes are collected from both the group and the member DNs. In this case
    273 we choose "maildrop" and assume for the moment that groups never have a
    274 "maildrop" (the "bgroup" "maildrop" attribute is for a different use case). The
    275 returned data for "auser" and "buser" is from items (11) and (13) in the
    276 example data.
    277 
    278         special.cf:
    279             ...
    280             search_base = dc=example, dc=com
    281             query_filter = mail=%s
    282             result_attribute = maildrop
    283             special_result_attribute = memberdn
    284         $ postmap -q agroup (a] example.com ldap:/etc/postfix/special.cf \
    285             auser (a] mailhub.example.com,buser (a] mailhub.example.com
    286 
    287 Note: if the desired member object result attribute is always also present in
    288 the group, you get surprising results: the expansion also returns the address
    289 of the group. This is a known limitation of Postfix releases prior to 2.4, and
    290 is addressed in the new with Postfix 2.4 "leaf_result_attribute" feature
    291 described in ldap_table(5).
    292 
    293 Our third use case has some groups that are expanded immediately, and other
    294 groups that are forwarded to a dedicated mailing list manager host for delayed
    295 expansion. This uses two LDAP tables, one for users and forwarded groups and a
    296 second for groups that can be expanded immediately. It is assumed that groups
    297 that require forwarding are never nested members of groups that are directly
    298 expanded.
    299 
    300         no_expand.cf:
    301             ...
    302             search_base = dc=example, dc=com
    303             query_filter = mail=%s
    304             result_attribute = maildrop
    305         expand.cf
    306             ...
    307             search_base = dc=example, dc=com
    308             query_filter = mail=%s
    309             result_attribute = maildrop
    310             special_result_attribute = memberdn
    311         $ postmap -q auser (a] example.com \
    312             ldap:/etc/postfix/no_expand.cf ldap:/etc/postfix/expand.cf \
    313             auser (a] mailhub.example.com
    314         $ postmap -q agroup (a] example.com \
    315             ldap:/etc/postfix/no_expand.cf ldap:/etc/postfix/expand.cf \
    316             auser (a] mailhub.example.com,buser (a] mailhub.example.com
    317         $ postmap -q bgroup (a] example.com \
    318             ldap:/etc/postfix/no_expand.cf ldap:/etc/postfix/expand.cf \
    319             bgroup (a] mlm.example.com
    320 
    321 Non-group objects and groups with delayed expansion (those that have a maildrop
    322 attribute) are rewritten to a single maildrop value. Groups that don't have a
    323 maildrop are expanded as the second use case. This admits a more elegant
    324 solution with Postfix 2.4 and later.
    325 
    326 Our final use case is the same as the third, but this time uses new features in
    327 Postfix 2.4. We now are able to use just one LDAP table and no longer need to
    328 assume that forwarded groups are never nested inside expanded groups.
    329 
    330         fancy.cf:
    331             ...
    332             search_base = dc=example, dc=com
    333             query_filter = mail=%s
    334             result_attribute = memberaddr
    335             special_result_attribute = memberdn
    336             terminal_result_attribute = maildrop
    337             leaf_result_attribute = mail
    338         $ postmap -q auser (a] example.com ldap:/etc/postfix/fancy.cf \
    339             auser (a] mailhub.example.com
    340         $ postmap -q cuser (a] example.com ldap:/etc/postfix/fancy.cf \
    341             cuser (a] example.com
    342         $ postmap -q agroup (a] example.com ldap:/etc/postfix/fancy.cf \
    343 
    344     auser (a] mailhub.example.com,buser (a] mailhub.example.com,auser (a] example.org,buser (a] example.org
    345         $ postmap -q bgroup (a] example.com ldap:/etc/postfix/fancy.cf \
    346             bgroup (a] mlm.example.com
    347 
    348 Above, delayed expansion is enabled via "terminal_result_attribute", which, if
    349 present, is used as the sole result and all other expansion is suppressed.
    350 Otherwise, the "leaf_result_attribute" is only returned for leaf objects that
    351 don't have a "special_result_attribute" (non-groups), while the
    352 "result_attribute" (direct member address of groups) is returned at every level
    353 of recursive expansion, not just the leaf nodes. This fancy example illustrates
    354 all the features of Postfix 2.4 group expansion.
    355 
    356 OOtthheerr uusseess ooff LLDDAAPP llooookkuuppss
    357 
    358 Other common uses for LDAP lookups include rewriting senders and recipients
    359 with Postfix's canonical lookups, for example in order to make mail leaving
    360 your site appear to be coming from "First.Last (a] example.com" instead of
    361 "userid (a] example.com".
    362 
    363 NNootteess aanndd tthhiinnggss ttoo tthhiinnkk aabboouutt
    364 
    365   * The bits of schema and attribute names used in this document are just
    366     examples. There's nothing special about them, other than that some are the
    367     defaults in the LDAP configuration parameters. You can use whatever schema
    368     you like, and configure Postfix accordingly.
    369 
    370   * You probably want to make sure that mailacceptinggeneralids are unique, and
    371     that not just anyone can specify theirs as postmaster or root, say.
    372 
    373   * An entry can have an arbitrary number of mailacceptinggeneralids or
    374     maildrops. Maildrops can also be comma-separated lists of addresses. They
    375     will all be found and returned by the lookups. For example, you could
    376     define an entry intended for use as a mailing list that looks like this
    377     (Warning! Schema made up just for this example):
    378 
    379         dn: cn=Accounting Staff List, dc=example, dc=com
    380         cn: Accounting Staff List
    381         o: example.com
    382         objectclass: maillist
    383         mailacceptinggeneralid: accountingstaff
    384         mailacceptinggeneralid: accounting-staff
    385         maildrop: mylist-owner
    386         maildrop: an-accountant
    387         maildrop: some-other-accountant
    388         maildrop: this, that, theother
    389 
    390   * If you use an LDAP map for lookups other than aliases, you may have to make
    391     sure the lookup makes sense. In the case of virtual lookups, maildrops
    392     other than mail addresses are pretty useless, because Postfix can't know
    393     how to set the ownership for program or file delivery. Your qquueerryy__ffiilltteerr
    394     should probably look something like this:
    395 
    396         query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
    397         (maildrop="*:*")(maildrop="*/*"))))
    398 
    399   * And for that matter, even for aliases, you may not want users to be able to
    400     specify their maildrops as programs, includes, etc. This might be
    401     particularly pertinent on a "sealed" server where they don't have local
    402     UNIX accounts, but exist only in LDAP and Cyrus. You might allow the fun
    403     stuff only for directory entries owned by an administrative account, so
    404     that if the object had a program as its maildrop and weren't owned by
    405     "cn=root" it wouldn't be returned as a valid local user. This will require
    406     some thought on your part to implement safely, considering the
    407     ramifications of this type of delivery. You may decide it's not worth the
    408     bother to allow any of that nonsense in LDAP lookups, ban it in the
    409     qquueerryy__ffiilltteerr, and keep things like majordomo lists in local alias
    410     databases.
    411 
    412         query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
    413         (maildrop="*:*")(maildrop="*/*"))(owner=cn=root, dc=your, dc=com)))
    414 
    415   * LDAP lookups are slower than local DB or DBM lookups. For most sites they
    416     won't be a bottleneck, but it's a good idea to know how to tune your
    417     directory service.
    418 
    419   * Multiple LDAP maps share the same LDAP connection if they differ only in
    420     their query related parameters: base, scope, query_filter, and so on. To
    421     take advantage of this, avoid spurious differences in the definitions of
    422     LDAP maps: host selection order, version, bind, tls parameters, ... should
    423     be the same for multiple maps whenever possible.
    424 
    425 FFeeeeddbbaacckk
    426 
    427 If you have questions, send them to postfix-users (a] postfix.org. Please include
    428 relevant information about your Postfix setup: LDAP-related output from
    429 postconf, which LDAP libraries you built with, and which directory server
    430 you're using. If your question involves your directory contents, please include
    431 the applicable bits of some directory entries.
    432 
    433 CCrreeddiittss
    434 
    435   * Manuel Guesdon: Spotted a bug with the timeout attribute.
    436   * John Hensley: Multiple LDAP sources with more configurable attributes.
    437   * Carsten Hoeger: Search scope handling.
    438   * LaMont Jones: Domain restriction, URL and DN searches, multiple result
    439     attributes.
    440   * Mike Mattice: Alias dereferencing control.
    441   * Hery Rakotoarisoa: Patches for LDAPv3 updating.
    442   * Prabhat K Singh: Wrote the initial Postfix LDAP lookups and connection
    443     caching.
    444   * Keith Stevenson: RFC 2254 escaping in queries.
    445   * Samuel Tardieu: Noticed that searches could include wildcards, prompting
    446     the work on RFC 2254 escaping in queries. Spotted a bug in binding.
    447   * Sami Haahtinen: Referral chasing and v3 support.
    448   * Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
    449     OpenLDAP cache deprecation. Limits on recursion, expansion and search
    450     results size. LDAP connection sharing for maps differing only in the query
    451     parameters.
    452   * Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions
    453     in external files (ldap:/path/ldap.cf) needed to securely store passwords
    454     for plain auth.
    455   * Liviu Daia revised the configuration interface and added the main.cf
    456     configuration feature.
    457   * Liviu Daia with further refinements from Jose Luis Tallon and Victor
    458     Duchovni developed the common query, result_format, domain and
    459     expansion_limit interface for LDAP, MySQL and PosgreSQL.
    460   * Gunnar Wrobel provided a first implementation of a feature to limit LDAP
    461     search results to leaf nodes only. Victor generalized this into the Postfix
    462     2.4 "leaf_result_attribute" feature.
    463   * Quanah Gibson-Mount contributed support for advanced LDAP SASL mechanisms,
    464     beyond the password-based LDAP "simple" bind.
    465 
    466 And of course Wietse.
    467 
    468