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