1 <!doctype html public
"-//W3C//DTD HTML 4.01 Transitional//EN"
2 "http://www.w3.org/TR/html4/loose.dtd">
8 <title>Postfix LDAP Howto
</title>
10 <meta http-equiv=
"Content-Type" content=
"text/html; charset=us-ascii">
16 <h1><img src=
"postfix-logo.jpg" width=
"203" height=
"98" ALT=
"">Postfix LDAP Howto
</h1>
20 <h2>LDAP Support in Postfix
</h2>
22 <p> Postfix can use an LDAP directory as a source for any of its
23 lookups:
<a href=
"aliases.5.html">aliases(
5)
</a>,
<a href=
"virtual.5.html">virtual(
5)
</a>,
<a href=
"canonical.5.html">canonical(
5)
</a>, etc. This allows
24 you to keep information for your mail service in a replicated
25 network database with fine-grained access controls. By not storing
26 it locally on the mail server, the administrators can maintain it
27 from anywhere, and the users can control whatever bits of it you
28 think appropriate. You can have multiple mail servers using the
29 same information, without the hassle and delay of having to copy
32 <p> Topics covered in this document:
</p>
36 <li><a href=
"#build">Building Postfix with LDAP support
</a>
38 <li><a href=
"#config">Configuring LDAP lookups
</a>
40 <li><a href=
"#example_alias">Example: aliases
</a>
42 <li><a href=
"#example_virtual">Example: virtual domains/addresses
</a>
44 <li><a href=
"#example_group">Example: expanding LDAP groups
</a>
46 <li><a href=
"#other">Other uses of LDAP lookups
</a>
48 <li><a href=
"#hmmmm">Notes and things to think about
</a>
50 <li><a href=
"#feedback">Feedback
</a>
52 <li><a href=
"#credits">Credits
</a>
56 <h2><a name=
"build">Building Postfix with LDAP support
</a></h2>
58 <p> These instructions assume that you build Postfix from source
59 code as described in the
<a href=
"INSTALL.html">INSTALL
</a> document. Some modification may
60 be required if you build Postfix from a vendor-specific source
63 <p> Note
1: Postfix no longer supports the LDAP version
1 interface.
66 <p> Note
2: to use LDAP with Debian GNU/Linux's Postfix, all you
67 need is to install the postfix-ldap package and you're done. There
68 is no need to recompile Postfix.
</p>
70 <p> You need to have LDAP libraries and include files installed
71 somewhere on your system, and you need to configure the Postfix
72 Makefiles accordingly.
</p>
74 <p> For example, to build the OpenLDAP libraries for use with
75 Postfix (i.e. LDAP client code only), you could use the following
80 % ./configure --without-kerberos --without-cyrus-sasl --without-tls \
81 --without-threads --disable-slapd --disable-slurpd \
82 --disable-debug --disable-shared
86 <p> If you're using the libraries from the UM distribution
87 (
<a href=
"http://www.umich.edu/~dirsvcs/ldap/ldap.html">http://www.umich.edu/~dirsvcs/ldap/ldap.html
</a>) or OpenLDAP
88 (
<a href=
"http://www.openldap.org">http://www.openldap.org
</a>), something like this in the top level of
89 your Postfix source tree should work:
</p>
94 % make makefiles
CCARGS=
"-I/usr/local/include -DHAS_LDAP" \
95 AUXLIBS=
"-L/usr/local/lib -lldap -L/usr/local/lib -llber"
99 <p> On Solaris
2.x you may have to specify run-time link information,
100 otherwise ld.so will not find some of the shared libraries:
</p>
105 % make makefiles
CCARGS=
"-I/usr/local/include -DHAS_LDAP" \
106 AUXLIBS=
"-L/usr/local/lib -R/usr/local/lib -lldap \
107 -L/usr/local/lib -R/usr/local/lib -llber"
111 <p> The 'make tidy' command is needed only if you have previously
112 built Postfix without LDAP support.
</p>
114 <p> Instead of '/usr/local' specify the actual locations of your
115 LDAP include files and libraries. Be sure to not mix LDAP include
116 files and LDAP libraries of different versions!!
</p>
118 <p> If your LDAP libraries were built with Kerberos support, you'll
119 also need to include your Kerberos libraries in this line. Note
120 that the KTH Kerberos IV libraries might conflict with Postfix's
121 lib/libdns.a, which defines dns_lookup. If that happens, you'll
122 probably want to link with LDAP libraries that lack Kerberos support
123 just to build Postfix, as it doesn't support Kerberos binds to the
124 LDAP server anyway. Sorry about the bother.
</p>
126 <p> If you're using one of the Netscape LDAP SDKs, you'll need to
127 change the AUXLIBS line to point to libldap10.so or libldapssl30.so
128 or whatever you have, and you may need to use the appropriate linker
129 option (e.g. '-R') so the executables can find it at runtime.
</p>
131 <p> If you are using OpenLDAP, and the libraries were built with SASL
132 support, you can add -DUSE_LDAP_SASL to the CCARGS to enable SASL support.
137 CCARGS=
"-I/usr/local/include -DHAS_LDAP -DUSE_LDAP_SASL"
141 <h2><a name=
"config">Configuring LDAP lookups
</a></h2>
143 <p> In order to use LDAP lookups, define an LDAP source
144 as a table lookup in
<a href=
"postconf.5.html">main.cf
</a>, for example:
</p>
148 <a href=
"postconf.5.html#alias_maps">alias_maps
</a> = hash:/etc/aliases,
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/ldap-aliases.cf
152 <p> The file /etc/postfix/ldap-aliases.cf can specify a great number
153 of parameters, including parameters that enable LDAP SSL or STARTTLS,
154 and LDAP SASL. For a complete description, see the
<a href=
"ldap_table.5.html">ldap_table(
5)
</a>
157 <h2><a name=
"example_alias">Example: local(
8) aliases
</a></h2>
159 <p> Here's a basic example for using LDAP to look up
<a href=
"local.8.html">local(
8)
</a>
160 aliases. Assume that in
<a href=
"postconf.5.html">main.cf
</a>, you have:
</p>
164 <a href=
"postconf.5.html#alias_maps">alias_maps
</a> = hash:/etc/aliases,
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/ldap-aliases.cf
168 <p> and in
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/ldap-aliases.cf you have:
</p>
172 server_host = ldap.example.com
173 search_base = dc=example, dc=com
177 <p> Upon receiving mail for a local address
"ldapuser" that isn't
178 found in the /etc/aliases database, Postfix will search the LDAP
179 server listening at port
389 on ldap.example.com. It will bind anonymously,
180 search for any directory entries whose mailacceptinggeneralid
181 attribute is
"ldapuser", read the
"maildrop" attributes of those
182 found, and build a list of their maildrops, which will be treated
183 as
<a href=
"http://tools.ietf.org/html/rfc822">RFC822
</a> addresses to which the message will be delivered.
</p>
185 <h2><a name=
"example_virtual">Example: virtual domains/addresses
</a></h2>
187 <p> If you want to keep information for virtual lookups in your
188 directory, it's only a little more complicated. First, you need to
189 make sure Postfix knows about the virtual domain. An easy way to
190 do that is to add the domain to the mailacceptinggeneralid attribute
191 of some entry in the directory. Next, you'll want to make sure all
192 of your virtual recipient's mailacceptinggeneralid attributes are
193 fully qualified with their virtual domains. Finally, if you want
194 to designate a directory entry as the default user for a virtual
195 domain, just give it an additional mailacceptinggeneralid (or the
196 equivalent in your directory) of
"@fake.dom". That's right, no
197 user part. If you don't want a catchall user, omit this step and
198 mail to unknown users in the domain will simply bounce.
</p>
200 <p> In summary, you might have a catchall user for a virtual domain
201 that looks like this:
</p>
205 dn: cn=defaultrecipient, dc=fake, dc=dom
207 objectclass: virtualaccount
209 owner: uid=root, dc=someserver, dc=isp, dc=dom
210 1 -
> mailacceptinggeneralid: fake.dom
211 2 -
> mailacceptinggeneralid: @fake.dom
212 3 -
> maildrop: realuser@real.dom
218 <dd> <p> 1: Postfix knows fake.dom is a valid virtual domain when
219 it looks for this and gets something (the maildrop) back.
</p>
221 <dd> <p> 2: This causes any mail for unknown users in fake.dom to
222 go to this entry ...
</p>
224 <dd> <p> 3: ... and then to its maildrop.
</p>
228 <p> Normal users might simply have one mailacceptinggeneralid and
229 <a href=
"QSHAPE_README.html#maildrop_queue">maildrop
</a>, e.g.
"normaluser@fake.dom" and
"normaluser@real.dom".
232 <h2><a name=
"example_group">Example: expanding LDAP groups
</a></h2>
235 LDAP is frequently used to store group member information. There are a
236 number of ways of handling LDAP groups. We will show a few examples in
237 order of increasing complexity, but owing to the number of independent
238 variables, we can only present a tiny portion of the solution space.
244 <li> <p> query groups as lists of addresses;
</p>
246 <li> <p> query groups as lists of user objects containing addresses;
</p>
248 <li> <p> forward special lists unexpanded to a separate list server,
249 for moderation or other processing;
</p>
251 <li> <p> handle complex schemas by controlling expansion and by treating
252 leaf nodes specially, using features that are new in Postfix
2.4.
</p>
257 The example LDAP entries and implied schema below show two group entries
258 (
"agroup" and
"bgroup") and four user entries (
"auser",
"buser",
"cuser"
259 and
"duser"). The group
"agroup" has the users
"auser" (
1) and
"buser" (
2)
260 as members via DN references in the multi-valued attribute
"memberdn", and
261 direct email addresses of two external users
"auser@example.org" (
3) and
262 "buser@example.org" (
4) stored in the multi-valued attribute
"memberaddr".
263 The same is true of
"bgroup" and
"cuser"/
"duser" (
6)/(
7)/(
8)/(
9), but
264 "bgroup" also has a
"maildrop" attribute of
"bgroup@mlm.example.com"
269 dn: cn=agroup, dc=example, dc=com
271 objectclass: ldapgroup
273 mail: agroup@example.com
274 1 -
> memberdn: uid=auser, dc=example, dc=com
275 2 -
> memberdn: uid=buser, dc=example, dc=com
276 3 -
> memberaddr: auser@example.org
277 4 -
> memberaddr: buser@example.org
282 dn: cn=bgroup, dc=example, dc=com
284 objectclass: ldapgroup
286 mail: bgroup@example.com
287 5 -
> maildrop: bgroup@mlm.example.com
288 6 -
> memberdn: uid=cuser, dc=example, dc=com
289 7 -
> memberdn: uid=duser, dc=example, dc=com
290 8 -
> memberaddr: cuser@example.org
291 9 -
> memberaddr: duser@example.org
296 dn: uid=auser, dc=example, dc=com
298 objectclass: ldapuser
300 10 -
> mail: auser@example.com
301 11 -
> maildrop: auser@mailhub.example.com
306 dn: uid=buser, dc=example, dc=com
308 objectclass: ldapuser
310 12 -
> mail: buser@example.com
311 13 -
> maildrop: buser@mailhub.example.com
316 dn: uid=cuser, dc=example, dc=com
318 objectclass: ldapuser
320 14 -
> mail: cuser@example.com
325 dn: uid=duser, dc=example, dc=com
327 objectclass: ldapuser
329 15 -
> mail: duser@example.com
335 <p> Our first use case ignores the
"memberdn" attributes, and assumes
336 that groups hold only direct
"memberaddr" strings as in (
3), (
4), (
8) and
337 (
9). The goal is to map the group address to the list of constituent
338 "memberaddr" values. This is simple, ignoring the various connection
339 related settings (hosts, ports, bind settings, timeouts, ...) we have:
346 search_base = dc=example, dc=com
347 query_filter = mail=%s
348 result_attribute = memberaddr
349 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/simple.cf \
350 auser@example.org,buser@example.org
354 <p> We search
"dc=example, dc=com". The
"mail" attribute is used in the
355 query_filter to locate the right group, the
"result_attribute" setting
356 described in
<a href=
"ldap_table.5.html">ldap_table(
5)
</a> is used to specify that
"memberaddr" values
357 from the matching group are to be returned as a comma separated list.
358 Always check tables using
<a href=
"postmap.1.html">postmap(
1)
</a> with the
"-q" option, before
359 deploying them into production use in
<a href=
"postconf.5.html">main.cf
</a>.
</p>
361 <p> Our second use case instead expands
"memberdn" attributes (
1), (
2),
362 (
6) and (
7), follows the DN references and returns the
"maildrop" of the
363 referenced user entries. Here we use the
"special_result_attribute"
364 setting from
<a href=
"ldap_table.5.html">ldap_table(
5)
</a> to designate the
"memberdn" attribute
365 as holding DNs of the desired member entries. The
"result_attribute"
366 setting selects which attributes are returned from the selected DNs. It
367 is important to choose a result attribute that is not also present in
368 the group object, because result attributes are collected from both
369 the group and the member DNs. In this case we choose
"maildrop" and
370 assume for the moment that groups never have a
"maildrop" (the
"bgroup"
371 "maildrop" attribute is for a different use case). The returned data for
372 "auser" and
"buser" is from items (
11) and (
13) in the example data.
</p>
378 search_base = dc=example, dc=com
379 query_filter = mail=%s
380 result_attribute = maildrop
381 special_result_attribute = memberdn
382 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/special.cf \
383 auser@mailhub.example.com,buser@mailhub.example.com
387 <p> Note: if the desired member object result attribute is always also
388 present in the group, you get surprising results: the expansion also
389 returns the address of the group. This is a known limitation of Postfix
390 releases prior to
2.4, and is addressed in the new with Postfix
2.4
391 "leaf_result_attribute" feature described in
<a href=
"ldap_table.5.html">ldap_table(
5)
</a>.
</p>
393 <p> Our third use case has some groups that are expanded immediately,
394 and other groups that are forwarded to a dedicated mailing list manager
395 host for delayed expansion. This uses two LDAP tables, one for users
396 and forwarded groups and a second for groups that can be expanded
397 immediately. It is assumed that groups that require forwarding are
398 never nested members of groups that are directly expanded.
</p>
404 search_base = dc=example, dc=com
405 query_filter = mail=%s
406 result_attribute = maildrop
409 search_base = dc=example, dc=com
410 query_filter = mail=%s
411 result_attribute = maildrop
412 special_result_attribute = memberdn
413 $ postmap -q auser@example.com \
414 <a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/expand.cf \
415 auser@mailhub.example.com
416 $ postmap -q agroup@example.com \
417 <a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/expand.cf \
418 auser@mailhub.example.com,buser@mailhub.example.com
419 $ postmap -q bgroup@example.com \
420 <a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/expand.cf \
421 bgroup@mlm.example.com
425 <p> Non-group objects and groups with delayed expansion (those that have a
426 maildrop attribute) are rewritten to a single maildrop value. Groups that
427 don't have a maildrop are expanded as the second use case. This admits
428 a more elegant solution with Postfix
2.4 and later.
</p>
430 <p> Our final use case is the same as the third, but this time uses new
431 features in Postfix
2.4. We now are able to use just one LDAP table and
432 no longer need to assume that forwarded groups are never nested inside
433 expanded groups.
</p>
439 search_base = dc=example, dc=com
440 query_filter = mail=%s
441 result_attribute = memberaddr
442 special_result_attribute = memberdn
443 terminal_result_attribute = maildrop
444 leaf_result_attribute = mail
445 $ postmap -q auser@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/fancy.cf \
446 auser@mailhub.example.com
447 $ postmap -q cuser@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/fancy.cf \
449 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/fancy.cf \
450 auser@mailhub.example.com,buser@mailhub.example.com,auser@example.org,buser@example.org
451 $ postmap -q bgroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/fancy.cf \
452 bgroup@mlm.example.com
456 <p> Above, delayed expansion is enabled via
"terminal_result_attribute",
457 which, if present, is used as the sole result and all other expansion is
458 suppressed. Otherwise, the
"leaf_result_attribute" is only returned for
459 leaf objects that don't have a
"special_result_attribute" (non-groups),
460 while the
"result_attribute" (direct member address of groups) is returned
461 at every level of recursive expansion, not just the leaf nodes. This fancy
462 example illustrates all the features of Postfix
2.4 group expansion.
</p>
464 <h2><a name=
"other">Other uses of LDAP lookups
</a></h2>
466 Other common uses for LDAP lookups include rewriting senders and
467 recipients with Postfix's canonical lookups, for example in order
468 to make mail leaving your site appear to be coming from
469 "First.Last@example.com" instead of
"userid@example.com".
471 <h2><a name=
"hmmmm">Notes and things to think about
</a></h2>
475 <li> <p> The bits of schema and attribute names used in this document are just
476 examples. There's nothing special about them, other than that some are
477 the defaults in the LDAP configuration parameters. You can use
478 whatever schema you like, and configure Postfix accordingly.
</p>
480 <li> <p> You probably want to make sure that mailacceptinggeneralids are
481 unique, and that not just anyone can specify theirs as postmaster or
484 <li> <p> An entry can have an arbitrary number of mailacceptinggeneralids or
485 maildrops. Maildrops can also be comma-separated lists of addresses.
486 They will all be found and returned by the lookups. For example, you
487 could define an entry intended for use as a mailing list that looks
488 like this (Warning! Schema made up just for this example):
</p>
492 dn: cn=Accounting Staff List, dc=example, dc=com
493 cn: Accounting Staff List
495 objectclass: maillist
496 mailacceptinggeneralid: accountingstaff
497 mailacceptinggeneralid: accounting-staff
498 maildrop: mylist-owner
499 maildrop: an-accountant
500 maildrop: some-other-accountant
501 maildrop: this, that, theother
505 <li> <p> If you use an LDAP map for lookups other than aliases, you may have to
506 make sure the lookup makes sense. In the case of virtual lookups,
507 maildrops other than mail addresses are pretty useless, because
508 Postfix can't know how to set the ownership for program or file
509 delivery. Your
<b>query_filter
</b> should probably look something like this:
</p>
513 query_filter = (
&(mailacceptinggeneralid=%s)(!(|(
maildrop=
"*|*")(
maildrop=
"*:*")(
maildrop=
"*/*"))))
517 <li> <p> And for that matter, even for aliases, you may not want users able to
518 specify their maildrops as programs, includes, etc. This might be
519 particularly pertinent on a
"sealed" server where they don't have
520 local UNIX accounts, but exist only in LDAP and Cyrus. You might allow
521 the fun stuff only for directory entries owned by an administrative
523 so that if the object had a program as its maildrop and weren't owned
524 by
"cn=root" it wouldn't be returned as a valid local user. This will
525 require some thought on your part to implement safely, considering the
526 ramifications of this type of delivery. You may decide it's not worth
527 the bother to allow any of that nonsense in LDAP lookups, ban it in
528 the
<b>query_filter
</b>, and keep things like majordomo lists in local alias
533 query_filter = (
&(mailacceptinggeneralid=%s)(!(|(
maildrop=
"*|*")(
maildrop=
"*:*")(
maildrop=
"*/*"))(owner=cn=root, dc=your, dc=com)))
537 <li> <p> LDAP lookups are slower than local DB or DBM lookups. For most sites
538 they won't be a bottleneck, but it's a good idea to know how to tune
539 your directory service.
</p>
541 <li> <p> Multiple LDAP maps share the same LDAP connection if they differ
542 only in their query related parameters: base, scope, query_filter, and
543 so on. To take advantage of this, avoid spurious differences in the
544 definitions of LDAP maps: host selection order, version, bind, tls
545 parameters, ... should be the same for multiple maps whenever possible.
</p>
549 <h2><a name=
"feedback">Feedback
</a></h2>
551 <p> If you have questions, send them to postfix-users@postfix.org. Please
552 include relevant information about your Postfix setup: LDAP-related
553 output from postconf, which LDAP libraries you built with, and which
554 directory server you're using. If your question involves your directory
555 contents, please include the applicable bits of some directory entries.
</p>
557 <h2><a name=
"credits">Credits
</a></h2>
561 <li>Manuel Guesdon: Spotted a bug with the timeout attribute.
563 <li>John Hensley: Multiple LDAP sources with more configurable attributes.
565 <li>Carsten Hoeger: Search scope handling.
567 <li>LaMont Jones: Domain restriction, URL and DN searches, multiple result
570 <li>Mike Mattice: Alias dereferencing control.
572 <li>Hery Rakotoarisoa: Patches for LDAPv3 updating.
574 <li>Prabhat K Singh: Wrote the initial Postfix LDAP lookups and connection caching.
576 <li>Keith Stevenson:
<a href=
"http://tools.ietf.org/html/rfc2254">RFC
2254</a> escaping in queries.
578 <li>Samuel Tardieu: Noticed that searches could include wildcards, prompting
579 the work on
<a href=
"http://tools.ietf.org/html/rfc2254">RFC
2254</a> escaping in queries. Spotted a bug
582 <li>Sami Haahtinen: Referral chasing and v3 support.
584 <li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
585 OpenLDAP cache deprecation. Limits on recursion, expansion
586 and search results size. LDAP connection sharing for maps
587 differing only in the query parameters.
589 <li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
590 external files (
<a href=
"ldap_table.5.html">ldap
</a>:/path/ldap.cf) needed to securely store
591 passwords for plain auth.
593 <li>Liviu Daia revised the configuration interface and added the
<a href=
"postconf.5.html">main.cf
</a>
594 configuration feature.
</li>
596 <li>Liviu Daia with further refinements from Jose Luis Tallon and
597 Victor Duchovni developed the common query, result_format, domain and
598 expansion_limit interface for LDAP, MySQL and PosgreSQL.
</li>
600 <li>Gunnar Wrobel provided a first implementation of a feature to
601 limit LDAP search results to leaf nodes only. Victor generalized
602 this into the Postfix
2.4 "leaf_result_attribute" feature.
</li>
604 <li>Quanah Gibson-Mount contributed support for advanced LDAP SASL
605 mechanisms, beyond the password-based LDAP
"simple" bind.
</li>
609 And of course Wietse.