exim: Completely review the exim configuration.
[matthijs/servers/drsnuggles.git] / etc / exim4 / conf.d / router / 30_ldap
diff --git a/etc/exim4/conf.d/router/30_ldap b/etc/exim4/conf.d/router/30_ldap
new file mode 100644 (file)
index 0000000..2f195c8
--- /dev/null
@@ -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: