or … what you would do with sendmail if you actually RTFM 😉

Introduction

It has been a long running joke (12+ years) between my friends and I on what mail server software you should use.

I chose sendmail, while my friends chose, exim, postfix, and qmail, or, well, imho, anything else instead.

They said “sendmail is too hard to configure”, to which I said “you haven’t read the manual”.

This page is dedicated to showing you how to configure sendmail in a secure configuration.

Before anyone talks about milter, spamasassin, and clamav – lets just start with the basics 😉 they’re something for another day/page/post/project.

Why Sendmail?

The reason why I choose sendmail is because of the following:-

It is the oldest SMTP implementation It is the original SMTP implementation against which all others are measured It comes with almost every single UNIX distribution – while many Linuxes etc have chosen alternatives it has served only deviate Linux from what is otherwise a standard platform environment. It often doesn’t require raising a change-control to install because it is already there in most cases (although you should change-control a configuration change and maybe even a data change) It’s not hard to configure once you’ve read the fscking manual – that’s true of all software. right?

Let’s not re-invent the wheel if we don’t have to.

Configuration Process

The key to configuring sendmail is to understand that it’s configuration file, sendmail.cf, is the product of a macro configuration, sendmail.mc, and that it is within the macro file that all configuration changes should be made.

Direct changes should never be made to the resulting .cf file as this file’s format is not guaranteed to be standardised, is version-specific, and subject to changes between releases (as notified by the developers in the operators guide) sufficient to prevent the configuration file which was built for one version of sendmail from being guaranteed to run with another version.

While in many cases the modification of the .cf file alone works, and in may cases the configuration file will work across releases – the method of modifying and/or migrating the cf file only cannot be trusted as its compatibility is not guaranteed and it is harder to reconstruct a macro file from a cf file than it is to reconstruct a cf file from a macro file and hence given the dynamic nature of the cf file makes the macro file the one and only place for making changes.

Sendmail also relies on an amount of data which it uses for mail routing information. This data is kept in in files with names such as access, aliases, virtusertable, mailertable and genericstable.

These are are compiled into simple databases to speed up access when performing look-ups.The are all compiled using the “makemap” command, discussed later on.

Right, now that you know about the macro file, we can start to configure sendmail.

Building the configuration

Creation of the sendmail configuration depends upon calling a set of pre-written macros within your sendmail.mc file to create the resulting sendmail.cf file. These are often found in a directory called “cf”. The custom macros are kept in a directory called “cf/cf”

The sendmail.mc macro file is parsed against these other m4 scripts to create the required configuration. from within the sendmail source’s “cf/cf” directory or equivalent sendmail configuration directory, the sendmail.cf file can be built from the “cf/cf” directory as follows:-

$ m4 ../m4/cf.4 sendmail.mc > sendmail.cf

cf.m4 is the top-level m4 script for building the configuration file. All other m4 scripts are called from here.

So, now you know how to create a configuration, lets get started on building one…

A typical sendmail configuration may/should resemble something like this – don’t worry about what it all means right now – all will be explained 😉

VERSIONID(`this is my custom sendmail.mc for linux - 201209302221')dnl OSTYPE(`linux')dnl DOMAIN(`generic')dnl undefine(`BITNET_RELAY')dnl undefine(`DECNET_RELAY')dnl undefine(`FAX_RELAY')dnl undefine(`UUCP_RELAY')dnl define(`confSMTP_LOGIN_MSG',`$j')dnl define(`confCF_VERSION',`0')dnl define(`confTO_IDENT',`0')dnl define(`confMAX_MESSAGE_SIZE',`2048000')dnl define(`confLOG_LEVEL',`1')dnl define(`confPRIVACY_FLAGS',`goaway,noexpn,novrfy,noetrn,noverb,nobodyreturn,needmailhelo,restrictmailq,restrictqrun')dnl define(`confAUTH_OPTIONS',`ABEX')dnl dnl define(`confSMART_HOST',`your.upstream.isp.smtp.service.tld')dnl FEATURE(`access_db',`hash -T <TMPF> /etc/mail/access')dnl FEATURE(`nouucp',`reject')dnl FEATURE(`use_cw_file')dnl FEATURE(`use_ct_file')dnl FEATURE(`virtusertable',`hash /etc/mail/virtusertable')dnl FEATURE(`mailertable',`hash /etc/mail/mailertable')dnl FEATURE(`genericstable',`hash /etc/mail/genericstable')dnl FEATURE(`domaintable',`hash /etc/mail/genericstable')dnl FEATURE(`relay_hosts_only')dnl FEATURE(`smrsh')dnl FEATURE(`blacklist_recipients')dnl FEATURE(`masquerade_envelope')dnl MASQUERADE_DOMAIN(`myolddomain.tld')dnl MASQUERADE_AS(`my.extenalname.tld')dnl EXPOSED_USER_FILE(`/etc/mail/exposed-users')dnl MAILER(smtp)dnl MAILER(local)dnl

Note: the ‘local’ mailer is shown for example but is included by default on all configurations.

Configuration Basics

Some Linux distributions (like Red Hat) come with configuration files which start with an include reference in order to avoid calling the top-level macro definition “../m4/cf.m4” on the command-line:-

include(`../m4/cf.m4')dnl

A basic sendmail configuration should then start with the (non-mandatory) version ID:-

VERSIONID(`This is my custom configuration for Sendmail v0.1')dnl

This field is supposed to contain an RCS, SVN, or other change reference, but equally will hold any text string in which you may want to use for tracking your configuration releases.

Note the non-mandatory syntax convention- functions are called in capitals with parenthesis – quotes are surrounded by backticks and single-quotes (`’), this merely improves readability but it is impressed to maintain the convention.

In the m4 language, dnl means “do-not-load” and as such discards anything to the right of the substring “dnl” before the next new line. It is good practice to end each directive with dnl.

The next line in the configurtion details OS-specific information:-

OSTYPE(`linux')dnl

This loads configuration directives which are relevant to your operating system class. You can make custom ostype macros by copying your favourite file in ../ostype to another name within there and then making the appropriate tweaks.

Popular options for OSTYPE include:-

aix5

darwin

freebsd6

hpux11

linux

openbsd

solaris8 (5.8 – 5.11)

There are more (too numerous to list endlessly). You will know which one to use depending upon your OS.

It may be desirable to create a custom ostype in cf/ostype if you are developing a new system. The content of the ostype macro is simply generic settings which apply to each OS.

Domain-specific settings

The next line in the configuration details domain-specific information. Most people use:-

DOMAIN(`generic')dnl

This sets domain-specific settings particular to your domain. the ‘generic’ profile allows for standard mail functionality for IPv4 (and maybe IPv6) DNS based mail routing at a bare minimum.

Pre-defined values may be (not that they apply to *your* domain and are the only examples) could be, amongst your own macros:-

Berkely.EDU

berkeley-only

CS.Berkeley.EDU

EECS.Berkeley.EDU

generic

S2K.Berkeley.EDU

The “generic” profile will do for most people however making a customised profile can have benefits.

It may be desirable to create a custom domain in cf/domain if you are developing a large environment. The content of the domain macro is simply generic settings which apply to the domain in question.

Disabling unwanted features

It is desirable to disable unwanted features as sendmail supports many different types of message transport beyond SMTP and these unused transports can represent unexpected attack vectors for malicious attacks.

Obscure formats such as BITnet and DECnet, along with UUCP are supported. The more useful FAX service is also supported, however that too is of limited value in a modern world.

These should be disabled at the start with the following declarations:-

undefine(`BITNET_RELAY')dnl undefine(`DECNET_RELAY')dnl undefine(`FAX_RELAY')dnl undefine(`UUCP_RELAY')dnl FEATURE(`nouucp',`reject')dnl

This ensures that uncommon transports do not become unexpected attack vectors as the code is old and most likely unmaintained hence it is better to switch it off from the start.

Obfuscating server details

The next step is to configure the mail server to mask it’s identity – this includes obfuscating the implementation and versioning of this release. The following directives disguise the identity of the software implementation:-

define(`confSMTP_LOGIN_MSG',`$j')dnl define(`confCF_VERSION',`0')dnl

This ensures that the initial “200” line returns nothing more than “200 hostname ESMTP” and that the configuration version returns nothing meaningful. This is just enough to allow SSL/TLS validation to succeed.

The configuration version is also obfuscated as this is generally version-specific for sendmail and therefore gives opportunity to elicit the release of the software given it’s configuration version.

There are certain hints which still give the implementation away despite efforts to obfuscate it such as error codes such as “500-2.0.0 whatever the error is” – such that the format <SMTP code>-<x>.<y>.<z> is instantly recognisable as being sendmail.

Help File disclosure

Next, wipe the helpfile, which also gives away the release and version of the software needs to be emptied into a null file:-

$ > /etc/mail/helpfile

The mail server will throw an error if this file does not exist and in that error the software implementation is revealed.

Do not set the helpfile to /dev/null in the configuration as the error message returned again reveals the Sendmail release.

The null file ensures a successful null response with a successful return code.

Disable ident lookup

Disable an ancient and irrelevant protocol for reverse username lookup

define(`confTO_IDENT',`0')dnl

Maximum Message Size

Set the maximum message size in order to limit attachment size. Example below shows a 20M limit:-

define(`confMAX_MESSAGE_SIZE',`2097152')dnl

Logging

Set the level of logging required to elicit sufficient information for audit purposes. Increase verbosity for debugging, but return values to normal levels afterwards to avoid storing un-necessary information.

I can’t remember where I found it but I believe it was at one point there being a recommendation to set logging to 1 on older AIX hosts as many old versions shipped with AIX have a vulnerability where it is possible to crash the sendmail daemon by sending it a mail crafted to cause the logging subsystem to fail when logging beyond “1” is enabled.

define(`confLOG_LEVEL',`1')dnl

If things don’t work – consider increasing this number until it tells you something useful

Masquerading

It may be necessary to over-ride the default host-name of the machine:-

define(`confDOMAIN_NAME',`smtp.blah.com')dnl

This directive affects the “200” and “250” responses, used for server identification.

This can be used to over-ride the hostname where the host has an external name or where the TLS certificate on the server uses a common name other than the actual machine hostname.

Privacy Flags

The following privacy flags disable every advertised piece of functionality which is not required for normal mail delivery and enforce some security features.

define(`confPRIVACY_FLAGS',`goaway,noexpn,novrfy,noetrn,noverb,nobodyreturn,needmailhelo,restrictmailq,restrictqrun,authwarnings')dnl

Authentication Options

The following authentication options ensure that authentication amongst other things is not offered unless the administrator wishes to enable it:-

define(`confAUTH_OPTIONS',`ABEX')dnl

This directly affects the EHLO response and causes less frequently used features to be disabled and therefore not be advertised.

Possible auth options are:-

A – Do not offer AUTH

a – Offer AUTH (default)

B – Do not offer VERB

b – Offer VERB (default)

C – Do not require security layer for

” – plaintext AUTH (default)

c – Require security layer for plaintext AUTH

D – Do not offer DSN

d – Offer DSN (default)

E – Do not offer ETRN

e – Offer ETRN (default)

L – Do not require AUTH (default)

l – Require AUTH

P – Do not offer PIPELINING

p – Offer PIPELINING (default)

S – Do not offer STARTTLS

s – Offer STARTTLS (default)

V – Do not request a client certifcate

v – Request a client certifcate (default)

X – Do not offer EXPN

x – Offer EXPN (default)

(Optional) SMART_HOST configuration

If you need to push all outbound mail to an upstream host, use the following definition

define(`confSMART_HOST',`your.upstream.isp.smtp.service.tld')dnl

Daemon Options

The following options set the daemon behaviours:-

DAEMON_OPTIONS(`M=AE')dnl

Possible options for modifier “M” are:-

a – always require authentication

b – bind to interface through which mail has been received

c – perform hostname canonifcation (.cf)

f – require fully qualifed hostname (.cf)

s – Run smtps (SMTP over SSL) instead of smtp

u – allow unqualifed addresses (.cf)

A – disable AUTH (overrides ’a’ modifer)

C – don’t perform hostname canonifcation

E – disallow ETRN (see RFC 2476)

O – optional; if opening the socket fails ignore it

S – don’t offer STARTTLS

Useful Features

The following features add functionality to the mail server.

Note: you should use tab spacing between data pairs within the data files listed herein.

Enable access database

The following feature enables access lists for supporting domains other than the local domain names.

FEATURE(`access_db',`hash -T <TMPF> /etc/mail/access.db')dnl

This ‘access’ database details what hosts, networks, and domains are allowed to use this relay. It’s format is a simple pair of values separated by tabs:-

identity facility

Where the identity is the host ip, hostname, netblock, or domain name, and the facility is one of:-

OK – accept mail for local delivery

RELAY – accept mail for remote delivery

REJECT – simply reject the mail

DISCARD – use with caution – only discards for recipient affected

### custom error string – tell them something they might not care to know in an RFC821 compliant response

ERROR:D.S.N ### custom error string – tell them something they might not care to know in an RFC1893 compliant response

An example for one primary domain and ‘store-and-forward’ for another domain would look something like:-

mydomain.tld OK myfriendsdomain.tld RELAY somespammer@youdontlike.tld REJECT somespammer@you-really-dont-like.tld 550 go forth and multiply

Local host names

The ‘use-cw-file’ feature enables the local-host-names file where all references to the local machine are recorded. The feature is enabled as follows:-

FEATURE(`use_cw_file')dnl

This enables the file /etc/mail/local-host-names and any domain listed in this file is destined for local delivery. The content of this file may be

localhost.localdomain localhost hostname.mydomain.tld

Trused Users

The following features enables a list of local users enumerated in /etc/mail/trusted-users

FEATURE(`use_ct_file')dnl

These users are allowed to spoof their from addresses without root privilege. /etc/mail/trusted-users may look something like:-

appusr01 appusr02 httpd

Where UNIX users appusr01, appusr02 and httpd can send mail with fake “from” addresses on the command-line.

Virtual Users (or destination address re-writing for users)

Enable virtual uers to be redirected to alternate addresses

FEATURE(`virtusertable',`hash /etc/mail/virtusertable.db')dnl

This database again consists of key pairs, this time, delimited by a colon (“:”):-

@myolddomain.cctld %1@mynewdomain.tld

This makes user1@myolddomain.cctld get re-written and forwarded to user1@mynewdomain.tld

Source address re-writing

Sometimes it may be necessary to masquerade as another domain name if the internal DNS namespace is not the same as the external DNS namespace. While masquerading seems a good idea, the genericstable feature enables similar functionality with far greater control over masquerading the source address.

FEATURE(`genericstable',`hash /etc/mail/genericstable.db')dnl

A genericstable data file consists of expression pairs which determine the re-writing of addresses. The following example masquerades host1 and host2 for example.com, preserving the username portion of the email address:-

@host1.mydomain.tld %1@mydomain.tld @host2.mydomain.tld %1@mydomain.tld root unix@mydomain.tld oracle dba@mydomain.tld

These rules make mail from user1@host1.mydomain.tld and user1@host2.mydomain.tld look like user1@mydomain.tld, along with making local users root and oracle look like their respective unix@mydomain.tld and dba@mydomain.tld

Domain short-names and aliases

The following feature will allow aliases which resolve as per the map in order to rewrite lookup requests when considering delivery

FEATURE(`domantable',`hash /etc/mail/domaintable')dnl

Enables the file /etc/mail/domaintable to create alias pairs such like

myorgalias myorgalias.geo.global.tld

Which would make root@myorgalias route to root@myorgalias.geo.global.tld instead.

Mail Routing

Enable selected domains to be routed to a destination of choice, in some cases (where [] parenthesis is used) over-ride DNS.

FEATURE(`mailertable',`hash /etc/mail/mailertable.db')dnl

Redirect incoming mail to specific hosts with a /etc/mail/mailertable file:-

mydomain.tld smtp:[mymailboxserver] myotherdomain.cctld smtp:[mymailboxserver] *.myotherdoain.cctld smtp:[myoldmailboxserver]

The default behavior of sendmail is to deliver by MX despite entries in the mailertable – square parenthesis []are used to note where mailertable over-rides DNS.

The square brackets are to over-ride DNS MX record lookup, which sendmail does by default despite there being an entry in the mailertable (which would only get attempted if the default MX lookup failed), hence the square brackets instruct sendmail to over-ride DNS MX record lookup.

Relay hosts only

The following feature ensure that your MTA does not become an open relay to all other hosts within your DNS domain

FEATURE(`relay_hosts_only’)dnl

See my nightmare post on this

Sendmail restricted shell

The following feature enables the sendmail restricted shell, otherwise known as smrsh, this provides a minimal shell for when sendmail is performing certain commands from the shell.

FEATURE(`smrsh')dnl

Address Blacklisting

The following feature enables, unsurprisingly, the facility of blacklisting recipients.

FEATURE(`blacklist_recipients')dnl

Masquerading

Masquerading is made up of a number of configuration directives. These directives control what parts of the header are re-written and for what domains the masquerading effects. This is normally limited to local domains only and is intended to over-ride the domain name for local mail destined outbound.

Make sure the envelope is masqueraded

FEATURE(`masquerade_envelope')dnl

Domain to be masqueraded

Typically a local machine alias

MASQUERADE_DOMAIN(`myolddomain.tld')dnl

Domain to be masqueraded as

Typically the organisational unit’s top level domain

MASQUERADE_AS(`my.extenalname.tld')dnl

Domain users who are not to be masqueraded

These are normally system and administrative accounts which should remain exposed

EXPOSED_USER_FILE(`/etc/mail/exposed-users')dnl

This list of users should normally include the following:-

root

You will see this behaviour expressed commonly in many configurations a simply

EXPOSED_USER(`root')dnl

Using the EXPOSED_USER_FILE avoids having to recompile the configuration every time a user is added to this list

Mailer Selection

The mailer section defines the local transports available, and the order in which the transports are used (in the case of smtp and uucp where smtp has to come first).

This section consists of MAILER() statements and some local configuration for those mailers.

At this point, it is well worth trying to discover the previous .mc file or an alternative .mc file for a machine of the same type if you wish for successful local delivery to anything other than a simple mailbox in /var/mail. Once discovered, this can be melded into your configuration.

If you don’t want more than smtp and maybe simple local delivery, then the following directives suffice:-

MAILER(`smtp')dnl

This is the only ‘useful’ mailer for SMTP. The following SMTP modes are known as alternatives to the default ‘smtp’ behaviour:-

“smtp” to do regular SMTP

“esmtp” to do ESMTP

“smtp8” to do 8bit MIME

“dsmtp” to do on demand delivery

“relay” for transmission to the RELAY_HOST, LUSER_RELAY, or MAIL_HUB.

Making it all work

Compiling the macro

Now the configuration is complete, it is time to compile the .cf file from the .mc file as follows from the cf/cf directory :-

m4 ../m4/cf.m4 ../sendmail.mc > sendmail.cf

Backup the old configuration

for x in mc cf ; do

cp /etc/mail/sendmail.$x /etc/mail/sendmail.$x.`date '+%Y-%m-%dT%H:%M:%D'`

done

Create necessary data files

Ensure all files which sendmail.cf relies upon exists by touching the file for each of the files identified in the output

grep etc sendmail.cf | egrep -v '^\#' | cut -f 2- -d \/ | while read filepath ; do touch /$filepath ; done

Create the database files

Ensure that all of the corresponding .db files for all databases (this example assumes all DBs are hash dbs)

egrep '^K' sendmail.cf | grep etc | cut -f 2- -d \/ | while read dbpath ; do makemap hash /$dbpath < /$dbpath ; done

Copy-in the new configuration

Place the newly built config into /etc/mail

cp sendmail.cf /etc/mail/sendmail.cf

Restart sendmail

Call the init script to restart sendmail on its new configuration

/etc/init.d/sendmail restart

Check logs

It is important to follow up any restart whether successful or otherwise with a quick look in the messages or maillog file

tail /var/log/messages

tail /var/log/maillog

If things are still not working – increase the LOG_LEVEL definition and restart

Troubleshooting

If you find your new configuration does not work, check for and understand the differences between your new configuration and your previously known good configuration. Are there things in the old config you stiill need (like a MAILER(procmail) definition or some associated features and define() fragments?

The bad news is that it can be at first a bit of trial and error – but the good news is that Sendmail has been run on so many platforms and in so many configurations, you would be hard pushed to find you’re using it in a way that someone has not done before and therefore a quick search can get you easily back on track.

TODO

LDAP configuration

TLS certificates (and setting mandatory TLS)

Anti-Virus and Anti-Spam

Anything else with libmilter