Using Sendmail to Proxy Mail Through a Firewall


I haven't seen clear instructions for setting up Sendmail to proxy mail through a bastion host, so once I figured it out I thought I should post them.

The setup I have is a bastion host in a DMZ and an internal mail hub. The bastion host receives incoming mail from the Internet and relays it to the internal mail hub. It also receives outgoing mail from the mail hub and delivers it to the appropriate destination host. I did this using Sendmail 8.11.x, but it should be fairly easy to use these instructions on any 8.10+ version.

First we'll look at the configuration on the bastion host. Here is a good basic m4 config file for a bastion host, with the necessary bits for our mail proxying highlighted:

include(`/usr/local/cf/m4/cf.m4')dnl
VERSIONID(`$Id$')dnl
OSTYPE(`linux')dnl
FEATURE(`nouucp', `reject')dnl
FEATURE(`always_add_domain')dnl
MASQUERADE_AS(`foo.net')dnl
FEATURE(`masquerade_envelope')dnl
FEATURE(`mailertable')dnl
FEATURE(`access_db')dnl
MAILER(`smtp')dnl
dnl Turn off ident querying, which is usually wrong and slows things down
define(`confTO_IDENT', `0')dnl
dnl Since the bastion box never delivers mail to disk or anything that
dnl generally requires it to assume a user's identity, we can run it as
dnl something other than root, which is very good.
define(`confRUN_AS_USER', `mailnull:mailnull')dnl
dnl Sendmail defaults to giving out all kinds of useful (to hackers)
dnl information in the greeting message.  There are still a number of
dnl ways to get Sendmail to give you that information, but this makes
dnl it a little harder.
define(`confSMTP_LOGIN_MSG', `')dnl
dnl This disables all of the commands that would allow an outsider to
dnl confirm email addresses, see who root mail is sent to, etc.
define(`confPRIVACY_FLAGS', `goaway')dnl
dnl Send a copy of bounce messages to the postmaster
define(`confCOPY_ERRORS_TO', `postmaster')dnl

Some things will obviously have to be changed for your environment. The VERSIONID is setup for RCS or CVS, change it as appropriate for your version control system. Note that we don't define the local mailer. The bastion host should never deliver mail to local disk, so that is appropriate. You'll need to make sure that you have a user and group named mailnull, or change the m4 file as appropriate. You'll need to set /var/spool/mqueue to be owned by the mailnull user in case Sendmail needs to spool some mail temporarily. Other than that the mailnull user and group should not have any privileges. Build a sendmail.cf out of this with m4 and put it in the right place.

The second step on the bastion host is to create a couple of files in /etc/mail (or /etc or whatever Sendmail thinks your system should use). The first is the mailertable referenced in the m4 file. A sample mailertable looks like:

foo.net		esmtp:[mailhub.foo.net]
.foo.net	esmtp:[mailhub.foo.net]

This tells Sendmail to relay all mail addressed to foo.net and machines in the foo.net domain (and any subdomains) to mailhub.foo.net. Sendmail expects the mailertable to be a database. Sendmail seems to recommend the hash format for mailertables if your makemap command supports it. Once you've created the text file, make a database out of it like so. This will make a file called mailertable.db (and no, it won't overwrite your original mailertable file even though the syntax sure looks like it will).

cd /etc/mail
makemap hash mailertable < mailertable

The last file to create is the access database, which we use to control who can send mail through this server. The following example will cause Sendmail to accept all mail from the mailhub (presumably outgoing mail) and mail from anywhere else as long as it is addressed to foo.net or some host in foo.net. You will need to make a database out of this using makemap just like with the mailertable.

Connect:mailhub.foo.net		RELAY
To:foo.net			RELAY

Ok, that's it for the bastion host. Just to review, you should have the following files in /etc/mail:

Now for the mail hub. Again, a sample m4 config file with the important bits highlighted:

include(`/usr/local/cf/m4/cf.m4')dnl
VERSIONID(`$Id$')dnl
OSTYPE(`solaris2')dnl
FEATURE(`nouucp', `reject')dnl
FEATURE(`always_add_domain')dnl
MASQUERADE_AS(`foo.net')dnl
FEATURE(`masquerade_envelope')dnl
FEATURE(`local_procmail')dnl
FEATURE(`mailertable')dnl
MAILER(`local')dnl
MAILER(`smtp')dnl
define(`SMART_HOST', `bastion.foo.net')dnl
define(`confTO_IDENT', `0')dnl
define(`confSMTP_LOGIN_MSG', `')dnl
define(`confPRIVACY_FLAGS', `goaway')dnl
define(`confCOPY_ERRORS_TO', `postmaster')dnl

Note that this time we define both the local and smtp mailers, as we'll want to deliver local mail to disk and outgoing mail via SMTP to the bastion host. The mailertable on the internal machine allows us to specify whole domains worth of hosts that we want the mail hub to accept mail for as if it were those machines. In a simpler environment you could use the use_cw_file feature instead and just create a text file (local-host-names) with the appropriate list of machines.

We also need to create some config files in /etc/mail on the mail hub. One of those is relay-domains, which is a simpler (but less secure) way of controlling relaying than the access database used on the bastion host. On the mail hub we can just add foo.net to /etc/mail/relay-domains, which tells sendmail to accept any mail addressed to or from our domain.

And, for sake of example, let's assume you are using a mailertable instead of use_cw_file. Here's a sample mailertable for a mail hub. This one directs Sendmail to send mail addressed to foo.net and any machine in the foo.net domain (or any subdomains) to the local mailer, which should deliver it to the user's mail folder. All other mail will be passed off to the smart host defined in the m4 file.

foo.net		local:
.foo.net	local:

To summarize for the mail hub, you should have the following files in /etc/mail:

That's it, fire up Sendmail on both boxes and run some tests.


Home
jheiss at aput.net
$Id: proxy.shtml,v 1.10 2009/01/21 17:28:00 jheiss Exp $