O'Reilly Book Excerpts: sendmail Cookbook
Cooking with sendmail
|
Related Reading
sendmail Cookbook |
Editor's note: sendmail Cookbook offers hundreds of step-by-step solutions to configuration problems just like the one in today's excerpt, on routing mail with LDAP. If you're an administrator, you know you can't spend hours tracking down the answer to every problem; the solutions and configuration code included with each recipe in the book can be implemented immediately. Check out this recipe and see for yourself.
Recipe 5.9: Routing Mail with LDAP
Problem
Your enterprise wants to use the IETF Internet Draft LDAP Schema for Intranet Mail Routing. You have been asked to configure sendmail to use the IETF draft schema and to read internal mail routing information from the LDAP server.
Solution
This solution requires collaboration between the LDAP administrator and the sendmail administrator.
Instructions for the LDAP administrator
Locate a copy of the IETF draft schema for intranet mail routing. The schema
is documented in the draft-lachman-laser-ldap-mail-routing-02 file available from the IETF. The schema is defined as part of the misc.schema file provided with the OpenLDAP distribution. Include the schema file in the LDAP configuration by adding the following include command to the slapd.conf file:
include /etc/openldap/schema/misc.schema
See the Discussion section for information on a possible conflict between the misc.schema file and other schema.
Additionally, the sendmail.schema file should be copied to the proper path and included in the LDAP configuration, as described in Recipe 1.3.
Restart LDAP to ensure that the IETF draft schema and the sendmail schema are available for use. Here is an example:
# ps -ax | grep slapd
1426 ? S 0:00 /usr/sbin/slapd -u ldap
# kill -TERM 1426
# /usr/sbin/slapd -u ldap
Create an LDIF file containing the mail routing information. Use the
inetLocalMailRecipient object class from the IETF draft schema to
format the mail routing data in the LDIF file. Run ldapadd to add the data to the LDAP database. The Discussion section shows an example of this step.
sendmail applies LDAP routing to a recipient address when the host portion of
that address is listed in class $={LDAPRoute}. To store the
$={LDAPRoute} class values on the LDAP server, create an LDIF file
containing a sendmailMTAClass record, which is an object class
defined in the sendmail.schema file. Set the
sendmailMTAClassName attribute to LDAPRoute and define
the values for that class using sendmailMTAClassValue attributes.
Use ldapadd to convert the LDIF file and add the data to
the LDAP database.
Instructions for the sendmail administrator
Direct sendmail to use LDAP intranet mail routing by adding the ldap_routing feature to the configuration. Add the LDAPROUTE_DOMAIN_FILE macro to load class $={LDAPRoute}, which can be loaded from the LDAP server. Lines, such as the following, should be added to the sendmail configuration:
dnl Stop Build from complaining
define(`confLDAP_DEFAULT_SPEC', ` -h ldserver -b dc=wrotethebook,dc=com')
dnl Identify the recipient hosts that require LDAP mail routing
LDAPROUTE_DOMAIN_FILE(`@LDAP')
dnl Enable the ldap_routing feature
FEATURE(`ldap_routing')
Rebuild and reinstall the sendmail.cf file, and restart sendmail as described in Recipe 1.8.
Discussion
This discussion covers both LDAP and sendmail configuration.
LDAP configuration
For a query to succeed, sendmail and LDAP must use and understand the same schema. The ldap_routing feature depends on a mail routing schema defined in a draft IETF document. That schema, which is available in the misc.schema file provided with the OpenLDAP distribution, must be included in the LDAP configuration in order for that LDAP to understand the mail routing queries coming from sendmail.
NOTE: A problem may occur when you attempt to restart LDAP after including the misc.schema file in the configuration. The error might look something like the following:
/etc/openldap/schema/redhat/rfc822-MailMember.schema: line 7: Duplicate attributeType: "1.3.6.1.4.1.42.2.27.2.1.15"In this case, the
includecommand for the misc.schema file was inserted into the slapd.conf file before an rfc822-MailMember.schemaincludecommand. When LDAP attempted to process the rfc822-MailMember.schema file, it found that the attribute type had already been declared - in this case, by the misc.schema file. If you have such a problem, you need to resolve the conflict. In this case, the resolution is straight-forward - simply remove the rfc822-MailMember.schemaincludecommand from the slapd.conf file. Theincludecommand can be safely removed because the rfc822-MailMember.schema file exactly duplicates the last two entries of the misc.schema file. This specific problem occurred on Red Hat Linux systems because the conflict is with a schema file provided by Red Hat. However, conflicts are possible any time multiple schema files are included in the LDAP configuration.
Include the sendmail.schema file in the LDAP
configuration to provide support for the other LDAP records used by sendmail.
For example, the sendmail configuration in this recipe reads LDAP records to
load the $={LDAPRoute} class, which requires the sendmail schema.
After the schema files are added to the slapd.conf file,
restart slapd to ensure that the schema files have been
read and are ready to be used.
Add the records required by the ldap_routing feature to the LDAP database using the draft IETF schema. This example shows three possible variations of the LDAP routing record:
# cat > ldap-routing
dn: uid=kathy, dc=wrotethebook, dc=com
objectClass: inetLocalMailRecipient
mailLocalAddress: kathy@rodent.wrotethebook.com
mailRoutingAddress: kathy@chef.wrotethebook.com
dn: uid=alana, dc=wrotethebook, dc=com
objectClass: inetLocalMailRecipient
mailLocalAddress: alana@wrotethebook.com
mailHost: chef.wrotethebook.com
dn: uid=craig, dc=wrotethebook, dc=com
objectClass: inetLocalMailRecipient
mailLocalAddress: craig@horseshoe.wrotethebook.com
mailHost: chef.wrotethebook.com
mailRoutingAddress: craig@crab.wrotethebook.com
Ctrl-D
# ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-routing
Enter LDAP Password: SecretLDAPpassword
adding new entry "uid=kathy, dc=wrotethebook, dc=com"
adding new entry "uid=alana, dc=wrotethebook, dc=com"
adding new entry "uid=craig, dc=wrotethebook, dc=com"
The object class of the records is inetLocalMailRecipient. Each
record contains a mailLocalAddress attribute that sendmail uses as
the database key. Records may contain one or both of the attributes
mailHost and mailRoutingAddress, which are the values
returned to sendmail by the LDAP database. The example shows how these values
are first used to build an LDIF file that is then processed by ldapadd to add the records to the LDAP database. Once added to the database, the records can be viewed with ldapsearch:
# ldapsearch -LLL -x '(objectClass=inetLocalMailRecipient)' \
> mailLocalAddress mailHost mailRoutingAddress
dn: uid=kathy, dc=wrotethebook, dc=com
mailLocalAddress: kathy@rodent.wrotethebook.com
mailRoutingAddress: kathy@chef.wrotethebook.com
dn: uid=alana, dc=wrotethebook, dc=com
mailLocalAddress: alana@wrotethebook.com
mailHost: chef.wrotethebook.com
dn: uid=craig, dc=wrotethebook, dc=com
mailLocalAddress: craig@horseshoe.wrotethebook.com
mailHost: chef.wrotethebook.com
mailRoutingAddress: craig@crab.wrotethebook.com
sendmail only queries the LDAP server for routing information if the host
portion of the recipient address is listed in the $={LDAPRoute}
class. Values in this class can be individually defined using
LDAPROUTE_DOMAIN macros or they can be read from a file using the
LDAPROUTE_DOMAIN_FILE macro. The sendmail configuration in this
recipe uses the LDAPROUTE_DOMAIN_FILE macro and reads the list of
values for the $={LDAPRoute} class from the LDAP server. The
following example shows how the LDAP administrator might store the
$={LDAPRoute} data on the LDAP server so that sendmail can retrieve
it later:
# cat > ldap-route-domains
dn: sendmailMTAClassName=LDAPRoute, dc=wrotethebook, dc=com
objectClass: sendmailMTA
objectClass: sendmailMTAClass
sendmailMTAHost: rodent.wrotethebook.com
sendmailMTAClassName: LDAPRoute
sendmailMTAClassValue: rodent.wrotethebook.com
sendmailMTAClassValue: wrotethebook.com
sendmailMTAClassValue: horseshoe.wrotethebook.com
Ctrl-D
# ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-route-domains
Enter LDAP Password: SecretLDAPpassword
adding new entry "sendmailMTAClassName=LDAPRoute, dc=wrotethebook, dc=com"
The sample LDIF file contains a sendmailMTAClass record, which
is an object class defined in the sendmail.schema file.
The record sets the sendmailMTAClassName attribute to
LDAPRoute and defines the values for that class using
sendmailMTAClassValue attributes. After the LDIF file is converted
and added to the LDAP database using the ldapadd
command, an ldapsearch command shows the content of the
LDAP record:
# ldapsearch -LLL -x '(sendmailMTAClassName=LDAPRoute)' sendmailMTAClassValue
dn: sendmailMTAClassName=LDAPRoute, dc=wrotethebook, dc=com
sendmailMTAClassValue: rodent.wrotethebook.com
sendmailMTAClassValue: wrotethebook.com
sendmailMTAClassValue: horseshoe.wrotethebook.com
LDAP is ready; now sendmail needs to be configured.
sendmail configuration
Three commands in this recipe's sendmail configuration help sendmail use the LDAP records that we have just created: the confLDAP_DEFAULT_SPEC define, the LDAPROUTE_DOMAIN_FILE macro, and the ldap_routing feature.
The confLDAP_DEFAULT_SPEC define sets default values that
sendmail uses to access the LDAP database. For many sendmail configurations that
use LDAP, the confLDAP_DEFAULT_SPEC define is not required because
the default values are correct. The confLDAP_DEFAULT_SPEC define is
used in this recipe to prevent sendmail from displaying a warning message. When
sendmail is configured with the ldap_routing feature, it
complains every time it is run if the configuration does not also contain a
confLDAP_DEFAULT_SPEC define. For example:
WARNING: Using default FEATURE(ldap_routing) map definition(s)
without setting confLDAP_DEFAULT_SPEC option.
Adding the confLDAP_DEFAULT_SPEC define to this recipe's
configuration eliminates this warning, but, of course, care must be taken to set
the correct values for the define. If the values in the
confLDAP_DEFAULT_SPEC define are incorrect, sendmail will not be
able to query the LDAP server successfully. The sample
confLDAP_DEFAULT_SPEC define in the Solution section contains two
values:
-h
The-hargument defines the hostname of the LDAP server. If you have multiple servers, use the hostname of the server that stores the sendmail data. If you have a single server, theHOSTvalue you configured in the ldap.conf file is probably the hostname you should use here.-b
The-bargument defines the LDAP default base distinguished name used for sendmail queries. The value should match the suffix of the database you wish to query, as defined in the slapd.conf by thesuffixentry for that database. The suffix used in this recipe is the system default defined for all LDAP clients by theBASEcommand in the ldap.conf file. Because the recipe uses the system default, it is not strictly necessary to define it with theconfLDAP_DEFAULT_SPECcommand.
The values defined by the confLDAP_DEFAULT_SPEC define appear in the sendmail.cf file on the LDAPDefaultSpec option line, as this grep shows:
# grep LDAPDefaultSpec sendmail.cf
O LDAPDefaultSpec= -h ldserver -b dc=wrotethebook,dc=com
Use ldapsearch to test the -h and -b values, as in this example:
# ldapsearch -LLL -x -h ldserver \
> -b 'dc=wrotethebook,dc=com' \
> '(objectClass=inetLocalMailRecipient)' mailLocalAddress
dn: uid=kathy, dc=wrotethebook, dc=com
mailLocalAddress: kathy@rodent.wrotethebook.com
dn: uid=alana, dc=wrotethebook, dc=com
mailLocalAddress: alana@wrotethebook.com
dn: uid=craig, dc=wrotethebook, dc=com
mailLocalAddress: craig@horseshoe.wrotethebook.com
If the -h and -b arguments work with ldapsearch, they should work for sendmail.
The @LDAP string used in place of the file path for the LDAPROUTE_DOMAIN_FILE macro tells sendmail to load the $={LDAPRoute} class from the LDAP server. Either an LDAPROUTE_DOMAIN_FILE macro or some LDAPROUTE_DOMAIN macros must appear in the configuration file when the ldap_routing feature is used because sendmail only uses LDAP routing for hosts listed in the $={LDAPRoute} class.
Finally, the ldap_routing FEATURE macro
is added to the configuration. The sendmail configuration in the Solution
section uses the simplest form of this command, which contains no optional
arguments; however, in this form, the ldap_routing
feature defines two database maps using two sendmail.cf
K commands:
ldapmhaccepts a recipient address as the key and returns the name of the mail host that should be used to reach that address. The mail host returned by theldapmhmap is used as the$hvalue for the mail delivery triple.ldapmraaccepts a recipient address as the key and returns the mail routing address that should be used instead of the recipient address to deliver the mail. The value returned byldapmrabecomes the$uvalue for the mail delivery triple.
Testing the results
A few simple tests show the impact of this recipe. First, a
sendmail -bt command tests that sendmail is
successfully reading the LDAP data:
# sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> $={LDAPRoute}
rodent.wrotethebook.com
horseshoe.wrotethebook.com
wrotethebook.com
> /map ldapmh alana@wrotethebook.com
map_lookup: ldapmh (alana@wrotethebook.com) returns chef.wrotethebook.com (0)
> /map ldapmra kathy@rodent.wrotethebook.com
map_lookup: ldapmra (kathy@rodent.wrotethebook.com) returns kathy@chef.wrotethebook.
com (0)
> /quit
The $={LDAPRoute} command shows that sendmail successfully
loaded this class from the LDAP server. The /map commands show that
sendmail can read LDAP routing data from both the ldapmh and the
ldapmra maps.
Next, sendmail -bv is used to show how the mail
delivery triples are rewritten for recipient addresses that contain hostnames
listed in $={LDAPRoute}:
# sendmail -bv kathy@rodent.wrotethebook.com
kathy@rodent.wrotethebook.com... deliverable: mailer esmtp, host chef.wrotethebook.
com., user kathy@chef.wrotethebook.com
# sendmail -bv alana@wrotethebook.com
alana@wrotethebook.com... deliverable: mailer relay, host chef.wrotethebook.com, user
alana@wrotethebook.com
# sendmail -bv craig@horseshoe.wrotethebook.com
craig@horseshoe.wrotethebook.com... deliverable: mailer relay, host chef.
wrotethebook.com, user craig@crab.wrotethebook.com
Refer back to the ldapsearch shown in an earlier example. You'll see that:
The record for kathy@rodent.wrotethebook.com returns only a mail routing address.
The record for alana@wrotethebook.com returns only a mail host.
The record for craig@horseshoe.wrotethebook.com returns both a mail host and a mail routing address.
The sendmail -bv tests show how these various return values impact the mail delivery triple:
In the first test, the mail routing address becomes the user value of the triple. The mailer and host values are necessary to deliver to that user address.
In the second test, the mail host returned by the LDAP query becomes the host value in the delivery triple. The mailer used is the
relaymailer because the mail will be relayed to that host for delivery. Because no mail routing address was returned by LDAP, the relay host is sent the original address, and it becomes the remote host's responsibility to deliver the mail to that address.The third test returns both a mail host value and a mail routing address. In that case, the
relaymailer is again used to relay mail to the remote host specified by the mail host value, and the delivery address passed to that host is the mail routing address. It then becomes the responsibility of that remote host to deliver the mail to the mail routing address.
LDAP routing is only one step in the delivery process. The virtusertable and the mailertable
are both applied after LDAP routing, and if the mailer used to deliver to the host returned by LDAP has the F=A flag set, aliasing is also applied. [6] If the mail is relayed to a remote host, that host also processes the address. The sendmail -bv test results clearly show the impact of LDAP routing, but only in part, because this recipe does not perform any subsequent processing on these addresses. On a production system with a more complex configuration, sendmail -bv may not provide such clear results. However, it is possible to test the impact of LDAP rulesets more directly.
The ldap_routing feature adds the LDAPExpand ruleset to query LDAP and process the return value, and it adds two rules to the Parse1 ruleset to call the new ruleset. Use sendmail -bt to check whether or not LDAP routing is applied to a specific address and to see the delivery triple returned for that address. Here is an example:
# sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> Parse1 craig<@horseshoe.wrotethebook.com.>
Parse1 input: craig < @ horseshoe . wrotethebook . com . >
LDAPExpand input: < craig < @ horseshoe . wrotethebook . com . > > < craig @
horseshoe . wrotethebook . com > < >
canonify input: craig @ crab . wrotethebook . com
Canonify2 input: craig < @ crab . wrotethebook . com >
Canonify2 returns: craig < @ crab . wrotethebook . com . >
canonify returns: craig < @ crab . wrotethebook . com . >
LDAPExpand returns: $# relay $@ chef . wrotethebook . com $: craig < @ crab .
wrotethebook . com . >
Parse1 returns: $# relay $@ chef . wrotethebook . com $: craig < @ crab .
wrotethebook . com . >
> /quit
Recipe 5.10 provides another example of testing the ldap_routing feature by passing an address to
Parse1. [7] See Recipe 5.10 for more details about this ruleset.
The ldap_routing feature
The ldap_routing FEATURE command used in this recipe is the simplest form of the command. The command accepts up to four optional arguments. Here is the complete syntax:
FEATURE(ldap_routing, mhmap, mramap, bounce, detail)
The four optional arguments are:
mhmapThis is a custom mail host map definition. It replaces the default
ldapmhmap created by the ldap_routing feature. At a minimum, the map definition must include-kand-varguments.mramapThis is a custom mail routing address map definition. It replaces the default
ldapmramap created by the ldap_routing feature. At a minimum, the map definition must include-kand-varguments.bounceThis argument tells sendmail what to do when a recipient address is not found in the LDAP database. Normally, delivery continues and sendmail delivers the mail to the recipient address as directed by the rest of the configuration. Two keywords can be used for this argument:
passthru
This keyword tells sendmail to continue with the delivery, which is the default action.bounce
This tells sendmail to return the mail as undeliverable. Actually, any value other thanpassthrucauses sendmail to bounce the mail; however, the sendmail developers suggest that, for the sake of clarity, you use the keywordbounceto reject the mail.detailThis tells sendmail how to handle recipient addresses that use the
+detail syntax. By default, an address that contains+detail information is looked up as-is, with the+detail information intact. Two keywords can be used to change this:strip
This keyword directs sendmail first to lookup the address with the+detail information, and then, if no match is found, to lookup the address without the+detail information.preserve
Just likestrip, this keyword directs sendmail first to lookup the address with the+detail information, and then, if no match is found, to lookup the address without the+detail information. In addition to modifying the lookup procedure, this keyword modifies the value returned from the LDAP database. If LDAP returns a mail routing address, sendmail appends the+detail information from the recipient address to the mail routing address when thepreservekeyword is specified.
See Also
The sendmail book covers the LDAPROUTE_DOMAIN_FILE macro in Section 23.7.11.18, the ldap_routing feature in Section 23.7.11.17, and the arguments available for the confLDAP_DEFAULT_SPEC define in Section 21.7.11. The Using LDAP for Aliases, Maps, and Classes
section of the cf/README file also provides important information.
[6] By default, the F=A flag is set for the local mailer.
[7] The focus characters (<>) are used in the address because Parse1, which is normally called after canonify has inserted them, expects focus
characters.
Check back here next week for two more recipes from the book that cover configuring sendmail for STARTTLS and limiting the SMTP command set.
Craig Hunt has worked with computer systems for the last thirty years.
Return to ONLamp.com.



