Routing and Alias Management with OpenLDAP and Sendmail

LDAP and Sendmail offer sys admins considerable advantages for dynamic mail routing and centralized alias management. A common requirement, as an organization grows, is to support geographically dispersed mailservers. While this can be achieved by using subdomaining (i.e., bill@nyc.acme.com, jane@dublin.acme.com), it is generally preferable to route the mail dynamically from a single address (jane@acme.com). I'll explore how this can be accomplished using Sendmail in conjunction with OpenLDAP.

A similar problem faced by administrators is dealing with growing and disparate "/etc/aliases" databases. These files are often maintained individually on a per system basis and, in the case of load balanced mail servers, need to be kept in sync manually. An alternative to individually maintaining each file is to migrate the alias data into an LDAP directory. I'll examine how to configure Sendmail to use OpenLDAP to accomplish this.

Finally, LDAP APIs exist for practically every common development platform. This gives the sys admin the ability to implement Web front-ends, command-line scripts, etc. to manage routing entries and aliases. It also provides developers an easy entry point for integrating application functionality with the organization's mail systems in a standard way.

Installing the Software

To implement this solution, you'll need recent versions of OpenLDAP and Sendmail. If your system has an OpenLDAP package, that is probably sufficient. If you choose to build OpenLDAP yourself, however, you can obtain stable sources from www.openldap.org
The ./configure script offers options for supporting SASL, TLS, etc. You won't need these to store mail data in the directory, but you may want to build OpenLDAP with them anyway (particularly TLS) if your needs change or if you plan to store sensitive data in the directory.

Your system should have a packaged version of Sendmail available (and probably installed). You'll need to make sure that it has been built with LDAP support. You can do this by running the following command:

[root@host]# sendmail -d0.1 

If you see "LDAPMAP" somewhere after "Compiled With:" you should be ok. If not, you'll need to configure Sendmail with LDAP support. The source can be obtained from www.sendmail.org.
Build instructions can be found in the INSTALL folder in the root of the Sendmail tarball. You'll need to add the following two lines to the site.config.m4 file:

APPENDDEF(`confMAPDEF', `-DLDAPMAP')dnl
APPENDDEF(`confLIBS',`-lldap -llber')dnl

After you've built and installed Sendmail, run "sendmail -d0.1" again and make sure the LDAPMAP string appears.

Configuring OpenLDAP

Once OpenLDAP has been installed, you'll need to configure it to load the necessary schemas to support routing and alias data. Assuming your OpenLDAP installation has been installed under root, look in /etc/openldap/schema for a file called "sendmail.schema". If the file isn't there, you'll need to grab it from the Sendmail tarball. Copy it from the "cf" subdir in the tarball to /etc/openldap/schema. Once it's there, edit your slapd.conf file (in /etc/openldap) and add the following lines to the include lines at the top of the file:

include /etc/openldap/schema/sendmail.schema
include /etc/openldap/schema/misc.schema 

Within these schema files, we will be making use of the inetLocalMailRecipient objectClass and SendmailMTA objectClasses and attributes.

Next we need to configure the suffix. The "suffix" keyword indicates the base dn of the directory that slapd will be serving. For purposes of this example, it will be:

"dc=acme,dc=com". 

Following suffix is the rootdn. This is the equivalent of a system superuser account and is what we'll use to bind and add entries to the directory. The rootdn line looks like this:

rootdn "cn=Manager,dc=acme,dc=com"

Now we can set a password for the rootdn. This is accomplished with the "rootpw" directive. Although you can use a plaintext password here, you're encouraged to use the slappasswd command to generate a crypted password. Running slappasswd will prompt you for a password twice then output an encrypted version. Cut and paste this into slapd.conf as follows:

rootpw {SSHA}SjHPdR/DxUWaUu1iGzhhMVzg2urKqSiR 

You can now start the slapd daemon using your system's service facilities (i.e., /sbin/service) or by manually running "slapd" as root.

Populating the Directory

Assuming you're starting with an empty LDAP directory, the first step is to set up a base dn for your organization. The base dn for our fictional organization looks like this (and is the same value we defined for the suffix field in slapd.conf):

"dc=acme,dc=com" 

This will be the root for all other entries in our directory. We'll create two organizational units now. One will be called "people" and will contain the mail routing data. (Note that this ou could also potentially contain posix account information, contact information, etc.) We will also create an ou called "aliases" that will contain the alias data.

We can add these to the directory like this:

[root@host]# ldapadd -x -D "cn=Manager,dc=acme,dc=com" -h ldap.acme.com -W
Enter LDAP Password:
dn: dc=acme,dc=com
objectClass: dcObject
objectClass: organization
o: acme
dc: acme
dn: ou=people,dc=acme,dc=com
objectClass: organizationalUnit
ou: people
dn: ou=aliases,dc=acme,dc=com
objectClass: organizationalUnit
ou: people

Run the following to confirm the entries have been added:

[root@host]# ldapsearch -x -b "dc=acme,dc=com"

You should now see the new entries.

Adding Routing Records

We for our fictional acme.com organization will have two locations -- our corporate headquarters located in Manhattan and an office in Dublin. We have mailservers in each location called mail.nyc.acme.com and mail.dub.acme.com. mail.nyc.acme.com is performing primary MX duties for the acme.com domain and as such is accepting all mail. We will add routing entries to deliver mail bound for users in New York onto mail.nyc.acme.com and mail bound for users in Dublin to mail.dub.acme.com.

Let's assume we have two users, Mike Jones and Jane Jones. Mike works in New York, and Jane is in Dublin. Mike's Unix username is mjones, and Jane's is jjones. Their "external" email addresses are "mjones@acme.com" and "jjones@acme.com". The following LDAP entries are needed to set up routing for them:

dn: cn=mjones,ou=people,dc=acme,dc=com
objectClass: inetOrgPerson
objectClass: inetLocalMailRecipient
cn: mjones
sn: Jones
mailLocalAddress: mjones@acme.com
mailRoutingAddress: mike@mail.nyc.acme.com
dn: cn=jjones,ou=people,dc=acme,dc=com
objectClass: inetOrgPerson
objectClass: inetLocalMailRecipient
cn: jjones
sn: Jones
mailLocalAddress: jjones@acme.com
mailRoutingAddress: jjones@mail.dub.acme.com 

Looking at the LDIF, "inetOrgPerson" is the structural object class for the entry and is required for OpenLDAP versions 2.1 and up. "inetLocalMailRecipient" is defined in the misc.schema and provides the "mailLocalAddress" and "mailRoutingAddress" attributes. "mailLocalAddress" is the user's email address as it appears inbound to the primary MX server (mail.nyc.acme.com). mailRoutingAddress defines the address to which we want to route the mail. In Mike's case, it will go to mail.nyc.acme.com, and in Jane's case it's going to get directed to mail.dub.acme.com in Dublin.

You can add these entries using the same syntax we used previously to create the base dn. Alternatively, you can put the entries into a text file and supply the "-f" flag to "ldapadd".

Maintaining mail routing information like this is extremely flexible. Let's say Mike needs to work in Dublin for a few weeks and wants to pull his mail down from the mail.dub.acme.com while there. We can use "ldapmodify" to change his routing address:

[root@host]# ldapmodify -x -D "cn=Manager,dc=acme,dc=com" -h ldap.acme.com -W
Enter LDAP Password:
dn: cn=mjones,ou=people,dc=acme,dc=com
changetype: modify
replace: mailRoutingAddress
mailRoutingAddress: mjones@mail.dub.acme.com 

Now Mike's mail will be routed to mail.dub.acme.com. Note that this happens on the fly. A restart of Sendmail or OpenLDAP is not required. This is obviously beneficial when maintaining more then a few mail servers.

Configuring Sendmail for Routing

Now that the directory is populated, we can look at configuring Sendmail. To begin, you'll need to generate a sendmail.cf. Find the appropriate sendmail.mc for your system. These are located in the Sendmail tarball in the "cf/cf" subdirectory. If you're using your system's Sendmail installation, the mc files are usually in /usr/share/sendmail or something similiar. You'll want to add the following lines:

define(`confLDAP_DEFAULT_SPEC', ` -h ldap.acme.com -s sub -b "dc=acme,dc=com"')dnl
FEATURE(`ldap_routing') dnl
LDAPROUTE_DOMAIN(`acme.com')dnl 

Build and install new cf file like this:

[root@host]# m4 ../m4/cf.m4 sendmail.mc > /etc/mail/sendmail.cf.new
[root@host]# cd /etc/mail
[root@host]# cp sendmail.cf sendmail.cf.old
[root@host]# cp sendmail.cf.new sendmail.cf

Then restart Sendmail and watch the appropriate log files (i.e., /var/log/messages and /var/log/maillog) for errors.

We'll now enter Sendmail in test mode to confirm everything is okay:

[root@host]# sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter

> /map ldapmra mjones@acme.com map_lookup:
ldapmra (mjones@acme.com) returns
mjones@mail.nyc.acme.com (0)
> /map ldapmra jjones@acme.com map_lookup: ldapmra (jjones@acme.com)
returns
jjones@mail.dub.acme.com (0) 

Sending some test messages should confirm mail is routing properly. If you're having issues double-check your /etc/mail/access and /etc/mail/local-host-names and make sure Sendmail can relay for your domain.

Having the routing set up this way gives us some interesting architectural options. Let's say we're running a large mailing list server on mail.nyc.acme.com. As the list membership grows, we decide we want to dedicate a server for mailing list processing. Instead of changing the mailing list addresses or jumping through hoops with aliases or procmail, we can add routing entries for each list and route it to the dedicated list server.

Routing in LDAP also gives you the ability to segment relay and delivery. You could potentially have load-balanced relay servers, each consulting a central LDAP directory for routing decisions. These relay servers would do no local delivery, but instead relay each message to the appropriate delivery server. The delivery servers themselves could be clustered for high availability (and their alias data all centrally shared via LDAP, as I'll show next.)

Migrating Aliases into LDAP

Now that we have routing data in LDAP, we can move the "/etc/aliases" database into the directory. We'll take every alias in /etc/aliases (or /etc/mail/aliases depending on your Sendmail installation) and represent them like this:

dn: sendmailMTAKey=postmaster,ou=alias,dc=acme,dc=com
objectClass: sendmailMTA
objectClass: sendmailMTAAlias
objectClass: sendmailMTAAliasObject
sendmailMTAAliasGrouping: aliases
sendmailMTACluster: acme.com
sendmailMTAKey: postmaster
sendmailMTAAliasValue: mjones@acme.com
dn: sendmailMTAKey=webmaster,ou=alias,dc=acme,dc=com
objectClass: sendmailMTA
objectClass: sendmailMTAAlias
objectClass: sendmailMTAAliasObject
sendmailMTAAliasGrouping: aliases
sendmailMTACluster: acme.com
sendmailMTAKey: postmaster
sendmailMTAAliasValue: mjones@acme.com, jjones@acme.com 

"sendmailMTACluster" will be referenced in the LDIFs as I'll show next. "sendmailMTAKey" defines the left-hand side of the alias. "sendmailMTAAliasValue" defines the right-hand side of the alias. Note that multiple values for the right-hand side of the alias are comma delimited.

Once this file is populated, you can add it using ldapadd's "-f" flag. Use ldapsearch to confirm the entries are present.

Configuring Sendmail to read Aliases from LDAP

Now that the aliases have been entered, we can configure Sendmail to read them. Open the mc file used previously and add the following:

define(`confLDAP_CLUSTER', `acme.com')dnl
define(`ALIAS_FILE',`ldap:')dnl

Take a look at "confLDAP_CLUSTER". This is what we were referencing with the "sendmailMTACluster" attribute in the alias LDIF. Once the definitions have been added to the sendmail.mc, you can build and install a new sendmail.cf.

Test the aliases are working by running the following:

[root@host]# sendmail -bv postmaster@acme.com mjones@acme.com... deliverable:
mailer esmtp, host acme.com., user mjones@acme.com
[root@host]# sendmail -v webmaster@acme.com jjones@acme.com... deliverable:
mailer esmtp, host acme.com., user john@acme.com

Now we can send some test messages and confirm that alias expansion is working.

Author: John D'Emic