ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: sendmail Cookbook

Cooking with sendmail

Related Reading

sendmail Cookbook
By Craig Hunt

by Craig Hunt

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 include command for the misc.schema file was inserted into the slapd.conf file before an rfc822-MailMember.schema include command. 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.schema include command from the slapd.conf file. The include command 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:

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:

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 sendmail -bv tests show how these various return values impact the mail delivery triple:

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:

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.

Copyright © 2009 O'Reilly Media, Inc.