Chapter 4. Masquerading
Introduction
Masquerading is sendmail-speak for rewriting the hostname in the address of outbound mail. The reasons for masquerading fall into two general categories:
- Mail routing
Many networks are designed to route all inbound mail through a central mail hub. When a host sends out mail using its own hostname in the sender address, replies to that mail may well come back to the host. Replacing the sending system’s hostname with the mail hub’s hostname in the sender address guarantees that replies come back to the hub. Masquerading is not the only way to do this. An MX record can also route mail to the hub. However, maintenance of the DNS zone file is under the control of the domain administrator. The sendmail administrator maintains masquerading, and most sendmail administrators prefer to be in charge of their own fate. Also, hostnames change over time. Masquerading can provide more consistent email addresses and can simplify maintenance.
- Organizational requirements
Some organizations simply have a policy of hiding hostnames. Management may think that “busy” hostnames project an image of disorganization. Marketing may think that “frivolous” hostnames project the wrong image to customers. Naive security people may even believe that hiding hostnames increases security. For whatever reason, management requires masquerading, which, in turn, creates the need for systems configured to receive replies to the masqueraded mail.
The MASQUERADE_AS
macro enables masquerading. This macro
stores a value in the sendmail.cf
$M
macro and adds code to the
MasqHdr
ruleset to rewrite the header sender
address using the value returned by $M
.[1] By default, masquerading applies to
all of the mail that originates on the local host. All of the valid
hostnames for the local host are stored in class
$=w
. When MASQUERADE_AS
is
used, sendmail replaces the hostname of the sender with the value of
$M
—if the original address lacks a hostname
or the hostname matches a value in class $=w
.
sendmail checks both class $=w
and class
$=M
when masquerading.[2]
Class $=M
, however, starts out empty. Use the
MASQUERADE_DOMAIN
macro to add individual hostnames to
class $=M
. To add multiple hosts, create a file
containing a list of hosts and use the
MASQUERADE_DOMAIN_FILE
macro to load the file into class
$=M
.
Normally, masquerading interprets the values in class
$=M
as hostnames and only exact matches are
masqueraded. Use the
masquerade_entire_domain
feature to interpret the values in
class $=M
as domain names. When
masquerade_entire_domain is used, every host in
a domain listed in class $=M
is masqueraded.
masquerade_entire_domain is used in Recipe 4.8.
By default, masquerading is applied to the header sender address. The masquerade_envelope feature causes sendmail to also apply masquerading to the envelope sender address. Recipe Recipe 4.10 shows an example of how the masquerade_envelope feature is used. Using the allmasquerade feature applies masquerading to both sender and recipient addresses. Recipe 4.5 discusses the allmasquerade feature.
Masquerading is widely used and highly configurable, yet it is not
the only way, or even the most powerful way, to rewrite the sender
address in the From
: header. Masquerading rewrites
the hostname in the sender address; the
genericstable
rewrites
the entire address—both the username and the hostname. The
genericstable is related to masquerading in that
they both modify the sender address, however, there are distinct
differences:
Masquerading rewrites the hostname based on the input hostname. The genericstable is applied to addresses based on the input hostname but can rewrite the entire address based on all or part of the input address.
Masquerading replaces each masqueraded hostname with the same value, whereas the genericstable can replace each input address with a different value.
The genericstable feature defines the
generics
database in the
sendmail.cf file and adds code to the
MasqHdr
ruleset to use that database to rewrite
the sender address in the From
: header. The key to
the genericstable database is a username or a
full email address, and the value returned for the key is a complete
email address. To use the genericstable, first
construct a text file in which each line of the file is a key/value
pair using the format: username, whitespace, email address. A sample
input entry in the text file is:
pat patstover@butler.wrotethebook.com
In this example, pat is the key against which the input username is matched, and patstover@butler.wrotethebook.com is the email address returned for that key. Before the genericstable can be used, the text file containing the key/value pairs must be converted to a hash type database using the sendmail makemap command.[3]
Both the username and the hostname from the input sender address are
used by the genericstable process. The key to
the genericstable database always contains a
username, either by itself or as part of a full email address.
However, sendmail only searches for that key if the hostname in the
input sender address matches a value in class
$=G
or if the input
sender address contains no hostname.
By default, class $=G
is empty. Use the
GENERICS_DOMAIN
macro to add individual hostnames to
class $=G
, or the
GENERICS_DOMAIN_FILE
macro to load class
$=G
from a file. Normally, the values in class
$=G
are interpreted as hostnames. Use the
generics_entire_domain
feature to make sendmail interpret the
values in class $=G
as domain names. When the
generics_entire_domain feature is used, the
genericstable is applied to mail sent from every
host in every domain listed in class $=G
.
Both masquerading and the genericstable are used in the recipes in this chapter. But first we start with a recipe that uses the always_add_domain feature. always_add_domain has nothing to do with the genericstable, and, strictly speaking, it is not a masquerading command. However, it does rewrite the sender address, which is the common thread running through this chapter.
4.1. Adding Domains to All Sender Addresses
Problem
You have been
asked
to configure sendmail to add a hostname to every header sender
address, even when the input address has no hostname part and the
mail is being delivered by the local
mailer.
Solution
Add the always_add_domain feature to the sendmail configuration. The command that you add is:
dnl Add the domain to all sender addresses FEATURE(`always_add_domain')
Using Recipe 1.8 as a guide, build and install the sendmail configuration and then restart sendmail.
Discussion
The always_add_domain feature causes sendmail to
make sure that every sender address includes a hostname part. By
default, sendmail does not add a hostname part to the sender address
when the address has no hostname part and the
local
mailer is delivering the mail. Thus, mail
from the address alana goes through the
local
mailer’s header sender
process unchanged when the default configuration is used, as this
test shows:
#sendmail -bt -Cgeneric-linux.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try local alana
Trying header sender address alana for mailer local canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromL input: alana MasqHdr input: alana MasqHdr returns: alana HdrFromL returns: alana final input: alana final returns: alana Rcode = 0, addr = alana >/quit
Rerunning the test after adding the always_add_domain feature to the configuration shows a different result:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try local alana
Trying header sender address alana for mailer local canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromL input: alana AddDomain input: alana AddDomain returns: alana < @ *LOCAL* > MasqHdr input: alana < @ *LOCAL* > MasqHdr returns: alana < @ chef . wrotethebook . com . > HdrFromL returns: alana < @ chef . wrotethebook . com . > final input: alana < @ chef . wrotethebook . com . > final returns: alana @ chef . wrotethebook . com Rcode = 0, addr = alana@chef.wrotethebook.com >/quit
Here, the address goes in as alana and comes out with the hostname added as alana@chef.wrotethebook.com.
See Also
Recipe 4.4 shows how the always_add_domain feature can be used with masquerading. The sendmail book covers always_add_domain in Section 4.8.4.
4.2. Masquerading the Sender Hostname
Problem
You have been asked to configure sendmail to replace the local hostname in the header sender address with a different hostname.
Solution
Add the
MASQUERADE_AS
macro to the sendmail
configuration to rewrite the hostname in the From
:
address to the hostname specified by the
MASQUERADE_AS
macro. Add the
EXPOSED_USER
macro to the sendmail
configuration to exclude non-unique user names from the address
rewrite. Here are examples of these two macros:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root)
Build the new sendmail.cf file, copy it to /etc/mail, and restart sendmail as described in Recipe 1.8.
Discussion
Use the MASQUERADE_AS
macro to configure sendmail
to rewrite the host portion of the sender address on outbound mail.
The value provided on the MASQUERADE_AS
command
line is stored in the sendmail.cf
$M
macro. sendmail uses the value from the
$M
macro to rewrite the hostname portion of the
header sender address when the hostname matches any value found in
sendmail.cf class $=w
or
class $=M
. sendmail also uses the value from
$M
(instead of the value from the
sendmail.cf
$j
macro) as the
hostname portion of the header sender address, when the address lacks
a hostname part. $j
holds the fully qualified name of the
local host. Normally, $j
is added to the username
to create a full email address. A test using the generic Linux
configuration, which does not contain the
MASQUERADE_AS
macro, shows this:
#sendmail -bt -Cgeneric-linux.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$M
Undefined >$j
chef.wrotethebook.com >/tryflags HS
>/try esmtp alana
Trying header sender address alana for mailer esmtp canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromSMTP input: alana PseudoToReal input: alana PseudoToReal returns: alana MasqSMTP input: alana MasqSMTP returns: alana < @ *LOCAL* > MasqHdr input: alana < @ *LOCAL* > MasqHdr returns: alana < @ chef . wrotethebook . com . > HdrFromSMTP returns: alana < @ chef . wrotethebook . com . > final input: alana < @ chef . wrotethebook . com . > final returns: alana @ chef . wrotethebook . com Rcode = 0, addr = alana@chef.wrotethebook.com >/quit
The -C
option on the sendmail command line loads
the generic-linux.cf configuration, which does
not contain the MASQUERADE_AS
macro. The
$M
command shows that the $M
macro is not defined. The $j
command shows the
fully qualified name of this host. In the example, the name is
chef.wrotethebook.com. The
/tryflags
command tells sendmail to process the
header sender (HS) address. The /try
command tells
sendmail to process alana as the header sender
address for the esmtp
mailer. Notice that
alana is an email address that does not contain
a host part. sendmail adds a hostname to the unqualified username,
and, by default, it adds the hostname found in $j
.
The value returned by the MasqHdr
ruleset shows
this.
A second test, this time using the generic configuration with the
addition of the sample lines shown in the Solution section, yields a
different result. This time, a value is returned by the
$M
command, in addition to the value returned for
$j
. When alana is processed
as the header sender address for the esmtp
mailer,
the MasqHdr
ruleset rewrites the address using the
value from $M
instead of the value from
$j
:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$M
wrotethebook.com >$j
chef.wrotethebook.com >/tryflags HS
>/try esmtp alana
Trying header sender address alana for mailer esmtp canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromSMTP input: alana PseudoToReal input: alana PseudoToReal returns: alana MasqSMTP input: alana MasqSMTP returns: alana < @ *LOCAL* > MasqHdr input: alana < @ *LOCAL* > MasqHdr returns: alana < @ wrotethebook . com . > HdrFromSMTP returns: alana < @ wrotethebook . com . > final input: alana < @ wrotethebook . com . > final returns: alana @ wrotethebook . com Rcode = 0, addr = alana@wrotethebook.com >/quit
The nullclient configuration covered in Recipe 3.1 also masquerades mail so that it appears to come from the mail hub instead of the local host. This configuration, however, differs substantially from the nullclient configuration. The nullclient did not deliver its own mail. All of its mail was relayed through the hub. In that sense, the nullclient’s mail really did originate from the mail hub. This recipe creates a configuration that delivers its own mail and changes the hostname in the header sender address even though the mail originates from the local host.
In this example, the host masquerades using the domain name. Because
all hosts in this sample domain masquerade using the same value, the
possibility exists for conflicts caused by non-unique usernames. The
classic example of a non-unique username is
root—every Unix system has a
root account. If mail from
root@crab.wrotethebook.com and mail from
root@jamis.wrotethebook.com was sent out as mail
from root@wrotethebook.com, it would be
difficult to sort out where the mail really came from and who should
receive replies to the mail. For that reason, the
EXPOSED_USER
macro is used to ensure that mail
from the root user is not masqueraded. A test shows this:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try esmtp root
Trying header sender address root for mailer esmtp canonify input: root Canonify2 input: root Canonify2 returns: root canonify returns: root 1 input: root 1 returns: root HdrFromSMTP input: root PseudoToReal input: root PseudoToReal returns: root MasqSMTP input: root MasqSMTP returns: root < @ *LOCAL* > MasqHdr input: root < @ *LOCAL* > MasqHdr returns: root < @ chef . wrotethebook . com . > HdrFromSMTP returns: root < @ chef . wrotethebook . com . > final input: root < @ chef . wrotethebook . com . > final returns: root @ chef . wrotethebook . com Rcode = 0, addr = root@chef.wrotethebook.com >/quit
The example in this recipe has only one username specified in an
EXPOSED_USER
macro. To specify multiple usernames,
add additional EXPOSED_USER
macros—one for
each username. For more than a few usernames, use the
EXPOSED_USER_FILE
macro as in this example:
EXPOSED_USER_FILE(`/etc/mail/exposed.users')
The file, /etc/mail/exposed.users in our example, contains a list of usernames, with one username on each line. The sample file might look something like the following:
$ cat /etc/mail/exposed.users
root
postmaster
bin
daemon
adm
mail
news
operator
smmsp
nobody
This is just an example. Only non-unique usernames from which mail is actually sent would be placed in this file.
See Also
The nullclient configuration in Recipe 2.1 is a related configuration. Recipe 4.3 to Recipe 4.11 show
masquerading with added features. The sendmail
book covers MASQUERADE_AS
in 4.4.2, and
EXPOSED_USER
and
EXPOSED_USER_FILE
are explained in 4.4.1. The
“Address
Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.3. Eliminating Masquerading for the Local Mailer
Problem
You have been asked to configure sendmail to masquerade the header sender address on all mail sent to external hosts, without adding the masquerade hostname to mail delivered by the local mailer.
Solution
Add the
local_no_masquerade
feature, the
MASQUERADE_AS
macro, and the
EXPOSED_USER
macro to the sendmail
configuration. Here are examples of these configuration commands:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Don't masquerade addresses for the local mailer FEATURE(`local_no_masquerade')
Build and install the new sendmail.cf file, and then restart sendmail. These steps are shown in Recipe 1.8.
Discussion
The hostname defined on the MASQUERADE_AS
command
line is stored in the sendmail.cf
$M
macro. sendmail rewrites the hostname in the
From
: address to the value found in the
$M
macro if the original hostname is listed in
class $=w
or class $=M
. By
default, class $=w
contains all of the names and
addresses of the local host. Thus, mail sent from the local host is
masqueraded using the value from $M
. This is
exactly what you want when mail is sent to an external host, but it
might not be exactly what you want when the local
mailer delivers the mail locally. Some tests show how local mail is
handled by the MASQUERADE_AS
macro.
First, we run two tests using the configuration defined in Recipe
Recipe 4.2 (i.e., masquerading without the
l
ocal_no_masquerade feature):
#sendmail -bt -Crecipe4.2.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try local alana
Trying header sender address alana for mailer local canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromL input: alana MasqHdr input: alana MasqHdr returns: alana HdrFromL returns: alana final input: alana final returns: alana Rcode = 0, addr = alana >/try local alana@chef.wrotethebook.com
Trying header sender address alana@chef.wrotethebook.com for mailer local canonify input: alana @ chef . wrotethebook . com Canonify2 input: alana < @ chef . wrotethebook . com > Canonify2 returns: alana < @ chef . wrotethebook . com . > canonify returns: alana < @ chef . wrotethebook . com . > 1 input: alana < @ chef . wrotethebook . com . > 1 returns: alana < @ chef . wrotethebook . com . > HdrFromL input: alana < @ chef . wrotethebook . com . > MasqHdr input: alana < @ chef . wrotethebook . com . > MasqHdr returns: alana < @ wrotethebook . com . > HdrFromL returns: alana < @ wrotethebook . com . > final input: alana < @ wrotethebook . com . > final returns: alana @ wrotethebook . com Rcode = 0, addr = alana@wrotethebook.com >/quit
Two valid local addresses are processed as header sender addresses
for the local
mailer. The first address is the
local address for the username alana without any
host part. In this case, the address goes in as
alana, is processed, and comes out as
alana. This is fine. Local addresses do not need
a hostname part for delivery. Any local user receiving mail from
alana can reply to that address and the mail
will be successfully delivered by the local mailer. The second
address, alana@chef.wrotethebook.com, is also a
valid local address for alana because
chef.wrotethebook.com is the name of the local
host. This time, however, the address is changed to
alana@wrotethebook.com by the header sender
process. If a local user replies to
alana@wrotethebook.com, the local mailer does
not deliver the mail locally; instead, it is sent to the mail
exchanger for wrotethebook.com by the
esmtp
mailer. Final delivery becomes the
responsibility of the mail exchanger.
After adding the configuration lines shown in the Solution section, and building and installing the sendmail.cf file, the test results are different:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >=SHdrFromL
R< @ > MAILER-DAEMON R@ < @ $* > MAILER-DAEMON R$+ $: $> AddDomain $1 >/tryflags HS
>/try local alana
Trying header sender address alana for mailer local canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromL input: alana HdrFromL returns: alana final input: alana final returns: alana Rcode = 0, addr = alana >/try local alana@chef.wrotethebook.com
Trying header sender address alana@chef.wrotethebook.com for mailer local canonify input: alana @ chef . wrotethebook . com Canonify2 input: alana < @ chef . wrotethebook . com > Canonify2 returns: alana < @ chef . wrotethebook . com . > canonify returns: alana < @ chef . wrotethebook . com . > 1 input: alana < @ chef . wrotethebook . com . > 1 returns: alana < @ chef . wrotethebook . com . > HdrFromL input: alana < @ chef . wrotethebook . com . > HdrFromL returns: alana < @ chef . wrotethebook . com . > final input: alana < @ chef . wrotethebook . com . > final returns: alana @ chef . wrotethebook . com Rcode = 0, addr = alana@chef.wrotethebook.com >/quit
Processing alana as a header sender address
yields the same result as before. The address goes in as
alana and comes out as
alana. However, this time the process is
different—the MasqHdr
ruleset is not called
by the HdrFromL
ruleset. The difference is more
clearly seen in the processing of the
alana@chef.wrotethebook.com address, which also
goes through the process unchanged. A reply to the header sender
address for either alana or
alana@chef.wrotethebook.com is handled as local
mail and delivered by the local
mailer.
Using local_no_masquerade reduces overhead by keeping local mail local, but the impact of having some local mail go through an external host is probably not too large. The local_no_masquerade feature also ensures that all mail from local users addressed to local users is handled in the same way. Consistency is an advantage of this feature. Recipe 4.4 shows the opposite approach to obtaining consistency, which is to force masquerading of all sender addresses.
See Also
Recipe 4.2 and Recipe 4.4
describe similar recipes. The sendmail book
covers MASQUERADE_AS
in 4.4.2,
EXPOSED_USER
in 4.4.1, and
local_no_masquerade in 4.8.20. The
“Address Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.4. Forcing Masquerading of Local Mail
Problem
You have
been asked to configure sendmail to
masquerade all header sender addresses, even those processed by the
local
mailer that lack a hostname part.
Solution
Create a sendmail configuration that combines the
always_add_domain
feature used in Recipe 4.1 with the
MASQUERADE_AS
and
EXPOSED_USER
macros used in Recipe 4.2. Examples of these commands are:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Add the domain name to all addresses FEATURE(`always_add_domain')
Build and install the new sendmail.cf file, and restart sendmail as described in Recipe 1.8.
Discussion
The always_add_domain feature is not specific to
masquerading. When always_add_domain is used
without the MASQUERADE_AS
macro, sendmail uses the
fully qualified name of the local host found in the
$j
macro to create the hostname part of the sender
address.[4] However, for this recipe we
want to masquerade the hostname portion of the address; so the
MASQUERADE_AS
macro is used together with the
always_add_domain feature in the recipe. After
completing this recipe, running a sendmail -bt
test produces the following result:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try local alana
Trying header sender address alana for mailer local canonify input: alana Canonify2 input: alana Canonify2 returns: alana canonify returns: alana 1 input: alana 1 returns: alana HdrFromL input: alana AddDomain input: alana AddDomain returns: alana < @ *LOCAL* > MasqHdr input: alana < @ *LOCAL* > MasqHdr returns: alana < @ wrotethebook . com . > HdrFromL returns: alana < @ wrotethebook . com . > final input: alana < @ wrotethebook . com . > final returns: alana @ wrotethebook . com Rcode = 0, addr = alana@wrotethebook.com >/try local alana@chef
Trying header sender address alana@chef for mailer local canonify input: alana @ chef Canonify2 input: alana < @ chef > Canonify2 returns: alana < @ chef . wrotethebook . com . > canonify returns: alana < @ chef . wrotethebook . com . > 1 input: alana < @ chef . wrotethebook . com . > 1 returns: alana < @ chef . wrotethebook . com . > HdrFromL input: alana < @ chef . wrotethebook . com . > AddDomain input: alana < @ chef . wrotethebook . com . > AddDomain returns: alana < @ chef . wrotethebook . com . > MasqHdr input: alana < @ chef . wrotethebook . com . > MasqHdr returns: alana < @ wrotethebook . com . > HdrFromL returns: alana < @ wrotethebook . com . > final input: alana < @ wrotethebook . com . > final returns: alana @ wrotethebook . com Rcode = 0, addr = alana@wrotethebook.com >/try local alana@chef.wrotethebook.com
Trying header sender address alana@chef.wrotethebook.com for mailer local canonify input: alana @ chef . wrotethebook . com Canonify2 input: alana < @ chef . wrotethebook . com > Canonify2 returns: alana < @ chef . wrotethebook . com . > canonify returns: alana < @ chef . wrotethebook . com . > 1 input: alana < @ chef . wrotethebook . com . > 1 returns: alana < @ chef . wrotethebook . com . > HdrFromL input: alana < @ chef . wrotethebook . com . > AddDomain input: alana < @ chef . wrotethebook . com . > AddDomain returns: alana < @ chef . wrotethebook . com . > MasqHdr input: alana < @ chef . wrotethebook . com . > MasqHdr returns: alana < @ wrotethebook . com . > HdrFromL returns: alana < @ wrotethebook . com . > final input: alana < @ wrotethebook . com . > final returns: alana @ wrotethebook . com Rcode = 0, addr = alana@wrotethebook.com >/quit
This test shows that local user addresses of all possible formats
(user
,
user
@
host
,
and
user
@
host
.domain
)
are all rewritten into exactly the same format. The masquerade
hostname stored in the $M
macro is added to
addresses that have no host part, and it is used to replace the
hostname on addresses that do have a host part. Thus, the addresses
alana, alana@chef, and
alana@chef.wrotethebook.com all come out of the
local
mailer header sender address process
rewritten to alana@wrotethebook.com. This
consistency ensures that a reply to mail from Alana, regardless of
how the original sender address was formatted, is handled in exactly
the same way. In this case, all replies to mail from Alana will go to
the mail exchanger first.
The goal of this recipe is to force sendmail to masquerade all sender addresses. At first glance, the allmasquerade feature might appear to be the correct choice for this recipe. However, the allmasquerade feature affects recipient addresses, and, in this recipe, we wish to rewrite only sender addresses. Recipe 4.5 covers masquerading the recipient address using the allmasquerade feature.
See Also
Recipe 4.1, Recipe 4.2,
Recipe 4.3, and Recipe 4.5
provide information on related configurations. Recipe 3.1 covers the nullclient
configuration. Recipe 3.3 covers the
MAIL_HUB
macro. The sendmail
book covers the always_add_domain feature in
4.8.5 and the allmasquerade feature in 4.8.4.
4.5. Masquerading Recipient Addresses
Problem
By default, masquerading only affects sender addresses. In addition to masquerading sender addresses, you have been asked to configure sendmail to masquerade recipient addresses when those addresses include masqueraded hostnames.
Solution
Add the
MASQUERADE_AS
and
EXPOSED_USER
macros and the
allmasquerade
feature to the sendmail
configuration. Here is an example of the lines that could be added:
dnl Masquerade as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Masquerade recipient and sender addresses FEATURE(`allmasquerade')
Build and install the new sendmail.cf file, and restart sendmail as described in Recipe 1.8.
Discussion
Using the basic masquerading configuration from Recipe 4.2, the header recipient address is not rewritten, as this test shows:
#sendmail -bt -Crecipe4.2.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HR
>/try esmtp david@chef
Trying header recipient address david@chef for mailer esmtp canonify input: david @ chef Canonify2 input: david < @ chef > Canonify2 returns: david < @ chef . wrotethebook . com . > canonify returns: david < @ chef . wrotethebook . com . > 2 input: david < @ chef . wrotethebook . com . > 2 returns: david < @ chef . wrotethebook . com . > EnvToSMTP input: david < @ chef . wrotethebook . com . > PseudoToReal input: david < @ chef . wrotethebook . com . > PseudoToReal returns: david < @ chef . wrotethebook . com . > MasqSMTP input: david < @ chef . wrotethebook . com . > MasqSMTP returns: david < @ chef . wrotethebook . com . > EnvToSMTP returns: david < @ chef . wrotethebook . com . > final input: david < @ chef . wrotethebook . com . > final returns: david @ chef . wrotethebook . com Rcode = 0, addr = david@chef.wrotethebook.com >/quit
The /tryflags
command requests a test of the
header recipient (HR
) address processing. The
/try
command tells sendmail to process the address
david@chef as a header recipient address for the
esmtp
mailer. The hostname
chef is converted by DNS to the canonical name
chef.wrotethebook.com. The hostname, however, is
not masqueraded.
A configuration that contains the allmasquerade
feature, in addition to the MASQUERADE_AS
macro,
rewrites recipient addresses for the
emstp
mailer, as this test
shows:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HR
>/try esmtp david@chef
Trying header recipient address david@chef for mailer esmtp canonify input: david @ chef Canonify2 input: david < @ chef > Canonify2 returns: david < @ chef . wrotethebook . com . > canonify returns: david < @ chef . wrotethebook . com . > 2 input: david < @ chef . wrotethebook . com . > 2 returns: david < @ chef . wrotethebook . com . > HdrFromSMTP input: david < @ chef . wrotethebook . com . > PseudoToReal input: david < @ chef . wrotethebook . com . > PseudoToReal returns: david < @ chef . wrotethebook . com . > MasqSMTP input: david < @ chef . wrotethebook . com . > MasqSMTP returns: david < @ chef . wrotethebook . com . > MasqHdr input: david < @ chef . wrotethebook . com . > MasqHdr returns: david < @ wrotethebook . com . > HdrFromSMTP returns: david < @ wrotethebook . com . > final input: david < @ wrotethebook . com . > final returns: david @ wrotethebook . com Rcode = 0, addr = david@wrotethebook.com > /quit
When the allmasquerade feature is used, masqueraded hostnames are hidden when they appear in the list of recipients. The advantage of this is that it provides a consistent view of the masqueraded addresses. A remote user might notice that people inside the wrotethebook.com domain send mail to the david account using the address david@chef.wrotethebook.com. That remote user might then try to do the same. If the organization really wants to encourage people to use the address david@wrotethebook.com to reach the david account, masquerading recipient addresses helps to do this by showing users only the preferred address.
See Also
Recipe 4.1 to Recipe 4.11
cover other masquerading features. The sendmail
book covers MASQUERADE_AS
in 4.4.2 and
EXPOSED_USER
in 4.4.1.
4.6. Masquerading at the Relay Host
Problem
You have been asked to configure the mail relay host to masquerade the header sender address of mail that originates on specific hosts as that mail passes through the mail relay.
Solution
Create a file that lists all of the hostnames that you want sendmail to masquerade. The file can be named anything you wish. This example names the file /etc/mail/masquerade-domains.
Add the
MASQUERADE_AS
,
EXPOSED_USER
, and
MASQUERADE_DOMAIN_FILE
macros to the sendmail
configuration on the mail relay host. The
MASQUERADE_DOMAIN_FILE
macro must specify the
masquerade-domains file created in the first
step. Here are examples of the commands added to the mail
relay’s configuration:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Load the list of hostnames that will be masqueraded MASQUERADE_DOMAIN_FILE(`/etc/mail/masquerade-domains')
Build the new sendmail.cf file, copy it to /etc/mail, and restart sendmail. Recipe 1.8 provides examples of these steps.
Discussion
The MASQUERADE_DOMAIN_FILE
macro specifies a file
that is loaded into sendmail.cf class
$=M
. sendmail masquerades hosts listed in class
$=M
, as well as those listed in class
$=w
. However, hosts listed in class
$=M
are not equivalent to those listed in class
$=w
. Placing a hostname in class
$=M
enables masquerading. But, unlike mail
addressed to hosts listed in class $=w
, mail
addressed to hosts in class $=M
is not accepted
for local delivery. Class $=M
makes it possible to
extend the set of hosts for which masquerading is performed without
adding to the list of local hostname aliases. A simple test shows the
effect of the MASQUERADE_DOMAIN_FILE
macro:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=M
horseshoe.wrotethebook.com rodent.wrotethebook.com jamis.wrotethebook.com >/tryflags HS
>/try esmtp david@jamis.wrotethebook.com
Trying header sender address david@jamis.wrotethebook.com for mailer esmtp canonify input: david @ jamis . wrotethebook . com Canonify2 input: david < @ jamis . wrotethebook . com > Canonify2 returns: david < @ jamis . wrotethebook . com . > canonify returns: david < @ jamis . wrotethebook . com . > 1 input: david < @ jamis . wrotethebook . com . > 1 returns: david < @ jamis . wrotethebook . com . > HdrFromSMTP input: david < @ jamis . wrotethebook . com . > PseudoToReal input: david < @ jamis . wrotethebook . com . > PseudoToReal returns: david < @ jamis . wrotethebook . com . > MasqSMTP input: david < @ jamis . wrotethebook . com . > MasqSMTP returns: david < @ jamis . wrotethebook . com . > MasqHdr input: david < @ jamis . wrotethebook . com . > MasqHdr returns: david < @ wrotethebook . com . > HdrFromSMTP returns: david < @ wrotethebook . com . > final input: david < @ wrotethebook . com . > final returns: david @ wrotethebook . com Rcode = 0, addr = david@wrotethebook.com >$=w
chef localhost.localdomain localhost [192.168.0.8] [localhost.localdomain] [127.0.0.1] chef.wrotethebook.com >/quit
The $=M
command displays the contents of class
$=M
and shows that class $=M
contains the data from the
/etc/mail/masquerade-domains file we created.
The email address david@jamis.wrotethebook.com
is masqueraded as david@wrotethebook.com when it
is processed as a header sender address for the
esmtp
mailer, even though
jamis.wrotethebook.com is not included in class
$=w
because it is included in class
$=M
.
Alternatives
The sample masquerade-domains file contains only
three entries. It is possible to replicate this configuration without
creating the masquerade-domains file by placing
three MASQUERADE_DOMAIN
macros in the sendmail
configuration file.
dnl Host names that will be masqueraded MASQUERADE_DOMAIN(`rodent.wrotethebook.com') MASQUERADE_DOMAIN(`horseshoe.wrotethebook.com') MASQUERADE_DOMAIN(`jamis.wrotethebook.com')
This alternative was rejected because it is not as flexible as
creating a separate masquerade-domains file.
This recipe masquerades individual hostnames. Individual hosts come
and go. Hostnames change. Each change would necessitate a change to
the m4 configuration with the associated
rebuild, reinstall, and restart if the
MASQUERADE_DOMAIN
solution were used. Changes in
the masquerade-domains file only require a
restart.
If you’re positive that you want to masquerade every
host granted relay privileges, you might be tempted to use the
relay-domains file as the
MASQUERADE_DOMAIN_FILE
:
MASQUERADE_DOMAIN_FILE(`/etc/mail/relay-domains')
This is generally a bad idea. A standard file should be used only for its standard purpose. The relay-domains file should be used only to grant relay privileges, and a separate file should be created to define masqueraded hostnames—even if those files are identical. The reason is that you cannot guarantee that they will remain identical into the future. In the long run, creating a separate file dedicated to a single purpose causes fewer problems than misusing a standard file.
See Also
Recipe 3.8 and Recipe 4.2
provide supporting information for this recipe. Recipe 4.4 and Recipe 4.7 cover similar
configurations that should be evaluated before implementing this
recipe. The sendmail book covers
MASQUERADE_AS
in 4.4.2,
EXPOSED_USER
in 4.4.1,
MASQUERADE_DOMAIN
in 4.4.3, and
MASQUERADE_DOMAIN_FILE
in 4.4.4. The
“Address Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.7. Limiting Masquerading
Problem
By default, every hostname that is accepted for local delivery (i.e., every hostname that is accepted as an alias for the local host) is masqueraded when masquerading is enabled. You have been asked to create a sendmail configuration that does not masquerade every local hostname alias. Instead you are to masquerade only those hostnames that are specifically identified for masquerading.
Solution
Build a file that contains the names of just those hosts that you wish to masquerade. In this example, we name the file /etc/mail/masquerade-domains.
Create a sendmail configuration containing the
MASQUERADE_AS
,
EXPOSED_USER
, and
MASQUERADE_DOMAIN_FILE
macros and the
limited_masquerade
feature. Here are sample commands:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Load the list of hostnames that will be masqueraded MASQUERADE_DOMAIN_FILE(`/etc/mail/masquerade-domains') dnl Only masquerade names listed in class $=M FEATURE(`limited_masquerade')
Rebuild and reinstall the sendmail.cf file, and then restart sendmail, as described in Recipe 1.8.
Discussion
By default, every host listed in class $=w
is
allowed to relay, and mail addressed to any host in class
$=w
is accepted for local delivery. In addition,
when the MASQUERADE_AS
macro is used, mail from
any host listed in class $=w
is masqueraded. This
is usually just what you want. An exception, however, occurs when
class $=w
defines a larger set of hosts for
relaying or local delivery than the set that should be masqueraded.
For example, assume that you have a mail exchanger that handles mail
for a few domains, and that your
local-host-names file contains the following
entries:
horseshoe.wrotethebook.com wrotethebook.com ora.com example.com stateu.edu
Two of these entries (horseshoe.wrotethebook.com and wrotethebook.com) are in the local domain. The others are not.
Normally, both the hostnames in class $=w
and
those in class $=M
are masqueraded. While this
system is the mail exchanger for ora.com,
example.com, and
stateu.edu, it should not masquerade those
domains as wrotethebook.com. The
limited_masquerade feature limits masquerading
to just those hosts listed in class $=M
. Relaying
and local delivery continue to be influenced by class
$=w
, but class $=w
is ignored
for masquerading when the limited_masquerade
feature is used. A few tests illustrate this.
The first test is a sendmail
-bt
test using the
local-host-names file just shown and a
masquerading configuration that does not use the
limited_masquerade feature.
#sendmail -bt -Crecipe4.2.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=w
example.com chef ora.com localhost.localdomain localhost [192.168.0.8] [localhost.localdomain] stateu.edu [127.0.0.1] horseshoe.wrotethebook.com wrotethebook.com chef.wrotethebook.com >/tryflags HS
>/try esmtp amanda@stateu.edu
Trying header sender address amanda@stateu.edu for mailer esmtp canonify input: amanda @ stateu . edu Canonify2 input: amanda < @ stateu . edu > Canonify2 returns: amanda < @ stateu . edu . > canonify returns: amanda < @ stateu . edu . > 1 input: amanda < @ stateu . edu . > 1 returns: amanda < @ stateu . edu . > HdrFromSMTP input: amanda < @ stateu . edu . > PseudoToReal input: amanda < @ stateu . edu . > PseudoToReal returns: amanda < @ stateu . edu . > MasqSMTP input: amanda < @ stateu . edu . > MasqSMTP returns: amanda < @ stateu . edu . > MasqHdr input: amanda < @ stateu . edu . > MasqHdr returns: amanda < @ wrotethebook . com . > HdrFromSMTP returns: amanda < @ wrotethebook . com . > final input: amanda < @ wrotethebook . com . > final returns: amanda @ wrotethebook . com Rcode = 0, addr = amanda@wrotethebook.com >/quit
In this case, the header sender address
amanda@stateu.edu is rewritten to
amanda@wrotethebook.com. The people at
stateu.edu do not want their addresses rewritten
in this manner, even though they use the services of the mail
exchanger. To fix this, add a
MASQUERADE_DOMAIN_FILE
macro to the configuration
and create a masquerade-domains file containing
the names of the hosts that should be masqueraded. The file might,
for example, contain the following:
rodent.wrotethebook.com crab.wrotethebook.com jamis.wrotethebook.com giant.wrotethebook.com horseshoe.wrotethebook.com
The MASQUERADE_DOMAIN_FILE
macro loads the file
into class $=M
. Adding the
limited_masquerade feature to the configuration
causes sendmail to ignore class $=w
and use
$=M
for masquerading, as the following test shows:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=w
example.com chef ora.com localhost.localdomain localhost [192.168.0.8] [localhost.localdomain] stateu.edu [127.0.0.1] horseshoe.wrotethebook.com wrotethebook.com chef.wrotethebook.com >$=M
rodent.wrotethebook.com crab.wrotethebook.com jamis.wrotethebook.com giant.wrotethebook.com horseshoe.wrotethebook.com >/tryflags HS
>/try esmtp amanda@stateu.edu
Trying header sender address amanda@stateu.edu for mailer esmtp canonify input: amanda @ stateu . edu Canonify2 input: amanda < @ stateu . edu > Canonify2 returns: amanda < @ stateu . edu . > canonify returns: amanda < @ stateu . edu . > 1 input: amanda < @ stateu . edu . > 1 returns: amanda < @ stateu . edu . > HdrFromSMTP input: amanda < @ stateu . edu . > PseudoToReal input: amanda < @ stateu . edu . > PseudoToReal returns: amanda < @ stateu . edu . > MasqSMTP input: amanda < @ stateu . edu . > MasqSMTP returns: amanda < @ stateu . edu . > MasqHdr input: amanda < @ stateu . edu . > MasqHdr returns: amanda < @ stateu . edu . > HdrFromSMTP returns: amanda < @ stateu . edu . > final input: amanda < @ stateu . edu . > final returns: amanda @ stateu . edu Rcode = 0, addr = amanda@stateu.edu >/quit
Now, mail from amanda@stateu.edu goes out with
her full stateu.edu address despite the fact
that stateu.edu still appears in class
$=w
. Only the hostnames in class
$=M
will be masqueraded.
The example used for these tests shows a single mail exchanger hosting multiple mail domains. This can also be done using virtual mail domains, which are covered in Chapter 5.
See Also
Recipe 2.1, Recipe 4.2,
Recipe 4.4, and Recipe 4.6
provide supporting information for this recipe. Recipe 4.4 and Recipe 4.6 cover similar
configurations that should be evaluated before implementing this
recipe. The sendmail book covers
MASQUERADE_AS
in 4.4.2,
EXPOSED_USER
in 4.4.1,
MASQUERADE_DOMAIN
in 4.4.3,
MASQUERADE_DOMAIN_FILE
in 4.4.4, and the
limited_masquerade feature in 4.8.18. The
“Address Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.8. Masquerading All Hosts in a Domain
Problem
You want
to masquerade every host within a domain
without defining every individual hostname in class
$=M
or class $=w
.
Solution
Create a sendmail configuration containing the
MASQUERADE_AS
and the
EXPOSED_USER
macros. Add the
MASQUERADE_DOMAIN
macro to define the domain to which
the masqueraded hosts belong. Also add the
masquerade_entire_domain
feature to ensure that every host in
the domain is masqueraded. Here is an example of these commands:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Store the domain name that will be masqueraded in class $=M MASQUERADE_DOMAIN(`wrotethebook.com') dnl Masquerade every host in the domain FEATURE(`masquerade_entire_domain')
Build the new sendmail.cf file, install it, and restart sendmail. Recipe 1.8 provides an example of these steps.
Discussion
By default, every hostname listed in class $=w
and
class $=M
is masqueraded when the
MASQUERADE_AS
macro is included in the
configuration. This works perfectly on most systems because the
system is only masquerading mail that originates on that system, and
every valid hostname for the local host is defined in class
$=w
. Therefore, the system will masquerade all
mail that is sent with one of its valid hostnames.
Mail exchangers, hubs, and relays are more complicated because they
may handle mail for a variety of hosts. It is very common for a mail
exchanger to handle mail for every host within a domain and to wish
to masquerade mail from every host in that domain. Simply adding the
domain name to class $=w
or class
$=M
is not enough because the domain name is
interpreted as a hostname, as this test shows:
#sendmail -bt -Crecipe4.6.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=m
wrotethebook.com horseshoe.wrotethebook.com >/tryflags HS
>/try esmtp michael@crab.wrotethebook.com
Trying header sender address michael@crab.wrotethebook.com for mailer esmtp canonify input: michael @ crab . wrotethebook . com Canonify2 input: michael < @ crab . wrotethebook . com > Canonify2 returns: michael < @ crab . wrotethebook . com . > canonify returns: michael < @ crab . wrotethebook . com . > 1 input: michael < @ crab . wrotethebook . com . > 1 returns: michael < @ crab . wrotethebook . com . > HdrFromSMTP input: michael < @ crab . wrotethebook . com . > PseudoToReal input: michael < @ crab . wrotethebook . com . > PseudoToReal returns: michael < @ crab . wrotethebook . com . > MasqSMTP input: michael < @ crab . wrotethebook . com . > MasqSMTP returns: michael < @ crab . wrotethebook . com . > MasqHdr input: michael < @ crab . wrotethebook . com . > MasqHdr returns: michael < @ crab . wrotethebook . com . > HdrFromSMTP returns: michael < @ crab . wrotethebook . com . > final input: michael < @ crab . wrotethebook . com . > final returns: michael @ crab . wrotethebook . com Rcode = 0, addr = michael@crab.wrotethebook.com >/quit
The test above shows that class $=M
contains the
value wrotethebook.com. However, mail from
crab, which is a host in the
wrotethebook.com domain, is not masqueraded
because the values in $=M
are viewed as hostnames,
and only exact matches are masqueraded.
Adding the masquerade_entire_domain feature to
the configuration changes this behavior. With this feature added,
values in class $=w
are still interpreted as
hostnames, but values in class $=M
are interpreted
as domain names, and every host in a domain listed in class
$=M
is masqueraded. The
masquerade_entire_domain feature is always
associated with either a MASQUERADE_DOMAIN
macro
or a MASQUERADE_DOMAIN_FILE
macro, both of which
load values into class $=M
, because the
masquerade_entire_domain feature only affects
values in class $=M
. Testing the configuration
created by this recipe shows the impact of this feature:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=M
wrotethebook.com >/tryflags HS
>/try esmtp michael@crab.wrotethebook.com
Trying header sender address michael@crab.wrotethebook.com for mailer esmtp canonify input: michael @ crab . wrotethebook . com Canonify2 input: michael < @ crab . wrotethebook . com > Canonify2 returns: michael < @ crab . wrotethebook . com . > canonify returns: michael < @ crab . wrotethebook . com . > 1 input: michael < @ crab . wrotethebook . com . > 1 returns: michael < @ crab . wrotethebook . com . > HdrFromSMTP input: michael < @ crab . wrotethebook . com . > PseudoToReal input: michael < @ crab . wrotethebook . com . > PseudoToReal returns: michael < @ crab . wrotethebook . com . > MasqSMTP input: michael < @ crab . wrotethebook . com . > MasqSMTP returns: michael < @ crab . wrotethebook . com . > MasqHdr input: michael < @ crab . wrotethebook . com . > MasqHdr returns: michael < @ wrotethebook . com . > HdrFromSMTP returns: michael < @ wrotethebook . com . > final input: michael < @ wrotethebook . com . > final returns: michael @ wrotethebook . com Rcode = 0, addr = michael@wrotethebook.com >/quit
The $=M
command shows the value stored in class
$=M
by the MASQUERADE_DOMAIN
macro. In this case we have only one value to store in class
$=M
, and we do not anticipate changing it, so
MASQUERADE_DOMAIN
works well. If you have several
values, you may want to use
MASQUERADE_DOMAIN_FILE
, which is used in Recipe
Recipe 4.6. In this test,
crab.wrotethebook.com is masqueraded because it
is a host in a domain listed in class
$=M
—the impact of the
masquerade_entire_domain feature. This feature
does not impact values in class $=w
. Those values
are still interpreted as hosts and are still masqueraded. If you want
to limit masquerading to just the domains defined in class
$=M
, add the
limited_masquerade feature to the configuration,
as described in
Recipe 4.7.
See Also
Recipe 4.4 and Recipe 4.7
cover similar configurations that should be evaluated before
implementing this recipe. The sendmail book
covers the masquerade_entire_domain feature in
4.8.25, MASQUERADE_AS
in 4.4.2,
EXPOSED_USER
in 4.4.1,
MASQUERADE_DOMAIN
in 4.4.3, and
MASQUERADE_DOMAIN_FILE
in 4.4.4. The
“Address Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.9. Masquerading Most of the Hosts in a Domain
Problem
You have been asked to create a sendmail configuration that masquerades all of the hosts in a domain, with the exception of a few special purpose hosts that should be exposed to the outside world.
Solution
Create a file listing all of the hosts in the domain that should be exempted from masquerading. The name of the file is arbitrary. This recipe uses the name /etc/mail/masquerade-exceptions.
Add the
masquerade_entire_domain
feature and the
MASQUERADE_AS
,
EXPOSED_USER
,
MASQUERADE_DOMAIN
, and
MASQUERADE_EXCEPTION_FILE
macros to the sendmail configuration.
Examples of the relevant commands are shown below:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Store the domain name that will be masqueraded in class $=M MASQUERADE_DOMAIN(`wrotethebook.com') dnl Masquerade all hosts in the domain FEATURE(`masquerade_entire_domain') dnl Load the list of hosts that should not be masqueraded MASQUERADE_EXCEPTION_FILE(`/etc/mail/masquerade-exceptions')
Using Recipe 1.8 as a guide, rebuild and reinstall sendmail.cf, and then restart sendmail.
Discussion
The masquerade_entire_domain feature causes
sendmail to treat every name in class $=M
as a
domain, and, when combined with the MASQUERADE_AS
macro, to masquerade every host within those domains. Recipe 4.8 shows the effect of the
masquerade_entire_domain feature. If you want to
masquerade most of the hosts in a domain, it is often easier to
masquerade the entire domain and then make exceptions than it is to
list all of the individual hosts that you want to masquerade. The
file identified by the MASQUERADE_EXCEPTION_FILE
macro is loaded into sendmail.cf class
$=N
. Class
$=N
contains a lists of
hosts that should not be masqueraded even if they belong to a domain
that is being masqueraded. For example, assume the
/etc/mail/masquerade-exceptions file created for
this recipe contains the following entries:
#cat > /etc/mail/masquerade-exceptions
www.wrotethebook.com info.wrotethebook.com sales.wrotethebook.comCtrl-D
This recipe masquerades all hosts in wrotethebook.com except for these three hosts, as this test shows:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=M
wrotethebook.com >/tryflags HS
>/try esmtp peyton@crab.wrotethebook.com
Trying header sender address peyton@crab.wrotethebook.com for mailer esmtp canonify input: peyton @ crab . wrotethebook . com Canonify2 input: peyton < @ crab . wrotethebook . com > Canonify2 returns: peyton < @ crab . wrotethebook . com . > canonify returns: peyton < @ crab . wrotethebook . com . > 1 input: peyton < @ crab . wrotethebook . com . > 1 returns: peyton < @ crab . wrotethebook . com . > HdrFromSMTP input: peyton < @ crab . wrotethebook . com . > PseudoToReal input: peyton < @ crab . wrotethebook . com . > PseudoToReal returns: peyton < @ crab . wrotethebook . com . > MasqSMTP input: peyton < @ crab . wrotethebook . com . > MasqSMTP returns: peyton < @ crab . wrotethebook . com . > MasqHdr input: peyton < @ crab . wrotethebook . com . > MasqHdr returns: peyton < @ wrotethebook . com . > HdrFromSMTP returns: peyton < @ wrotethebook . com . > final input: peyton < @ wrotethebook . com . > final returns: peyton @ wrotethebook . com Rcode = 0, addr = peyton@wrotethebook.com >$=N
info.wrotethebook.com www.wrotethebook.com sales.wrotethebook.com >/try esmtp jill@sales.wrotethebook.com
Trying header sender address jill@sales.wrotethebook.com for mailer esmtp canonify input: jill @ sales . wrotethebook . com Canonify2 input: jill < @ sales . wrotethebook . com > Canonify2 returns: jill < @ sales . wrotethebook . com . > canonify returns: jill < @ sales . wrotethebook . com . > 1 input: jill < @ sales . wrotethebook . com . > 1 returns: jill < @ sales . wrotethebook . com . > HdrFromSMTP input: jill < @ sales . wrotethebook . com . > PseudoToReal input: jill < @ sales . wrotethebook . com . > PseudoToReal returns: jill < @ sales . wrotethebook . com . > MasqSMTP input: jill < @ sales . wrotethebook . com . > MasqSMTP returns: jill < @ sales . wrotethebook . com . > MasqHdr input: jill < @ sales . wrotethebook . com . > MasqHdr returns: jill < @ sales . wrotethebook . com . > HdrFromSMTP returns: jill < @ sales . wrotethebook . com . > final input: jill < @ sales . wrotethebook . com . > final returns: jill @ sales . wrotethebook . com Rcode = 0, addr = jill@sales.wrotethebook.com >/quit
The $=M
command shows that class
$=M
contains the domain name
wrotethebook.com. In this configuration, the
masquerade_entire_domain feature is used, so
processing peyton@crab.wrotethebook.com as a
header sender address for the esmtp
mailer yields
the address peyton@wrotethebook.com because
crab is a host in the
wrotethebook.com domain. However, processing the
address jill@sales.wrotethebook.com as a header
sender address for the esmtp
mailer returns the
original address with no masquerading, even though
sales.wrotethebook.com is a host in the
wrotethebook.com domain. The reason that
sales.wrotethebook.com is not masqueraded is
because it is listed in class $=N
as the
$=N
command shows.
Alternatives
Given the small number of hostnames in the
masquerade-exceptions file, using the
MASQUERADE_EXCEPTION
macro would be a viable
alternative to creating a file for the
MASQUERADE_EXCEPTION_FILE
macro. This recipe could
be rewritten by replacing the MASQUERADE_EXCEPTION
macro with the following lines:
dnl Define hosts that should not be masqueraded MASQUERADE_EXCEPTION(`www.wrotethebook.com') MASQUERADE_EXCEPTION(`info.wrotethebook.com') MASQUERADE_EXCEPTION(`sales.wrotethebook.com')
The systems that are listed as exceptions to masquerading are
generally special purpose systems. There are usually only a limited
number of these systems, and there are few changes to this set of
systems. For these reasons, the
MASQUERADE_EXCEPTION
macro is a viable alternative
to creating a file to hold this list of hostnames. When you have a
large number of hosts that should be excepted from masquerading, or
the list of exempted hosts changes frequently, the
MASQUERADE_EXCEPTION_FILE
is the best choice. Use
the macro that you prefer—they both work
well.
See Also
Recipe 4.7 and Recipe 4.8
cover similar configurations that should be evaluated before
implementing this recipe. The sendmail book
covers the masquerade_entire_domain feature in
4.8.25, MASQUERADE_AS
in 4.4.2,
EXPOSED_USER
in 4.4.1,
MASQUERADE_DOMAIN
in 4.4.3,
MASQUERADE_DOMAIN_FILE
in 4.4.4, and
MASQUERADE_EXCEPTION
and
MASQUERADE_EXCEPTION_FILE
in 4.4.5. The
“Address Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.10. Masquerading the Envelope Address
Problem
In addition to masquerading the header sender address, you have been asked to create a configuration that masquerades the envelope sender address used by the SMTP protocol.
Solution
Add the
masquerade_envelope
feature, the
MASQUERADE_AS
macro, and the
EXPOSED_USER
macro to the sendmail
configuration file. Here are examples:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Users whose mail is not masqueraded EXPOSED_USER(root) dnl Masquerade the envelope address as wrotethebook.com FEATURE(`masquerade_envelope')
Build and install the new configuration, and then restart sendmail. Recipe 1.8 provides an example.
Discussion
By default, the MASQUERADE_AS
macro replaces the
hostname in the From
: message header with the
masquerade value. The From
: header address is
referred to as the header sender address. From
the point of view of the SMTP protocol, the message headers are just
part of the message—the data sent after the SMTP
DATA
command. The addresses exchanged by the
SMTP protocol before the SMTP
DATA
command are called the
envelope addresses, and the address of the
source of the mail is called the envelope sender
address. The envelope sender address appears in the SMTP
protocol exchange as the value in the SMTP MAIL
From
: command. By default, the
MASQUERADE_AS
macro does not masquerade the
hostname in the envelope sender address. A test of the basic
masquerade configuration shows this:
#sendmail -bt -Crecipe4.2.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags HS
>/try esmtp clark@horseshoe.wrotethebook.com
Trying header sender address clark@horseshoe.wrotethebook.com for mailer esmtp canonify input: clark @ horseshoe . wrotethebook . com Canonify2 input: clark < @ horseshoe . wrotethebook . com > Canonify2 returns: clark < @ horseshoe . wrotethebook . com . > canonify returns: clark < @ horseshoe . wrotethebook . com . > 1 input: clark < @ horseshoe . wrotethebook . com . > 1 returns: clark < @ horseshoe . wrotethebook . com . > HdrFromSMTP input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal returns: clark < @ horseshoe . wrotethebook . com . > MasqSMTP input: clark < @ horseshoe . wrotethebook . com . > MasqSMTP returns: clark < @ horseshoe . wrotethebook . com . > MasqHdr input: clark < @ horseshoe . wrotethebook . com . > MasqHdr returns: clark < @ wrotethebook . com . > HdrFromSMTP returns: clark < @ wrotethebook . com . > final input: clark < @ wrotethebook . com . > final returns: clark @ wrotethebook . com Rcode = 0, addr = clark@wrotethebook.com >/tryflags ES
>/try esmtp clark@horseshoe.wrotethebook.com
Trying envelope sender address clark@horseshoe.wrotethebook.com for mailer esmtp canonify input: clark @ horseshoe . wrotethebook . com Canonify2 input: clark < @ horseshoe . wrotethebook . com > Canonify2 returns: clark < @ horseshoe . wrotethebook . com . > canonify returns: clark < @ horseshoe . wrotethebook . com . > 1 input: clark < @ horseshoe . wrotethebook . com . > 1 returns: clark < @ horseshoe . wrotethebook . com . > EnvFromSMTP input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal returns: clark < @ horseshoe . wrotethebook . com . > MasqSMTP input: clark < @ horseshoe . wrotethebook . com . > MasqSMTP returns: clark < @ horseshoe . wrotethebook . com . > MasqEnv input: clark < @ horseshoe . wrotethebook . com . > MasqEnv returns: clark < @ horseshoe . wrotethebook . com . > EnvFromSMTP returns: clark < @ horseshoe . wrotethebook . com . > final input: clark < @ horseshoe . wrotethebook . com . > final returns: clark @ horseshoe . wrotethebook . com Rcode = 0, addr = clark@horseshoe.wrotethebook.com >/quit
The first /tryflags
command configures sendmail to
test header sender (HS
) address processing. The
first /try
command processes
clark@horseshoe.wrotethebook.com as the header
sender address for the esmtp
mailer. The result
shows that the address is masqueraded as
clark@wrotethebook.com. The second
/tryflags
command configures the system for
envelope sender (ES
) address processing. This
time, the address is not masqueraded. This is the basic masquerade
configuration; it masquerades header addresses but not envelope
addresses. The masquerade_envelope feature
changes this, as the following test of this recipe’s
configuration shows:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/tryflags ES
>/try esmtp clark@horseshoe.wrotethebook.com
Trying envelope sender address clark@horseshoe.wrotethebook.com for mailer esmtp canonify input: clark @ horseshoe . wrotethebook . com Canonify2 input: clark < @ horseshoe . wrotethebook . com > Canonify2 returns: clark < @ horseshoe . wrotethebook . com . > canonify returns: clark < @ horseshoe . wrotethebook . com . > 1 input: clark < @ horseshoe . wrotethebook . com . > 1 returns: clark < @ horseshoe . wrotethebook . com . > EnvFromSMTP input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal input: clark < @ horseshoe . wrotethebook . com . > PseudoToReal returns: clark < @ horseshoe . wrotethebook . com . > MasqSMTP input: clark < @ horseshoe . wrotethebook . com . > MasqSMTP returns: clark < @ horseshoe . wrotethebook . com . > MasqEnv input: clark < @ horseshoe . wrotethebook . com . > MasqHdr input: clark < @ horseshoe . wrotethebook . com . > MasqHdr returns: clark < @ wrotethebook . com . > MasqEnv returns: clark < @ wrotethebook . com . > EnvFromSMTP returns: clark < @ wrotethebook . com . > final input: clark < @ wrotethebook . com . > final returns: clark @ wrotethebook . com Rcode = 0, addr = clark@wrotethebook.com >/quit
This test replicates the second part of the earlier test. With the basic masquerade configuration, the envelope sender address was not masqueraded—now it is.
Users and user mail tools deal with header sender addresses. A reply in a user mail tool will reply to an address found in a message header. Masquerading header sender addresses ensures that remote users receive the correct address for replying to local users. In this way, masquerading benefits users.
Users, however, do not usually deal with envelope addresses. Masquerading envelope addresses simplifies machine interactions, and there are several good reasons to do this:
To enable relaying. Evaluating the envelope address is one of the standard checks sendmail performs to authorize relaying. The header sender address is not normally used in relaying. Thus, if hosts need to masquerade in order to pass mail through a relay, it is the envelope sender address that is masqueraded.
To ensure proper delivery of error messages, which are sent to the envelope address.
To prevent sendmail from rejecting mail from hosts using private hostnames.sendmail checks the envelope sender address to see if it can be resolved via DNS. When private hostnames are used internally, mail from those hosts must be masqueraded to a hostname found in the public DNS and that masquerading must be applied to the envelope address. Otherwise, the mail might be rejected by the sendmail process running on the remote system.
For these and other reasons, many sites that use masquerading apply it to both the header and the envelope addresses.
See Also
Recipe 4.2 provides supporting information
for this configuration. Chapter 3 covers
configuring a mail relay and discusses the use of the envelope
address in relaying. The sendmail book covers
the MASQUERADE_AS
macro in 4.4.2, the
EXPOSED_USER
macro in 4.4.1, and the
masquerade_envelope feature in 4.8.26. The
“Address
Masquerading” section of
Linux Sendmail Administration, by Craig Hunt
(Sybex), is a tutorial on masquerading. The
cf/README file covers masquerading in the
section Masquerading and Relaying.
4.11. Rewriting the From Address with the genericstable
Problem
You have been asked to configure sendmail to replace the login name used as the sender address for mail originating from the local host with an address that meets the organization’s desired address format.
Solution
Build a genericstable database to map the input sender address to the format you desire in the header sender address. Each entry in the genericstable contains two fields: the key and the value returned for that key. The key field of a genericstable entry can be either a full email address or a username. The value returned is the value that will be used as the rewritten sender address, which is normally a full address containing both a username and a hostname. To create the genericstable , first create a text file that contains the database entries and then run that text file through the makemap script to build the genericstable database.
To use the newly created genericstable database,
create a sendmail configuration containing the
genericstable feature and the
GENERICS_DOMAIN
macro. The domain name argument of
the GENERICS_DOMAIN
macro tells sendmail when to
use the genericstable. Here are sample lines
that could be added to the sendmail configuration:
dnl Process login names through the genericstable FEATURE(`genericstable') dnl Identify the host that the genericstable applies to GENERICS_DOMAIN(`chef.wrotethebook.com')
Rebuild, reinstall, and restart sendmail as described in Recipe 1.8.
Discussion
By default, mail originating on the local host uses the
user’s login name as the username part, so, in this
example, the key field of the genericstable is a
login name and the value returned is a full email address. The
mapping can be anything you wish, but, for this example, we map login
names to the user’s real name and the local domain
name formatted as
firstname
.lastname
@
domain
.[5]
To do this, a text file is created and then run through
makemap:
#cd /etc/mail
#cat > genericstable
kathy kathy.mccafferty@wrotethebook.com craig craig.hunt@wrotethebook.com sara sara.henson@wrotethebook.com dave david.craig@wrotethebook.com becky rebecca.fro@wrotethebook.com alana alana.smiley@wrotethebook.com jay jay.james@wrotethebook.comCtrl-D
#makemap hash genericstable < genericstable
Of course, if mail arrives at the local host addressed to
firstname
.lastname
@
domain
,
aliases are needed to deliver the mail to the users’
real address. Aliases based on the genericstable
entries shown above could be added to the
aliases database in the following manner:
#cd /etc/mail
#cat > aliases
kathy.mccafferty: kathy craig.hunt: craig sara.henson: sara david.craig: dave rebecca.fro: becky alana.smiley: alana jay.james: jayCtrl-D
#newaliases
After building the genericstable database and
adding the genericstable feature to the
configuration, the genericstable data is
available to sendmail, as this sendmail
-bt
test shows:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/map generics sara
map_lookup: generics (sara) returns sara.henson@wrotethebook.com (0) >/quit
Here sendmail is run with the -bt
option and the
/map
command is used to lookup the entry for the
login name sara. The value returned is
sara.henson@wrotethebook.com. Thus, one would
expect that any mail originating on this host from
sara would have the sender address rewritten to
sara.henson@wrotethebook.com. That would be the
case here, but only because this recipe includes the
GENERICS_DOMAIN
macro. Without the
GENERICS_DOMAIN
macro, the
genericstable feature does not act as one might
expect. Here is an example using the
genericstable feature without the
GENERICS_DOMAIN
macro:
#sendmail -bt -Cspecial4-11-test.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$j
chef.wrotethebook.com >/tryflags HS
>/try esmtp sara
Trying header sender address sara for mailer esmtp canonify input: sara Canonify2 input: sara Canonify2 returns: sara canonify returns: sara 1 input: sara 1 returns: sara HdrFromSMTP input: sara PseudoToReal input: sara PseudoToReal returns: sara MasqSMTP input: sara MasqSMTP returns: sara < @ *LOCAL* > MasqHdr input: sara < @ *LOCAL* > canonify input: sara . henson @ wrotethebook . com Canonify2 input: sara . henson < @ wrotethebook . com > Canonify2 returns: sara . henson < @ wrotethebook . com . > canonify returns: sara . henson < @ wrotethebook . com . > MasqHdr returns: sara . henson < @ wrotethebook . com . > HdrFromSMTP returns: sara . henson < @ wrotethebook . com . > final input: sara . henson < @ wrotethebook . com . > final returns: sara . henson @ wrotethebook . com Rcode = 0, addr = sara.henson@wrotethebook.com >/try esmtp sara@chef
Trying header sender address sara@chef for mailer esmtp canonify input: sara @ chef Canonify2 input: sara < @ chef > Canonify2 returns: sara < @ chef . wrotethebook . com . > canonify returns: sara < @ chef . wrotethebook . com . > 1 input: sara < @ chef . wrotethebook . com . > 1 returns: sara < @ chef . wrotethebook . com . > HdrFromSMTP input: sara < @ chef . wrotethebook . com . > PseudoToReal input: sara < @ chef . wrotethebook . com . > PseudoToReal returns: sara < @ chef . wrotethebook . com . > MasqSMTP input: sara < @ chef . wrotethebook . com . > MasqSMTP returns: sara < @ chef . wrotethebook . com . > MasqHdr input: sara < @ chef . wrotethebook . com . > MasqHdr returns: sara < @ chef . wrotethebook . com . > HdrFromSMTP returns: sara < @ chef . wrotethebook . com . > final input: sara < @ chef . wrotethebook . com . > final returns: sara @ chef . wrotethebook . com Rcode = 0, addr = sara@chef.wrotethebook.com >/quit
The test just shown is run on a host named
chef.wrotethebook.com, as the
$j
command shows. This host is using the
genericstable feature without the
GENERICS_DOMAIN
macro. Because Unix login names
must be unique on a given host, we know that
sara, sara@chef, and
sara@chef.wrotethebook.com are all the same
person. Yet the test shows that sendmail does not treat these input
addresses the same with regard to the
genericstable. When the input address has no
host part, it is rewritten; otherwise, it is not rewritten. The
reason is simple: the genericstable we created
only maps login names; none of the database keys contains a hostname
part. However, we want to create a consistent header sender
address—not an address that varies depending on the address
input by the user.
For this to be accomplished, this recipe uses the
GENERICS_DOMAIN
macro and sets that macro to the
fully qualified name used in the $j
macro. This
causes sendmail to consistently apply the
genericstable to rewriting the header sender
address for all mail that originates on this host. Here is a test run
with this recipe’s configuration:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=G
chef.wrotethebook.com >/tryflags HS
>/try esmtp sara@[127.0.0.1]
Trying header sender address sara@[127.0.0.1] for mailer esmtp canonify input: sara @ [ 127 . 0 . 0 . 1 ] Canonify2 input: sara < @ [ 127 . 0 . 0 . 1 ] > Canonify2 returns: sara < @ chef . wrotethebook . com . > canonify returns: sara < @ chef . wrotethebook . com . > 1 input: sara < @ chef . wrotethebook . com . > 1 returns: sara < @ chef . wrotethebook . com . > HdrFromSMTP input: sara < @ chef . wrotethebook . com . > PseudoToReal input: sara < @ chef . wrotethebook . com . > PseudoToReal returns: sara < @ chef . wrotethebook . com . > MasqSMTP input: sara < @ chef . wrotethebook . com . > MasqSMTP returns: sara < @ chef . wrotethebook . com . > MasqHdr input: sara < @ chef . wrotethebook . com . > canonify input: sara . henson @ wrotethebook . com Canonify2 input: sara . henson < @ wrotethebook . com > Canonify2 returns: sara . henson < @ wrotethebook . com . > canonify returns: sara . henson < @ wrotethebook . com . > MasqHdr returns: sara . henson < @ wrotethebook . com . > HdrFromSMTP returns: sara . henson < @ wrotethebook . com . > final input: sara . henson < @ wrotethebook . com . > final returns: sara . henson @ wrotethebook . com Rcode = 0, addr = sara.henson@wrotethebook.com >/quit
The GENERICS_DOMAIN
macro loads the value
specified on its command line into sendmail.cf
class $=G
. sendmail applies the
genericstable to any sender address that has a
name listed in class $=G
as its hostname part. In
this test, the $=G
command shows that class
$=G
contains only
chef.wrotethebook.com, which is the fully
qualified name of this host—the same value found in
$j
. Therefore, any hostname that sendmail rewrites
to $j
will match the value in
$=G
and will also be processed through the
genericstable. In this test, we show an extreme
case using the loopback address [127.0.0.1]
as the
hostname, which sendmail rewrites to the value returned by
$j
and then rewrites through the
genericstable. We could just as easily have used
chef, localhost,
[192.168.0.8]
, or
chef.wrotethebook.com as the hostname—all
valid variations of the local host’s name
work.[6]
In this recipe, the email processed through the
genericstable all originates on the local host.
Thus, the input sender address either has no hostname part, or the
hostname in the sender address is a hostname of the local host. Many
systems (mail relay hosts are an example) handle mail from a variety
of hosts. The sender address in that case can contain the hostname of
any host granted relaying privileges. Because mail is only processed
through the genericstable when the
sender’s hostname is found in class
$=G
, the hostnames of all of the hosts allowed to
send mail through the relay should be loaded into class
$=G
. If this is a small number of hostnames, it
can be done by placing multiple GENERICS_DOMAIN
macros in the configuration. If there are more than a few hostnames,
it is simpler to create a file that lists all of the hosts and then
to reference that file inside the configuration with a
GENERICS_DOMAIN_FILE
macro. For example, the
following macro used in this recipe, in place of the
GENERICS_DOMAIN
macro, would load
$=G
with all of the hostnames listed in a file
called /etc/mail/generics-domains:
GENERICS_DOMAIN_FILE(`/etc/mail/generics-domains')
The GENERICS_DOMAIN_FILE
macro makes it possible
to coordinate genericstable processing with
other sendmail functions simply by extracting hostnames from the
files that control those other functions and storing those names in
the file loaded by the GENERICS_DOMAIN_FILE
macro.
For example, copying the hostnames from
/etc/mail/local-host-names to the file loaded by
the GENERICS_DOMAIN_FILE
macro enables
genericstable processing for all systems that
use a mail
exchanger.
See Also
Recipe 4.12 shows another
genericstable example. Recipe 4.14 shows how the
genericstable can be read from an LDAP server.
The sendmail book covers the
genericstable in 4.8.16, the
GENERICS_DOMAIN
macro in 4.8.16.1, and the
GENERICS_DOMAIN_FILE
macro in 4.8.16.2. Chapter 9
of Linux Sendmail Administration, by Craig Hunt
(Sybex), contains the tutorial section “Masquerading
Usernames” that provides
additional information.
4.12. Rewriting Sender Addresses for an Entire Domain
Problem
You have been asked to configure sendmail to rewrite the sender address into your organization’s standard header sender address format on all mail originating from the local domain.
Solution
Build a genericstable database to map the input address to the format desired for the header sender address. Each entry in the genericstable contains two fields. The first field matches the input address and the second field rewrites the address. To create the genericstable, first create a text file that contains the database entries, then run that text file through the makemap command to build the genericstable database.
Add the genericstable feature, the
GENERICS_DOMAIN
macro, and the
generics_entire_domain
feature to the sendmail configuration.
The added commands would look something like the following:
dnl Process login names through the genericstable FEATURE(`genericstable') dnl Load wrotethebook.com into G GENERICS_DOMAIN(`wrotethebook.com') dnl Interpret the value in G as a domain name FEATURE(`generics_entire_domain')
Build sendmail.cf, copy it to /etc/mail, and then restart sendmail. See Recipe Recipe 1.8 if you need an example of this step.
Discussion
The input sender address is rewritten as specified by the genericstable. All or part of the input address is used as a key to search the genericstable. When a value is returned by the search, that value is used to rewrite the address. For this example, we create the following genericstable:
#cd /etc/mail
#cat > genericstable
kathy kathy.mccafferty@wrotethebook.com craig craig.hunt@wrotethebook.com sara sara.henson@wrotethebook.com dave david.craig@wrotethebook.com becky rebecca.fro@wrotethebook.com jay jay.james@wrotethebook.com alana@blur.wrotethebook.com alana.darling@wrotethebook.com alana@giant.wrotethebook.com alana.henson@wrotethebook.com alana alana.smiley@wrotethebook.comCtrl-D
#makemap hash genericstable < genericstable
The genericstable feature adds the code sendmail
needs to make use of the genericstable. The
GENERICS_DOMAIN
macro adds the value specified on
the macro command line to sendmail class $=G
.
Normally, the values listed in class $=G
are
interpreted as hostnames, and only exact matches enable
genericstable processing. The
generics_entire_domain feature causes sendmail
to interpret the values in class $=G
as domain
names, and any host within one of those domains is processed through
the genericstable. Here is a test of a system
running this recipe:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=G
wrotethebook.com >/tryflags HS
>/try esmtp dave@tassajara.wrotethebook.com
Trying header sender address dave@tassajara.wrotethebook.com for mailer esmtp canonify input: dave @ tassajara . wrotethebook . com Canonify2 input: dave < @ tassajara . wrotethebook . com > Canonify2 returns: dave < @ tassajara . wrotethebook . com . > canonify returns: dave < @ tassajara . wrotethebook . com . > 1 input: dave < @ tassajara . wrotethebook . com . > 1 returns: dave < @ tassajara . wrotethebook . com . > HdrFromSMTP input: dave < @ tassajara . wrotethebook . com . > PseudoToReal input: dave < @ tassajara . wrotethebook . com . > PseudoToReal returns: dave < @ tassajara . wrotethebook . com . > MasqSMTP input: dave < @ tassajara . wrotethebook . com . > MasqSMTP returns: dave < @ tassajara . wrotethebook . com . > MasqHdr input: dave < @ tassajara . wrotethebook . com . > canonify input: david . craig @ wrotethebook . com Canonify2 input: david . craig < @ wrotethebook . com > Canonify2 returns: david . craig < @ wrotethebook . com . > canonify returns: david . craig < @ wrotethebook . com . > MasqHdr returns: david . craig < @ wrotethebook . com . > HdrFromSMTP returns: david . craig < @ wrotethebook . com . > final input: david . craig < @ wrotethebook . com . > final returns: david . craig @ wrotethebook . com Rcode = 0, addr = david.craig@wrotethebook.com >/map generics dave
map_lookup: generics (dave) returns david.craig@wrotethebook.com (0) >/quit
The test shows that the hostname
tassajara.wrotethebook.com is not in class
$=G
; in fact, class $=G
only
contains the domain name wrotethebook.com. Yet
the header sender address
dave@tassajara.wrotethebook.com is rewritten to
david.craig@wrotethebook.com, which is the value
returned by the genericstable for the key
dave
.
In this example, every dave account in the entire wrotethebook.com domain belongs to David Craig. No matter what host in that domain he sends mail from, when the mail passes through this system, it is rewritten to david.craig@wrotethebook.com. For replies to the rewritten address to work correctly, the rewritten hostname must resolve to a host that will accept the mail, and that host must have an alias for david.craig that delivers the mail to the real dave account.
A more interesting case is the mapping of the username alana. Three people in the wrotethebook.com domain have this username: Alana Darling, Alana Henson, and Alana Smiley. The following test shows how each of these names are mapped:
# sendmail -bt ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> > /tryflags HS > /try esmtp alana@blur.wrotethebook.com Trying header sender address alana@blur.wrotethebook.com for mailer esmtp canonify input: alana @ blur . wrotethebook . com Canonify2 input: alana < @ blur . wrotethebook . com > Canonify2 returns: alana < @ blur . wrotethebook . com . > canonify returns: alana < @ blur . wrotethebook . com . > 1 input: alana < @ blur . wrotethebook . com . > 1 returns: alana < @ blur . wrotethebook . com . > HdrFromSMTP input: alana < @ blur . wrotethebook . com . > PseudoToReal input: alana < @ blur . wrotethebook . com . > PseudoToReal returns: alana < @ blur . wrotethebook . com . > MasqSMTP input: alana < @ blur . wrotethebook . com . > MasqSMTP returns: alana < @ blur . wrotethebook . com . > MasqHdr input: alana < @ blur . wrotethebook . com . > canonify input: alana . darling @ wrotethebook . com Canonify2 input: alana . darling < @ wrotethebook . com > Canonify2 returns: alana . darling < @ wrotethebook . com . > canonify returns: alana . darling < @ wrotethebook . com . > MasqHdr returns: alana . darling < @ wrotethebook . com . > HdrFromSMTP returns: alana . darling < @ wrotethebook . com . > final input: alana . darling < @ wrotethebook . com . > final returns: alana . darling @ wrotethebook . com Rcode = 0, addr = alana.darling@wrotethebook.com > /try esmtp alana@giant.wrotethebook.com Trying header sender address alana@giant.wrotethebook.com for mailer esmtp canonify input: alana @ giant . wrotethebook . com Canonify2 input: alana < @ giant . wrotethebook . com > Canonify2 returns: alana < @ giant . wrotethebook . com . > canonify returns: alana < @ giant . wrotethebook . com . > 1 input: alana < @ giant . wrotethebook . com . > 1 returns: alana < @ giant . wrotethebook . com . > HdrFromSMTP input: alana < @ giant . wrotethebook . com . > PseudoToReal input: alana < @ giant . wrotethebook . com . > PseudoToReal returns: alana < @ giant . wrotethebook . com . > MasqSMTP input: alana < @ giant . wrotethebook . com . > MasqSMTP returns: alana < @ giant . wrotethebook . com . > MasqHdr input: alana < @ giant . wrotethebook . com . > canonify input: alana . henson @ wrotethebook . com Canonify2 input: alana . henson < @ wrotethebook . com > Canonify2 returns: alana . henson < @ wrotethebook . com . > canonify returns: alana . henson < @ wrotethebook . com . > MasqHdr returns: alana . henson < @ wrotethebook . com . > HdrFromSMTP returns: alana . henson < @ wrotethebook . com . > final input: alana . henson < @ wrotethebook . com . > final returns: alana . henson @ wrotethebook . com Rcode = 0, addr = alana.henson@wrotethebook.com > /try esmtp alana@anywhere.wrotethebook.com Trying header sender address alana@anywhere.wrotethebook.com for mailer esmtp canonify input: alana @ anywhere . wrotethebook . com Canonify2 input: alana < @ anywhere . wrotethebook . com > Canonify2 returns: alana < @ anywhere . wrotethebook . com . > canonify returns: alana < @ anywhere . wrotethebook . com . > 1 input: alana < @ anywhere . wrotethebook . com . > 1 returns: alana < @ anywhere . wrotethebook . com . > HdrFromSMTP input: alana < @ anywhere . wrotethebook . com . > PseudoToReal input: alana < @ anywhere . wrotethebook . com . > PseudoToReal returns: alana < @ anywhere . wrotethebook . com . > MasqSMTP input: alana < @ anywhere . wrotethebook . com . > MasqSMTP returns: alana < @ anywhere . wrotethebook . com . > MasqHdr input: alana < @ anywhere . wrotethebook . com . > canonify input: alana . smiley @ wrotethebook . com Canonify2 input: alana . smiley < @ wrotethebook . com > Canonify2 returns: alana . smiley < @ wrotethebook . com . > canonify returns: alana . smiley < @ wrotethebook . com . > MasqHdr returns: alana . smiley < @ wrotethebook . com . > HdrFromSMTP returns: alana . smiley < @ wrotethebook . com . > final input: alana . smiley < @ wrotethebook . com . > final returns: alana . smiley @ wrotethebook . com Rcode = 0, addr = alana.smiley@wrotethebook.com > /quit
The complete addresses used in the genericstable keys for Alana Darling and Alana Henson make it possible for sendmail to do one-to-one mappings for those addresses. The key used for Alana Smiley’s entry, however, is just a username. That key matches any input address that contains the username alana, except for the input addresses alana@blur.wrotethebook.com and alana@giant.wrotethebook.com.
When a system handles mail that originates from several hosts, it is possible to have duplicate login names. The fact that the key in the genericstable can contain a full email address allows you to map these overlapping usernames.
See Also
Recipe 4.11 provides another
genericstable example, and Recipe 4.14 shows how the
genericstable can be read from an LDAP server.
The sendmail book covers the
genericstable in 4.8.16, the
GENERICS_DOMAIN
macro in 4.8.16.1, and the
generics_entire_domain feature in 4.8.15.
Chapter 9 of Linux Sendmail Administration, by
Craig Hunt (Sybex), contains the tutorial section
“Masquerading Usernames” that
provides additional information.
4.13. Masquerading with LDAP
Problem
You have been asked to configure sendmail so that it reads data that controls masquerading from an LDAP server.
Solution
On the LDAP server, add the sendmail.schema file to the LDAP configuration as described in Recipe 1.3.
On the LDAP server, add the masquerade configuration data to the LDAP
database. To do this, create an LDIF file containing the list of
masqueraded domains and an LDIF file containing the list of exposed
users. The object class of the data in both files must be the
sendmailMTAClass
defined in the sendmail.schema file. Use
ldapadd to add this data to the LDAP database.
Check the sendmail compiler options on the sendmail host. If sendmail
does not list LDAPMAP
among the
“Compiled with:” flags, recompile
and reinstall sendmail as described in Recipe 1.3.
On the sendmail host, create a configuration that reads the
MASQUERADE_DOMAIN_FILE
and the
EXPOSED_USER
files from the LDAP server. Set
confLDAP_CLUSTER
to match the
sendmailMTACluster
attribute used in
the records added to the LDAP database. Here is an example of the
lines added to the sendmail configuration:
dnl Masquerade the From address as wrotethebook.com MASQUERADE_AS(`wrotethebook.com') dnl Define the LDAP cluster to which this host belongs define(`confLDAP_CLUSTER', `wrotethebook.com')dnl dnl Use LDAP to read usernames that are not masqueraded EXPOSED_USER_FILE(`@LDAP') dnl Get the list of masqueraded hostnames from LDAP MASQUERADE_DOMAIN_FILE(`@LDAP')
Build the new sendmail.cf file and copy it to /etc/mail. Restart sendmail. See Recipe 1.8 for examples.
Discussion
This recipe assumes that you have a fully functional LDAP server to which the sendmail masquerading information can be added. If you need help on LDAP, see Understanding and Deploying LDAP Directory Services, by Howes, Smith, and Good (Macmillan), and LDAP System Administration, by Gerald Carter (O’Reilly). Get the LDAP server up, running, and debugged before you attempt this recipe.
After ensuring that the LDAP server is ready to support sendmail, add
the masquerade data to the LDAP database. This example creates a
record for the hostnames that will be loaded into the
$=M
class:
#cat > ldap-masquerade-domains
dn: sendmailMTAClassName=M, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAClass sendmailMTACluster: wrotethebook.com sendmailMTAClassName: M sendmailMTAClassValue: rodent.wrotethebook.com sendmailMTAClassValue: horseshoe.wrotethebook.com sendmailMTAClassValue: jamis.wrotethebook.comCtrl-D
#ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-masquerade-domains
Enter LDAP Password:SecretLDAPpassword
adding new entry "sendmailMTAClassName=M, dc=wrotethebook, dc=com"
This example adds the list of exposed users to the LDAP database:
#cat > ldap-exposed-users
dn: sendmailMTAClassName=E, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAClass sendmailMTACluster: wrotethebook.com sendmailMTAClassName: E sendmailMTAClassValue: rootCtrl-D
#ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-exposed-users
Enter LDAP Password:SecretLDAPpassword
adding new entry "sendmailMTAClassName=E, dc=wrotethebook, dc=com"
The examples above show the LDAP format used for sendmail class data.
The class to which the LDAP record applies is defined by the
sendmailMTAClassName
attribute. The values that
should be loaded into the specified class are defined in the LDAP
record using one or more sendmailMTAClassValue
attributes. In the examples, the record for class
$=M
contains three values, and the record for
class
$=E
contains one value.
Convert the LDIF data to LDAP format and add it to the LDAP database
using the ldapadd command.[7] Use the
ldapsearch command to examine the new
data:[8]
#ldapsearch -LLL -x '(sendmailMTAClassName=M)' sendmailMTAClassValue
dn: sendmailMTAClassName=M, dc=wrotethebook, dc=com sendmailMTAClassValue: rodent.wrotethebook.com sendmailMTAClassValue: horseshoe.wrotethebook.com sendmailMTAClassValue: jamis.wrotethebook.com #ldapsearch -LLL -x '(sendmailMTAClassName=E)' sendmailMTAClassValue
dn: sendmailMTAClassName=E, dc=wrotethebook, dc=com sendmailMTAClassValue: root
LDAP is ready. Now configure sendmail to ask LDAP for the masquerade
data by using the string @LDAP
in place of the
file path in the MASQUERADE_DOMAIN_FILE
and the
EXPOSED_USER_FILE
macros. When
@LDAP
is specified, sendmail queries LDAP for the
required data using the standard sendmail schema.
This recipe also defines a value for
confLDAP_CLUSTER
. sendmail LDAP records apply
either to an individual host or to a group of hosts called a
cluster. A cluster is analogous to a NIS
domain—it is a group of hosts that use the same LDAP data. The
cluster name is defined in the sendmail configuration with the
confLDAP_CLUSTER
variable and in the LDAP records
using the sendmailMTACluster
attribute. If the
LDAP records apply to a single host, the host is identified in the
LDAP records by the sendmailMTAHost
attribute, but
no special sendmail configuration is needed because the hostname
value returned by $j
is used for the LDAP query.
Note that if the confLDAP_CLUSTER
variable is
configured for a host that also has host-specific LDAP data, both the
host data and the cluster data will be returned when that host issues
an LDAP query. This recipe defines a cluster name in the sendmail
configuration and uses only cluster data in the LDAP database.
A simple test shows the impact of this recipe:
#sendmail -bt -Cgeneric-linux.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=M
>$=E
>/quit
#sendmail -bt -Csendmail.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=M
rodent.wrotethebook.com jamis.wrotethebook.com horseshoe.wrotethebook.com >$=E
root >/quit
A sendmail
-bt
test using the
generic configuration shows that, by default, class
$=M
and class $=E
are empty.
When the test is rerun using the sendmail.cf
file created in this recipe, class $=M
and class
$=E
contain the data defined in the LDAP database.
The MASQUERADE_DOMAIN_FILE
and the
EXPOSE_USER_FILE
are not the only files that can
be read from LDAP. The MASQUERADE_EXCEPTION_FILE
,
used in Recipe 4.9, can also be read from an
LDAP server by replacing the pathname in the macro command with the
string @LDAP
. In fact, any file that is loaded
into a class can be read from LDAP. Additionally, sendmail databases
can be read from LDAP, as the next recipe illustrates with the
genericstable.
Because classes are loaded during startup, you must restart sendmail whenever you add, change, or delete any LDAP records that affect sendmail classes. The changes only take effect after sendmail is restarted.
See Also
Recipe 4.6 explains the role that class
$=M
plays in masquerading, and Recipe 4.2 explains why class $=E
is
needed. Recipe 3.9 is another recipe that
uses LDAP to load a class, which provides additional insight on using
LDAP. The sendmail book covers the
MASQUERADE_DOMAIN_FILE
macro in Section 4.4.4, the
EXPOSE_USER_FILE
macro in Section 4.4.1.1, and the
confLDAP_CLUSTER
define in Section 21.9.82. The
cf/README file covers LDAP in the
Using LDAP for Aliases, Maps, and Classes
section.
4.14. Reading the genericstable via LDAP
Solution
On the LDAP server, add support for the sendmail.schema file to the LDAP configuration as described in Recipe 1.3.
On the LDAP server, add the genericstable data
to the LDAP database by first creating an LDIF file and then running
ldapadd. Use the
sendmailMTAMap
object class defined in the
sendmail.schema file to format the
genericstable data in the LDIF file.
On the LDAP server, you can also create an LDIF file containing the
generics domain data for class $=G
. The data
should be formatted using the sendmailMTAClass
object class defined in the sendmail.schema
file. Use ldapadd to add the data from the LDIF
file to the LDAP database.
On the sendmail system, check that sendmail includes LDAP support. If
the command sendmail
-bt
-d0.1
does not display the string
LDAPMAP
in the “Compiled
with:” list, recompile and reinstall sendmail as
described in Recipe 1.3.
On the sendmail system, add a genericstable
FEATURE
macro that loads the
genericstable from the LDAP server to the
sendmail configuration. Use the
GENERICS_DOMAIN_FILE
macro to load class
$=G
from the LDAP server. Set the
confLDAP_CLUSTER
define to match the sendmailMTACluster
attribute
used in the genericstable entries added to the
LDAP database. Here are sample lines you might add to a sendmail
configuration to read the genericstable from an
LDAP server:
dnl Define the sendmailMTACluster value define(`confLDAP_CLUSTER', `wrotethebook.com') dnl Load the genericstable from the LDAP server FEATURE(`genericstable', `LDAP') dnl Load class $=G from the LDAP server GENERICS_DOMAIN_FILE(`@LDAP')
Follow the example in Recipe 1.8 to build and install the sendmail.cf file and then restart sendmail.
Discussion
This recipe uses an LDAP server to duplicate the configuration used in Recipe 4.11. Why and how the genericstable is used is covered in Recipe 4.11. This recipe focuses on how the genericstable data is stored in and retrieved from an LDAP server.
The data is first entered into an LDIF file. Each data entry is
formatted in a manner compatible with the sendmail schema. The LDAP
record format used for the genericstable can be
used for any sendmail database by simply placing the
database’s map name in the
sendmailMTAMapName
attribute, the correct key data
in the sendmailMTAKey
attribute, and the correct
return value in the sendmailMTAMapValue
attribute.
Here is an example using the dave,
becky, and alana entries
from the genericstable described in Recipe 4.11.
#cat > ldap-generics
dn: sendmailMTAMapName=generics, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAMap sendmailMTACluster: wrotethebook.com sendmailMTAMapName: generics dn: sendmailMTAKey=dave, sendmailMTAMapName=generics, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAMap objectClass: sendmailMTAMapObject sendmailMTAMapName: generics sendmailMTACluster: wrotethebook.com sendmailMTAKey: dave sendmailMTAMapValue: david.craig@wrotethebook.com dn: sendmailMTAKey=becky, sendmailMTAMapName=generics, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAMap objectClass: sendmailMTAMapObject sendmailMTAMapName: generics sendmailMTACluster: wrotethebook.com sendmailMTAKey: becky sendmailMTAMapValue: rebecca.fro@wrotethebook.com dn: sendmailMTAKey=alana, sendmailMTAMapName=generics, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAMap objectClass: sendmailMTAMapObject sendmailMTAMapName: generics sendmailMTACluster: wrotethebook.com sendmailMTAKey: alana sendmailMTAMapValue: alana.smiley@wrotethebook.comCtrl-D
#ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-generics
Enter LDAP Password:SecretLDAPpassword
adding new entry "sendmailMTAMapName=generics, dc=wrotethebook, dc=com" adding new entry "sendmailMTAKey=dave, sendmailMTAMapName=generics, dc=wrotethebook, dc=com" adding new entry "sendmailMTAKey=becky, sendmailMTAMapName=generics, dc=wrotethebook, dc=com" adding new entry "sendmailMTAKey=alana, sendmailMTAMapName=generics, dc=wrotethebook, dc=com"
Notice that four entries are used to enter the first three
genericstable values in the LDAP database. The
first entry defines the genericstable map name,
which is generics
. Once the map name is known to
LDAP, data can be associated with that map name. The next three
entries contain the actual genericstable data.
The sendmail configuration in this recipe also uses LDAP to load
class $=G
. The following example adds a class
$=G
entry to the LDAP database containing a
sendmailMTAClassValue
attribute that matches the
GENERICS_DOMAIN
value used in Recipe 4.11:
#cat > ldap-generics-domain
dn: sendmailMTAClassName=G, dc=wrotethebook, dc=com objectClass: sendmailMTA objectClass: sendmailMTAClass sendmailMTAHost: chef.wrotethebook.com sendmailMTAClassName: G sendmailMTAClassValue: chef.wrotethebook.comCtrl-D
#ldapadd -x -D "cn=Manager,dc=wrotethebook,dc=com" \
> -W -f ldap-generics-domain
Enter LDAP Password:SecretLDAPpassword
adding new entry "sendmailMTAClassName=G, dc=wrotethebook, dc=com"
Again, the data is first entered into an LDIF file and then stored in
the LDAP database using the ldapadd command. The
sendmailMTAClassName
attribute identifies the
class to which the data belongs. The individual values bound for
class $=G
are defined by the
sendmailMTAClassValue
attributes in the LDAP
record. Only one sendmailMTAClassValue
is used in
the example, but a single sendmailMTAClassName
record can hold multiple values.
The ldapsearch command can be used to examine the results:
#ldapsearch -LLL -x '(sendmailMTAMapName=generics)' sendmailMTAMapValue
dn: sendmailMTAMapName=generics, dc=wrotethebook, dc=com dn: sendmailMTAKey=dave, sendmailMTAMapName=generics, dc=wrotethebook, dc=com sendmailMTAMapValue: david.craig@wrotethebook.com dn: sendmailMTAKey=becky, sendmailMTAMapName=generics, dc=wrotethebook, dc=com sendmailMTAMapValue: rebecca.fro@wrotethebook.com dn: sendmailMTAKey=alana, sendmailMTAMapName=generics, dc=wrotethebook, dc=com sendmailMTAMapValue: alana.smiley@wrotethebook.com #ldapsearch -LLL -x '(sendmailMTAClassName=G)' sendmailMTAClassValue
dn: sendmailMTAClassName=G, dc=wrotethebook, dc=com sendmailMTAClassValue: chef.wrotethebook.com
Three modifications were made to the sendmail configuration from Recipe 4.11 to create a configuration that reads genericstable data from LDAP:
The
confLDAP_CLUSTER
define was added to the configuration to set the sendmail.cf${sendmailMTACluster}
macro to the same value as thesendmailMTACluster
attribute used in the genericstable LDAP records. This ensures that sendmail retrieves the correct values from the LDAP server. If theconfLDAP_CLUSTER
define is not used, sendmail only retrieves LDAP records with asendmailMTAHost
attribute set to the fully qualified hostname of the sendmail host. In this example, the class$=G
LDAP record uses thesendmailMTAHost
attribute, so it would be retrieved by a host named chef.wrotethebook.com even ifconfLDAP_CLUSTER
was not defined in that host’s sendmail configuration.The
GENERICS_DOMAIN
macro used in Recipe 4.11 was changed to aGENERICS_DOMAIN_FILE
macro in this recipe. The@LDAP
string in theGENERICS_DOMAIN_FILE
macro tells sendmail to load class$=G
with data retrieved from the LDAP server. (Traditionally, the value passed to theGENERICS_DOMAIN_FILE
macro is the pathname of a local file that contains the data for class$=G
.)
The string
LDAP
was added to the genericstableFEATURE
command. TheLDAP
string tells sendmail to read the genericstable data from the LDAP server using the standard sendmail schema.
Adding the LDAP
string to the
genericstable
FEATURE
command
changes the format of the sendmail.cf
K
command that defines the
generics
database. A grep
shows the effect of the LDAP string:
#grep 'Kgenerics' recipe4-10.cf
Kgenerics hash /etc/mail/genericstable #grep '^Kgenerics' sendmail.cf
Kgenerics ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster}) (sendmailMTAHost=$j))(sendmailMTAMapName=generics)(sendmailMTAKey=%0))
The first grep shows the default format of the
K
command used to declare the
generics
database when no arguments are used with
the genericstable
FEATURE
command. By default, sendmail looks for a hash type database in the
local file /etc/mail/genericstable. Adding the
LDAP argument to the genericstable
FEATURE
command changes the database type to
ldap
and adds some LDAP-specific options to the
K
command. The -v
option
specifies the LDAP attribute used as the return value. The
-k
option defines the LDAP search criteria used as
a database key.
All of the attribute names used in the K
command
shown above are attributes defined in the sendmail schema. If you
define your own schema, you cannot simply use the
LDAP
string in the
genericstable
FEATURE
command. You must instead manually define -v
and
-k
options that use your custom schema. For
example:
FEATURE(`genericstable', `ldap: -1 -k (&(objectClass=LocalUserObject) (LocalUserKey=%0)) -v LocalUserEmailAddress')
Of course, the attribute names used in this example are imaginary and
would need to be replaced with the attributes you defined when you
designed your custom schema. However, the format of the
genericstable
FEATURE
command
is similar to the command you would use in your sendmail
configuration.
Notice that the K
command created by adding the
LDAP
argument to the
genericstable feature does not include the LDAP
server name or the LDAP default base distinguished name. The server
and the base can be added to the K
command using
the same -h
and -b
arguments
that you would use with the ldapsearch command.
In the sendmail configuration, the -h
and
-b
arguments are defined using
confLDAP_DEFAULT_SPEC
; see Recipe 5.9 for an example. However, it is not necessary
to use confLDAP_DEFAULT_SPEC
if the
HOST
and BASE
values in the
ldap.conf file are correct for sendmail.
After this recipe is installed, genericstable data can be read from LDAP exactly as if it were being read from a local database, as the following test illustrates:
#rm -f /etc/mail/genericstable*
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >/map generics alana
map_lookup: generics (alana) returns alana.smiley@wrotethebook.com (0) >/quit
The rm command proves that the test cannot be
reading genericstable data from a local file.
The /map
command passes the key
alana to the generics
database and gets the return value
alana.smiley@wrotethebook.com. The
/map
command in this example is formatted exactly
as it was in Recipe 4.11, when data was read
from a local database. The only difference is that, here, data is
being read from an LDAP server.
The test that was used in Recipe 4.11 to
evaluate the effect of the $=G
class can be rerun
after this recipe is installed. Again, it works exactly as expected
despite the fact that the data comes from an LDAP server:
#sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >$=G
chef.wrotethebook.com >/tryflags HS
>/try esmtp alana@[127.0.0.1]
Trying header sender address alana@[127.0.0.1] for mailer esmtp canonify input: alana @ [ 127 . 0 . 0 . 1 ] Canonify2 input: alana < @ [ 127 . 0 . 0 . 1 ] > Canonify2 returns: alana < @ rodent . wrotethebook . com . > canonify returns: alana < @ rodent . wrotethebook . com . > 1 input: alana < @ rodent . wrotethebook . com . > 1 returns: alana < @ rodent . wrotethebook . com . > HdrFromSMTP input: alana < @ rodent . wrotethebook . com . > PseudoToReal input: alana < @ rodent . wrotethebook . com . > PseudoToReal returns: alana < @ rodent . wrotethebook . com . > MasqSMTP input: alana < @ rodent . wrotethebook . com . > MasqSMTP returns: alana < @ rodent . wrotethebook . com . > MasqHdr input: alana < @ rodent . wrotethebook . com . > canonify input: alana . smiley @ wrotethebook . com Canonify2 input: alana . smiley < @ wrotethebook . com > Canonify2 returns: alana . smiley < @ wrotethebook . com . > canonify returns: alana . smiley < @ wrotethebook . com . > MasqHdr returns: alana . smiley < @ wrotethebook . com . > HdrFromSMTP returns: alana . smiley < @ wrotethebook . com . > final input: alana . smiley < @ wrotethebook . com . > final returns: alana . smiley @ wrotethebook . com Rcode = 0, addr = alana.smiley@wrotethebook.com >/quit
The data for any sendmail database and for any sendmail class can be defined centrally through an LDAP server. sendmail treats the data the same, regardless of the source.
See Also
Recipe 4.11 explains how the
genericstable is used and it explains the
importance of class $=G
. Chapter 5 provides additional examples of using LDAP for
sendmail databases. The sendmail book covers the
GENERICS_DOMAIN_FILE
macro in Section 4.8.16.2,
the genericstable in Section 4.8.16, and the
confLDAP_CLUSTER
define in Section 21.9.82. The
cf/README file covers LDAP in the section
Using LDAP for Aliases, Maps, and Classes.
[1] Even configurations that don’t use
MASQUERADE_AS
have a MasqHdr
ruleset that adds the fully qualified name of the local host returned
by $j
to the sender address when the address lacks
a hostname.
[2] To force
sendmail to ignore class $=w
and use only class
$=M
for masquerading, use the
limited_masquerade feature.
[3] Recipe 4.11 provides an example of building a genericstable database.
[4] Recipe 4.1 shows the effect of always_add_domain when used without masquerading.
[5] The
firstname
.lastname
@
domain
format is not universally appreciated. See the FAQ for some reasons
why you might not want to use this address format.
[6] This does not necessarily mean that all of the
local hostname aliases found in class $=w
will be
processed through the genericstable. This recipe
only processes hostnames that resolve to the value returned by
$j
.
[7] All LDAP examples in this book assume that OpenLDAP is being used. Adjust the commands to fit your system.
[8] If ldapsearch requires
-h
and -b
values, use
confLDAP_DEFAULT_SPEC
to set the same values for
sendmail, as described in Recipe 5.9.
Get sendmail Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.