or … what you would do with sendmail if you actually RTFM 😉
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.
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.
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.
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:-
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:-
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:-
- 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.
The next line in the configuration details domain-specific information. Most people use:-
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:-
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:-
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
Maximum Message Size
Set the maximum message size in order to limit attachment size. Example below shows a 20M limit:-
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.
If things don’t work – consider increasing this number until it tells you something useful
It may be necessary to over-ride the default host-name of the machine:-
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.
The following privacy flags disable every advertised piece of functionality which is not required for normal mail delivery and enforce some security features.
The following authentication options ensure that authentication amongst other things is not offered unless the administrator wishes to enable it:-
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
The following options set the daemon behaviours:-
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
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:-
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 firstname.lastname@example.org REJECT email@example.com 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:-
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
The following features enables a list of local users enumerated in /etc/mail/trusted-users
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
This database again consists of key pairs, this time, delimited by a colon (“:”):-
This makes firstname.lastname@example.org get re-written and forwarded to email@example.com
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.
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 %firstname.lastname@example.org @host2.mydomain.tld %email@example.com root firstname.lastname@example.org oracle email@example.com
These rules make mail from firstname.lastname@example.org and email@example.com look like firstname.lastname@example.org, along with making local users root and oracle look like their respective email@example.com and firstname.lastname@example.org
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
Enables the file /etc/mail/domaintable to create alias pairs such like
Which would make root@myorgalias route to email@example.com instead.
Enable selected domains to be routed to a destination of choice, in some cases (where  parenthesis is used) over-ride DNS.
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
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.
The following feature enables, unsurprisingly, the facility of blacklisting recipients.
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
Domain to be masqueraded
Typically a local machine alias
Domain to be masqueraded as
Typically the organisational unit’s top level domain
Domain users who are not to be masqueraded
These are normally system and administrative accounts which should remain exposed
This list of users should normally include the following:-
You will see this behaviour expressed commonly in many configurations a simply
Using the EXPOSED_USER_FILE avoids having to recompile the configuration every time a user is added to this list
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:-
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'`
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
Call the init script to restart sendmail on its new configuration
It is important to follow up any restart whether successful or otherwise with a quick look in the messages or maillog file
If things are still not working – increase the LOG_LEVEL definition and restart
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.
- LDAP configuration
- TLS certificates (and setting mandatory TLS)
- Anti-Virus and Anti-Spam
- Anything else with libmilter