Address rewriting with sendmail genericstable
Paul Heinlein
First published on January 19, 2006
Last updated on January 25, 2006
In mid-2005, Galois, Inc., my former employer, took over the care and feeding of cvs.haskell.org, the public code repository for several significant open-source Haskell projects. All active developers have accounts on the machine, so there are several dozen local accounts.
Whenever a developer submits a change to the repository, a backend
script formats and sends an e-mail message detailing the change for
other developers interested in that section of code. Since the commit
scripts run under each developer’s local user account, the automated
e-mail messages were addressed From: <username@cvs.haskell.org>
.
The problem is that neither Galois nor the developers wanted to maintain
a separate cvs.haskell.org
e-mail server. Galois didn’t want the hassle
of maintaining a separate mail domain, and the developers were leary of
being assigned yet another e-mail address.
The ideal solution
Ideally, outbound messages would appear with a From
header that
reflected each developer’s e-mail address of choice. Similarly, any
message delivered to a local account, such as an error message from a
cron job, would be sent off to the same remote address.
Aliases
The easy part of the final solution was to use aliases to ensure that
mail addressed to the developers’ local accounts would be delivered to
their preferred remote e-mail address. This would ensure that error
messages from cron jobs or automated scripts would actually get read.
The aliases database, typically found at /etc/mail/aliases
, is well
known. It’s included and populated on nearly every system that ships
with sendmail
as the main mail transport agent.
Simply, each line includes the local mail alias, a colon, and the real destination for mail addressed to that local alias. The real destination may be a local account, a program (like Mailman), or, in this case, a remote e-mail address.
bob: robert.smith@hisdomain.net
kate: kate@school.ac.uk
After editing the aliases
file, you need to run newaliases
to
translate your aliases into the binary database format sendmail can
read.
Generics table
The tougher issue was masquerading locally generated messages, especially CVS commits, so they appeared to be from a developer’s remote e-mail address. Sendmail has long been able to masquerade local users as all coming from a single domain, but I’d never had to masquerade each local account from its own domain.
The solution lay in sendmail’s genericstable feature. The sendmail
documentation
says, “This feature will cause unqualified addresses (i.e., without a
domain) and addresses with a domain listed in class {G}
to be looked up
in a map and turned into another (“generic”) form, which can change both
the domain name and the user name.”
Unlike the aliases feature, however, getting the genericstable feature
to work will likely mean that you’ll have to modify your sendmail.cf
configuration file. For most of us, that means editing the m4
source
file (typically, but not always, /etc/mail/sendmail.mc
). In my case,
the additions were minor:
FEATURE(`genericstable')dnl
GENERICS_DOMAIN(`localhost.localdomain')dnl
The format of the genericstable
file is, annoyingly, just slightly
different from that used by aliases
. In this case, there are two
whitespace-separated columns, the first containing the local username,
the second the remote address.
bob robert.smith@hisdomain.net
kate kate@school.ac.uk
After populating /etc/mail/genericstable
, it’s necessary to build the
binary representation sendmail will use:
makemap hash /etc/mail/genericstable < /etc/mail/genericstable
Any e-mail messages generated by local username bob will have their
From
header rewritten to appear to be from
<robert.smith@hisdomain.net>
.
All done!
Once the aliases and genericstable databases are in place, sendmail is able to deliver mail addressed to local accounts to their proper remote mailboxes and masquerade locally generated messages as being from those same mailboxes. Handy, eh?