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 <h2><a name=
"config">Configuring LDAP lookups
</a></h2>
133 <p> In order to use LDAP lookups, define an LDAP source
134 as a table lookup in
<a href=
"postconf.5.html">main.cf
</a>, for example:
</p>
138 <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
142 <p> The file /etc/postfix/ldap-aliases.cf can specify a great number
143 of parameters, including parameters that enable LDAP SSL and
144 STARTTLS. For a complete description, see the
<a href=
"ldap_table.5.html">ldap_table(
5)
</a> manual
147 <h2><a name=
"example_alias">Example: local(
8) aliases
</a></h2>
149 <p> Here's a basic example for using LDAP to look up
<a href=
"local.8.html">local(
8)
</a>
150 aliases. Assume that in
<a href=
"postconf.5.html">main.cf
</a>, you have:
</p>
154 <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
158 <p> and in
<a href=
"ldap_table.5.html">ldap
</a>:/etc/postfix/ldap-aliases.cf you have:
</p>
162 server_host = ldap.example.com
163 search_base = dc=example, dc=com
167 <p> Upon receiving mail for a local address
"ldapuser" that isn't
168 found in the /etc/aliases database, Postfix will search the LDAP
169 server listening at port
389 on ldap.example.com. It will bind anonymously,
170 search for any directory entries whose mailacceptinggeneralid
171 attribute is
"ldapuser", read the
"maildrop" attributes of those
172 found, and build a list of their maildrops, which will be treated
173 as
<a href=
"http://tools.ietf.org/html/rfc822">RFC822
</a> addresses to which the message will be delivered.
</p>
175 <h2><a name=
"example_virtual">Example: virtual domains/addresses
</a></h2>
177 <p> If you want to keep information for virtual lookups in your
178 directory, it's only a little more complicated. First, you need to
179 make sure Postfix knows about the virtual domain. An easy way to
180 do that is to add the domain to the mailacceptinggeneralid attribute
181 of some entry in the directory. Next, you'll want to make sure all
182 of your virtual recipient's mailacceptinggeneralid attributes are
183 fully qualified with their virtual domains. Finally, if you want
184 to designate a directory entry as the default user for a virtual
185 domain, just give it an additional mailacceptinggeneralid (or the
186 equivalent in your directory) of
"@fake.dom". That's right, no
187 user part. If you don't want a catchall user, omit this step and
188 mail to unknown users in the domain will simply bounce.
</p>
190 <p> In summary, you might have a catchall user for a virtual domain
191 that looks like this:
</p>
195 dn: cn=defaultrecipient, dc=fake, dc=dom
197 objectclass: virtualaccount
199 owner: uid=root, dc=someserver, dc=isp, dc=dom
200 1 -
> mailacceptinggeneralid: fake.dom
201 2 -
> mailacceptinggeneralid: @fake.dom
202 3 -
> maildrop: realuser@real.dom
208 <dd> <p> 1: Postfix knows fake.dom is a valid virtual domain when
209 it looks for this and gets something (the maildrop) back.
</p>
211 <dd> <p> 2: This causes any mail for unknown users in fake.dom to
212 go to this entry ...
</p>
214 <dd> <p> 3: ... and then to its maildrop.
</p>
218 <p> Normal users might simply have one mailacceptinggeneralid and
219 <a href=
"QSHAPE_README.html#maildrop_queue">maildrop
</a>, e.g.
"normaluser@fake.dom" and
"normaluser@real.dom".
222 <h2><a name=
"example_group">Example: expanding LDAP groups
</a></h2>
225 LDAP is frequently used to store group member information. There are a
226 number of ways of handling LDAP groups. We will show a few examples in
227 order of increasing complexity, but owing to the number of independent
228 variables, we can only present a tiny portion of the solution space.
234 <li> <p> query groups as lists of addresses;
</p>
236 <li> <p> query groups as lists of user objects containing addresses;
</p>
238 <li> <p> forward special lists unexpanded to a separate list server,
239 for moderation or other processing;
</p>
241 <li> <p> handle complex schemas by controlling expansion and by treating
242 leaf nodes specially, using features that are new in Postfix
2.4.
</p>
247 The example LDAP entries and implied schema below show two group entries
248 (
"agroup" and
"bgroup") and four user entries (
"auser",
"buser",
"cuser"
249 and
"duser"). The group
"agroup" has the users
"auser" (
1) and
"buser" (
2)
250 as members via DN references in the multi-valued attribute
"memberdn", and
251 direct email addresses of two external users
"auser@example.org" (
3) and
252 "buser@example.org" (
4) stored in the multi-valued attribute
"memberaddr".
253 The same is true of
"bgroup" and
"cuser"/
"duser" (
6)/(
7)/(
8)/(
9), but
254 "bgroup" also has a
"maildrop" attribute of
"bgroup@mlm.example.com"
259 dn: cn=agroup, dc=example, dc=com
261 objectclass: ldapgroup
263 mail: agroup@example.com
264 1 -
> memberdn: uid=auser, dc=example, dc=com
265 2 -
> memberdn: uid=buser, dc=example, dc=com
266 3 -
> memberaddr: auser@example.org
267 4 -
> memberaddr: buser@example.org
272 dn: cn=bgroup, dc=example, dc=com
274 objectclass: ldapgroup
276 mail: bgroup@example.com
277 5 -
> maildrop: bgroup@mlm.example.com
278 6 -
> memberdn: uid=cuser, dc=example, dc=com
279 7 -
> memberdn: uid=duser, dc=example, dc=com
280 8 -
> memberaddr: cuser@example.org
281 9 -
> memberaddr: duser@example.org
286 dn: uid=auser, dc=example, dc=com
288 objectclass: ldapuser
290 10 -
> mail: auser@example.com
291 11 -
> maildrop: auser@mailhub.example.com
296 dn: uid=buser, dc=example, dc=com
298 objectclass: ldapuser
300 12 -
> mail: buser@example.com
301 13 -
> maildrop: buser@mailhub.example.com
306 dn: uid=cuser, dc=example, dc=com
308 objectclass: ldapuser
310 14 -
> mail: cuser@example.com
315 dn: uid=duser, dc=example, dc=com
317 objectclass: ldapuser
319 15 -
> mail: duser@example.com
325 <p> Our first use case ignores the
"memberdn" attributes, and assumes
326 that groups hold only direct
"memberaddr" strings as in (
3), (
4), (
8) and
327 (
9). The goal is to map the group address to the list of constituent
328 "memberaddr" values. This is simple, ignoring the various connection
329 related settings (hosts, ports, bind settings, timeouts, ...) we have:
336 search_base = dc=example, dc=com
337 query_filter = mail=%s
338 result_attribute = memberaddr
339 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:simple.cf
340 auser@example.org,buser@example.org
344 <p> We search
"dc=example, dc=com". The
"mail" attribute is used in the
345 query_filter to locate the right group, the
"result_attribute" setting
346 described in
<a href=
"ldap_table.5.html">ldap_table(
5)
</a> is used to specify that
"memberaddr" values
347 from the matching group are to be returned as a comma separated list.
348 Always check tables using
<a href=
"postmap.1.html">postmap(
1)
</a> with the
"-q" option, before
349 deploying them into production use in
<a href=
"postconf.5.html">main.cf
</a>.
</p>
351 <p> Our second use case instead expands
"memberdn" attributes (
1), (
2),
352 (
6) and (
7), follows the DN references and returns the
"maildrop" of the
353 referenced user entries. Here we use the
"special_result_attribute"
354 setting from
<a href=
"ldap_table.5.html">ldap_table(
5)
</a> to designate the
"memberdn" attribute
355 as holding DNs of the desired member entries. The
"result_attribute"
356 setting selects which attributes are returned from the selected DNs. It
357 is important to choose a result attribute that is not also present in
358 the group object, because result attributes are collected from both
359 the group and the member DNs. In this case we choose
"maildrop" and
360 assume for the moment that groups never have a
"maildrop" (the
"bgroup"
361 "maildrop" attribute is for a different use case). The returned data for
362 "auser" and
"buser" is from items (
11) and (
13) in the example data.
</p>
368 search_base = dc=example, dc=com
369 query_filter = mail=%s
370 result_attribute = maildrop
371 special_result_attribute = memberdn
372 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:special.cf
373 auser@mailhub.example.com,buser@mailhub.example.com
377 <p> Note: if the desired member object result attribute is always also
378 present in the group, you get surprising results: the expansion also
379 returns the address of the group. This is a known limitation of Postfix
380 releases prior to
2.4, and is addressed in the new with Postfix
2.4
381 "leaf_result_attribute" feature described in
<a href=
"ldap_table.5.html">ldap_table(
5)
</a>.
</p>
383 <p> Our third use case has some groups that are expanded immediately,
384 and other groups that are forwarded to a dedicated mailing list manager
385 host for delayed expansion. This uses two LDAP tables, one for users
386 and forwarded groups and a second for groups that can be expanded
387 immediately. It is assumed that groups that require forwarding are
388 never nested members of groups that are directly expanded.
</p>
394 search_base = dc=example, dc=com
395 query_filter = mail=%s
396 result_attribute = maildrop
399 search_base = dc=example, dc=com
400 query_filter = mail=%s
401 result_attribute = maildrop
402 special_result_attribute = memberdn
403 $ postmap -q auser@example.com
<a href=
"ldap_table.5.html">ldap
</a>:no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:expand.cf
404 auser@mailhub.example.com
405 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:expand.cf
406 auser@mailhub.example.com,buser@mailhub.example.com
407 $ postmap -q bgroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:no_expand.cf
<a href=
"ldap_table.5.html">ldap
</a>:expand.cf
408 bgroup@mlm.example.com
412 <p> Non-group objects and groups with delayed expansion (those that have a
413 maildrop attribute) are rewritten to a single maildrop value. Groups that
414 don't have a maildrop are expanded as the second use case. This admits
415 a more elegant solution with Postfix
2.4 and later.
</p>
417 <p> Our final use case is the same as the third, but this time uses new
418 features in Postfix
2.4. We now are able to use just one LDAP table and
419 no longer need to assume that forwarded groups are never nested inside
420 expanded groups.
</p>
426 search_base = dc=example, dc=com
427 query_filter = mail=%s
428 result_attribute = memberaddr
429 special_result_attribute = memberdn
430 terminal_result_attribute = maildrop
431 leaf_result_attribute = mail
432 $ postmap -q auser@example.com
<a href=
"ldap_table.5.html">ldap
</a>:fancy.cf
433 auser@mailhub.example.com
434 $ postmap -q cuser@example.com
<a href=
"ldap_table.5.html">ldap
</a>:fancy.cf
436 $ postmap -q agroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:fancy.cf
437 auser@mailhub.example.com,buser@mailhub.example.com,auser@example.org,buser@example.org
438 $ postmap -q bgroup@example.com
<a href=
"ldap_table.5.html">ldap
</a>:fancy.cf
439 bgroup@mlm.example.com
443 <p> Above, delayed expansion is enabled via
"terminal_result_attribute",
444 which, if present, is used as the sole result and all other expansion is
445 suppressed. Otherwise, the
"leaf_result_attribute" is only returned for
446 leaf objects that don't have a
"special_result_attribute" (non-groups),
447 while the
"result_attribute" (direct member address of groups) is returned
448 at every level of recursive expansion, not just the leaf nodes. This fancy
449 example illustrates all the features of Postfix
2.4 group expansion.
</p>
451 <h2><a name=
"other">Other uses of LDAP lookups
</a></h2>
453 Other common uses for LDAP lookups include rewriting senders and
454 recipients with Postfix's canonical lookups, for example in order
455 to make mail leaving your site appear to be coming from
456 "First.Last@example.com" instead of
"userid@example.com".
458 <h2><a name=
"hmmmm">Notes and things to think about
</a></h2>
462 <li> <p> The bits of schema and attribute names used in this document are just
463 examples. There's nothing special about them, other than that some are
464 the defaults in the LDAP configuration parameters. You can use
465 whatever schema you like, and configure Postfix accordingly.
</p>
467 <li> <p> You probably want to make sure that mailacceptinggeneralids are
468 unique, and that not just anyone can specify theirs as postmaster or
471 <li> <p> An entry can have an arbitrary number of mailacceptinggeneralids or
472 maildrops. Maildrops can also be comma-separated lists of addresses.
473 They will all be found and returned by the lookups. For example, you
474 could define an entry intended for use as a mailing list that looks
475 like this (Warning! Schema made up just for this example):
</p>
479 dn: cn=Accounting Staff List, dc=example, dc=com
480 cn: Accounting Staff List
482 objectclass: maillist
483 mailacceptinggeneralid: accountingstaff
484 mailacceptinggeneralid: accounting-staff
485 maildrop: mylist-owner
486 maildrop: an-accountant
487 maildrop: some-other-accountant
488 maildrop: this, that, theother
492 <li> <p> If you use an LDAP map for lookups other than aliases, you may have to
493 make sure the lookup makes sense. In the case of virtual lookups,
494 maildrops other than mail addresses are pretty useless, because
495 Postfix can't know how to set the ownership for program or file
496 delivery. Your
<b>query_filter
</b> should probably look something like this:
</p>
500 query_filter = (
&(mailacceptinggeneralid=%s)(!(|(
maildrop=
"*|*")(
maildrop=
"*:*")(
maildrop=
"*/*"))))
504 <li> <p> And for that matter, even for aliases, you may not want users able to
505 specify their maildrops as programs, includes, etc. This might be
506 particularly pertinent on a
"sealed" server where they don't have
507 local UNIX accounts, but exist only in LDAP and Cyrus. You might allow
508 the fun stuff only for directory entries owned by an administrative
510 so that if the object had a program as its maildrop and weren't owned
511 by
"cn=root" it wouldn't be returned as a valid local user. This will
512 require some thought on your part to implement safely, considering the
513 ramifications of this type of delivery. You may decide it's not worth
514 the bother to allow any of that nonsense in LDAP lookups, ban it in
515 the
<b>query_filter
</b>, and keep things like majordomo lists in local alias
520 query_filter = (
&(mailacceptinggeneralid=%s)(!(|(
maildrop=
"*|*")(
maildrop=
"*:*")(
maildrop=
"*/*"))(owner=cn=root, dc=your, dc=com)))
524 <li> <p> LDAP lookups are slower than local DB or DBM lookups. For most sites
525 they won't be a bottleneck, but it's a good idea to know how to tune
526 your directory service.
</p>
528 <li> <p> Multiple LDAP maps share the same LDAP connection if they differ
529 only in their query related parameters: base, scope, query_filter, and
530 so on. To take advantage of this, avoid spurious differences in the
531 definitions of LDAP maps: host selection order, version, bind, tls
532 parameters, ... should be the same for multiple maps whenever possible.
</p>
536 <h2><a name=
"feedback">Feedback
</a></h2>
538 <p> If you have questions, send them to postfix-users@postfix.org. Please
539 include relevant information about your Postfix setup: LDAP-related
540 output from postconf, which LDAP libraries you built with, and which
541 directory server you're using. If your question involves your directory
542 contents, please include the applicable bits of some directory entries.
</p>
544 <h2><a name=
"credits">Credits
</a></h2>
548 <li>Manuel Guesdon: Spotted a bug with the timeout attribute.
550 <li>John Hensley: Multiple LDAP sources with more configurable attributes.
552 <li>Carsten Hoeger: Search scope handling.
554 <li>LaMont Jones: Domain restriction, URL and DN searches, multiple result
557 <li>Mike Mattice: Alias dereferencing control.
559 <li>Hery Rakotoarisoa: Patches for LDAPv3 updating.
561 <li>Prabhat K Singh: Wrote the initial Postfix LDAP lookups and connection caching.
563 <li>Keith Stevenson:
<a href=
"http://tools.ietf.org/html/rfc2254">RFC
2254</a> escaping in queries.
565 <li>Samuel Tardieu: Noticed that searches could include wildcards, prompting
566 the work on
<a href=
"http://tools.ietf.org/html/rfc2254">RFC
2254</a> escaping in queries. Spotted a bug
569 <li>Sami Haahtinen: Referral chasing and v3 support.
571 <li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
572 OpenLDAP cache deprecation. Limits on recursion, expansion
573 and search results size. LDAP connection sharing for maps
574 differing only in the query parameters.
576 <li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
577 external files (
<a href=
"ldap_table.5.html">ldap
</a>:/path/ldap.cf) needed to securely store
578 passwords for plain auth.
580 <li>Liviu Daia revised the configuration interface and added the
<a href=
"postconf.5.html">main.cf
</a>
581 configuration feature.
</li>
583 <li>Liviu Daia with further refinements from Jose Luis Tallon and
584 Victor Duchovni developed the common query, result_format, domain and
585 expansion_limit interface for LDAP, MySQL and PosgreSQL.
</li>
587 <li>Gunnar Wrobel provided a first implementation of a feature to
588 limit LDAP search results to leaf nodes only. Victor generalized
589 this into the Postfix
2.4 "leaf_result_attribute" feature.
</li>
593 And of course Wietse.