X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=etc%2Fexim4%2Fconf.d%2Frouter%2F30_ldap;fp=etc%2Fexim4%2Fconf.d%2Frouter%2F30_ldap;h=2f195c86fc0f68157846557d5c8baaa127b88d78;hb=00913fc5c4f0bd4a78df5fb067cfb10392aebe7c;hp=0000000000000000000000000000000000000000;hpb=0625c01974df320cade3f9cd56674bcdfee0d9f3;p=matthijs%2Fservers%2Fdrsnuggles.git diff --git a/etc/exim4/conf.d/router/30_ldap b/etc/exim4/conf.d/router/30_ldap new file mode 100644 index 0000000..2f195c8 --- /dev/null +++ b/etc/exim4/conf.d/router/30_ldap @@ -0,0 +1,184 @@ +### router/900_exim4-config_local_user +################################# + +# These routers deal with mail meant for virtual_domains and real_domains. +# The targets for addresses in these domains and hosts are taken from +# the LDAP server. +# +# The first two routers check the LDAP directory to find an object to +# which the mail should be delivered. The first one works on +# virtual_domains, and finds an object based on the mail or +# alternateMailAddress attributes. For example, info@stdin.nl might be +# resolved by that one. The second one works on real_domains and finds an +# object based its uid (i.e., username). For example, +# matthijs@login.drsnuggles.stderr.nl might be resolved by the second +# router. +# +# The next set of routers works when a person is retrieved. They handle +# forwarding the mail to another mail server when the mailHost attribute +# is set but it is not us, forwarding the email to any +# mailForwardingAddresses in the object or delivering the mail locally +# when the mailHost is set. Note that both either or both of the last +# two routers can apply. If none of these three apply, routing continues +# to the last set. Local delivery works by redirecting to username@localhost +# and redirecting to the local_delivery router. This allows for a number of +# different routers (put after the local_delivery router) to handle the +# local_delivery. This is also the only way to get to any routers after the +# ones in this file! +# +# The last two routers work when a group is retrieved. They handle +# forwarding the mail to any members, both rfc822members (ie, addresses) +# and uniqueMembers (ie, other LDAP objects). +# +# Note that this distinction between persons and groups is not made by +# looking at the object classes, but at the attributes. Any object that +# has a mailHost and/or mailForwardingAddresses is treated as a person, +# meaning that only these two attributes are processed. Any object that +# has neither of these attributes is assumed to be a group and has its +# rfc822members and uniqueMembers processed. Any object that has none of +# these properties, will cause a delivery failure. +# +# Note also that only the first two routers have the domains +# precondition set, to differentiate between virtual_domains and +# real_domains. Assuming that the routers in this file will only be called for +# virtual_domains and real_domains, exactly one of these two routers will be +# called. If the lookup fails, the more option ensures that the rest of the +# routers are not called. +# +# The routers in this file assume that the url to LDAP server is defined +# as LDAPSERVER and the base dn is defined as LDAPBASE. No assumptions +# are made about the structure of the LDAP directory, so any object that +# has the mail or mailForwardingAddress attributes is considered a +# valid target for email, anywhere in the directory. These routers do +# assume that a single email address is listed only once. If not, mails +# to the address will be deferred. + +LDAPURL=LDAPSERVER/LDAPBASE + +# This router looks up an object in the ldap directory using its mail and +# alternateMailAddress attributes, for any domains in virtual_domains. +# This handles email addresses in "virtual" domains, since the object +# found does not need to actually have a username (it can even be a +# group). +ADDR=${quote_ldap:${local_part}@${domain}} +ldap_lookup_virtual: + debug_print = "R: ldap_lookup_virtual for $local_part@$domain: Finding person or group with (alternate) email address $local_part@$domain" + driver = redirect + domains = +virtual_domains + address_data = ${lookup ldap {LDAPURL?uid,mailHost,mailForwardingAddress,rfc822member,uniqueMember?sub?(|(mail=ADDR)(mailAlternateAddress=ADDR))}{$value}fail} + # Noop, this router just needs to pass its preconditions, evaluate + # address_data and then pass control to ldap_person_other_mailhost below + data = ${local_part}@${domain} + redirect_router=ldap_person_other_mailhost + # If no objects are found and the address_data expansion is forced to fail, + # stop processing. Note that this setting does not apply when the domains + # precondition fails. + more = false + cannot_route_message = "Unknown address" + +# This router looks up an object in the ldap directory using its uid +# (username) attribute, for any hosts in real_domains. This handles email +# addresses of actual users in this domain, i.e., objects that have uid +# property. +LOCALPART=${quote_ldap:${local_part}} +ldap_lookup_real: + debug_print = "R: ldap_lookup_real for $local_part@$domain: Finding user with uid $local_part" + driver = redirect + domains = +real_domains + address_data = ${lookup ldap {LDAPURL?uid,mailHost,mailForwardingAddress?sub?(uid=LOCALPART)}{$value}fail} + # Noop, this router just needs to pass its preconditions, evaluate + # address_data and then pass control to ldap_person_other_mailhost below + data = ${local_part}@${domain} + redirect_router=ldap_person_other_mailhost + # If no objects are found and the address_data expansion is forced to fail, + # stop processing. Note that this setting does not apply when the domains + # precondition fails. + more = false + cannot_route_message = "Unknown user" + +# If the person has a mailhost configured, and it's not us, forward to +# that mailhost. If this router accepts, no others will be tried. This +# means we're also not processing any mailForwardingAddresses, assuming +# that the host forwarded to will do this. +# +# This entry is mostly future-compatible, since at the time of writing +# there are no other mailservers using the same LDAP directory. But it +# looks cool! +ldap_person_other_mailhost: + debug_print = "R: ldap_person_other_mailhost for $local_part@$domain: Forwarding to a mailHost if it is not us" + driver = manualroute + condition = ${if and { \ + # If mailhost is not empty + {!eqi{${extract{mailHost}{$address_data}}}{}} \ + # And mailhost is not this host + {!eqi{${extract{mailHost}{$address_data}}}{$primary_hostname}} \ + }} + # Then, forward to the other mailHost + route_data = ${extract{mailHost}{$address_data}} + transport = remote_smtp + +# Forward the mail to any mailForwardingAddresses configured +DELIVER_HERE=${if eqi{${extract{mailHost}{$address_data}}}{$primary_hostname}{true}{false}} +ldap_person_forward: + debug_print = "R: ldap_person_forward for $local_part@$domain: Forwarding to any mailForwardingAddresses" + driver = redirect + data = ${extract{mailForwardingAddress}{$address_data}} + # Pass the message to the ldap_person_local router as well, so we can support both + # local delivery and forwarding. However, only set unseen to yes if we know + # the ldap_person_local will accept it. Just putting unseen = yes here doesn't + # work because if there is no local delivery, an error message is generated + # even when the email was forwarded succesfully. + unseen = DELIVER_HERE + +# Deliver the mail locally if the mailHost points to us. +ldap_person_local: + debug_print = "R: ldap_person_local for $local_part@$domain: Doing local delivery if the mailHost is us" + driver = redirect + # Lookup if there is a user that has the target email address in either his + # mail attribute, or one of his mailAlternateAddresses and also has his + # mailstore on this host as its mailhost. + condition = DELIVER_HERE + # Forward the email to username@localhost. There is a separate set of routers + # that explicitly handles the localhost "domain", and has support for things + # like .forward files, procmail, etc. + data = ${extract{uid}{$address_data}}@localhost + redirect_router = local_delivery + +# If any of the two above routers accepted the message, processing will +# stop here! + +# Forward the mail to any full members (uniqueMember) configured +ldap_group_member: + debug_print = "R: ldap_group_member for $local_part@$domain: Forwarding to any uniqueMembers" + driver = redirect + # Lookup the mail address (if any) of each member. This gracefully ignores + # any members without an email address. + data = ${map \ + # Since multipe attributes are separated by ", ", we replace ", " by "\n" + # and use that as a list separator (fortunately it's not just ",", as the + # documentation suggests, since then we would have had one big dn...) + {<\n ${sg \ + {${extract{uniqueMember}{$address_data}}} \ + {, } \ + {\n} \ + }} \ + {${lookup ldap {LDAPSERVER/$item?mail?base?}}} \ + } + # Pass the message to the ldap_group_address router as well, so we can support both + # addresses (rfc822member) and dns (uniqueMember) in a group. + # However, only set unseen to yes if we know the ldap_group_address will + # accept it. Just putting unseen = yes here doesn't work because if there is + # no local delivery, an error message is generated even when the email was + # already forwarded succesfully. + unseen = ${if !eqi{${extract{rfc822member}{$address_data}}}{}{true}{false}} + +# Forward the mail to any mail-only members (rfc822member) configured +ldap_group_address: + debug_print = "R: ldap_group_address for $local_part@$domain: Forwarding to any rfc822members" + driver = redirect + data = ${extract{rfc822member}{$address_data}} + cannot_route_message = "Recipient is not set up for mail reception" + # If this router did not match, stop processing. + more = false + +# vim: set sts=2 expandtab sw=2 ai: