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