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