Nuts and Bolts OpenSMTPD Lead image: Lead Image © alphaspirit, 123RF.com
Lead Image © alphaspirit, 123RF.com
 

OpenSMTPD makes mail server configuration easy

Scrutinized

The OpenBSD origins of the OpenSMTPD mail transfer agent makes SMTP easier to implement and manage and more secure. By Tobias Eggendorfer

Setting up Sendmail or Postfix is no fun. The code for complex and historically evolved software such as Sendmail is virtually impossible to validate externally and comprehensively for accuracy and security. Even worse, the configuration file is a confusing juggernaut of many hundreds of lines, and each individual option can have various side effects on the behavior and the security of the mail server.

When looking around for a lean and secure alternative, administrators are quickly drawn to OpenSMTPD [1], the mail transfer agent (MTA) from the OpenBSD team. OpenBSD is known for its high security standards. The operating system has had only two remotely exploitable security vulnerabilities in the last two decades, mainly because of strict coding standards, code reviews, and data flow analyses to prevent errors – or at least detect them at an early stage.

OpenSMTPD's project goals [2] comply with the OpenBSD specifications: Security and reliability are at the top of the priority list. The community achieves this through defensive programming, privilege separation, and lean implementation (Figure 1).

Only rarely does a security vulnerability demand a new version of OpenSMTPD.
Figure 1: Only rarely does a security vulnerability demand a new version of OpenSMTPD.

Another factor is a configuration file that can be used to solve almost all mail server standard cases in fewer than 10 lines; even complex setups hardly need more effort.

Multiple Flavors

OpenSMTPD is at home not just on its native OpenBSD, but on Linux (with binary packages for Gentoo, Slackware, Debian, and Arch) and other BSDs, including Mac OS X. The package for Ubuntu served as the basis for the following discussion. To install the MTA, enter:

sudo apt-get install opensmtpd

OpenSMTPD also can be loaded onto the system in the classic way:

wget https://www.opensmtpd.org/archives/opensmtpd-portable-latest.tar.gz
tar -xzf opensmtpd-portable-latest.tar.gz

After some missing packages are installed using apt-get, enter

./configure
make
sudo make install

In a manual installation, the administrator needs to create the _smtpd and _smtpq users; otherwise, apt-script does this with slightly different names (opensmtpd, opensmtpq), each with a private user group.

Configuration

The configuration file ends up in an unusual location for OpenBSD (/etc/ rather than /etc/mail/) when installing via Apt. The default smtpd.conf is pleasantly surprising: Four lines of instructions are enough for the local configuration to receive and forward email from your computers.

If you want to access the mail server from the outside, you have several possible approaches. The simplest: listen on all accepts email liberally on all local IP addresses. However, anyone who, for example, wants to use their server while on the road via port 587, with SMTP authentication and TLS, and deal with all normal incoming email on port 25 should configure the server as in Listing 1.

Listing 1: Protected External Accessibility

01 pki post key '/etc/ssl/private/post.example.org'
02 pki mail key '/etc/ssl/private/mail.example.org'
03 table remote db:/etc/smtpd_remote.db
04 listen on 192.0.2.15 inet4 port 25 hostname post.example.org tls pki post
05 listen on 192.0.2.15 inet4 port 587 hostname mail.example.org tls-require pki mail auth <remote> tag remote
06 listen on 127.0.0.1 inet4 port 25 hostname local.example.org
07 limit mta inet4

Lines 1 and 2 set SSL keys for the server: one for authenticating remote users and the other for incoming email. Whether two keys are needed if a second server isn't planned is something you can consider, but the two instructions show how easy it is to include multiple keys. Lines 4 and 5 assign these keys to the respective connections (I use 192.0.2.15) using the short names post and mail.

Line 4 offers TLS as an option, whereas line 5 sets it as mandatory (tls-require). The individual ports use different hostnames. If you do not want this, you can leave out the hostname directive. OpenSMTPD then takes on the hostname of the running system. Line 5 also requires that users authenticate. The smtpd_remote.db file contains valid SMTP users generated from smptd_remote with:

makemap smtpd_remote

Each line in this file contains the username and the associated encrypted password separated by a tab. Password encryption is handled by the following:

smtpctl encrypt

Email submitted in this way can be identified later and systematically treated using the remote tag. Line 6 specifies that no TLS is required for mail coming from localhost.

OpenSMTPD supports IPv4 and IPv6; If you would rather restrict the server to IPv4, lines 4-6 do the trick per connection, whereas line 7 enforces IPv4 for the mta.

Passing on Email

Now that everything that needs to be configured has been configured, OpenSMTPD will accept email – and even do so in a construction that is complex by the program's own standards. OpenSMTPD now still has to learn how to process email. Again, you have several approaches available. The smtpd.conf line

accept for local alias <aliases> deliver to mbox

delivers the email for genuine local users of the Linux system. The aliases.db file has both an email address and the name of a local user or a recipient email address in the style of Sendmail aliases.

However, if you have several domains set up for individual email addresses, perhaps even with forwarding, you need to tweak some more controls:

accept from any for domain 'example.org' virtual <virtusers> deliver to mbox

Again, virtusers is designed pretty much like aliases. However, the resulting usernames do not really need to exist on the system; they can instead be resolved by further rules. Listing 2 shows an example: aliases resolves the postmasters if there is no other resolution. The last line shows how you can redirect any missing email addresses to an account. However, a catch-all account like this will attract spam, because spammers love guessing email addresses.

Listing 2: aliases

max.muster@example.org   max
max@example.org          max.muster@example.org
mm@example.org           max@example.org
moritz@example.org       moritz@example.net
catchall@example.org     postmaster
@example.org             catchall@example.org

Two steps are required to integrate the two user lists: First, you need to convert them to the faster db format as follows:

makemap -t aliases aliases
makemap -t aliases virtusers

Second, you need to integrate the list into smtpd.conf:

table aliases db:/etc/aliases.db
table virtusers db:/etc/virtusers.db

If you have many domains with the same email addresses, you can also store the domain lists in a table and even use wildcards. Listing 3 (domains) demonstrates this for example.org, example.net, example.com, and all their subdomains. Because you want the same email accounts to exist in all of these domains, the users file looks simpler:

max     max
moritz  moritz@example.com

Listing 3: domains

example.org
*.example.org
example.net
*.example.net
example.com
*.example.com

The first line ensures that OpenSMTPD forwards email addressed to max@*.example.* to local user Max, whereas Moritz's email all goes to moritz@example.com. The list can be transferred to users.db and then included in smtpd.conf using:

makemap -t aliases users
table users db:/etc/users.db
table domains '/etc/domains'
accept from any for domain <domains> virtual <users> deliver to mbox

The last line takes care of the delivery, saving effort for companies that have registered many domains appropriate to their own brands (so that domain grabbers cannot preempt them) yet want email to be accessible under all of these domains. Flexible wildcards like this are not envisaged in Sendmail.

In OpenSMTPD, wildcards work everywhere, although example.* should be treated with caution from a security perspective, taking into account the now countless number of top-level domains.

Mail Delivery

A line in smtpd.conf allows email to leave the separate MTA for moritz@example.com:

accept from local for any relay

From now on, OpenSMTPD relays email via the mail server responsible for the respective receiving domain. However, if you do not have a fixed IP address, you will probably not like this mechanism: Most email recipients use blacklists to block senders with dynamic IPs. The best case scenario is that the email ends up in the recipient's spam folder.

A mail server hosted by your provider can offer a solution – an SMTP smart host, as Sendmail calls it, which can also be accomplished easily using OpenSMTPD:

accept from local for any relay via secure+auth://provider@mail.provider.com:25 auth <authtable>

Placeholders can of course also be used here. Another table follows the same pattern for using SMTP Auth to authenticate against the server:

provider Username:Password

The provider is the placeholder in the accept rule. The Username and Password follow in plain text [3], which is unfortunate from a security perspective: Administrators should choose passwords that they do not use anywhere else for authenticating against the relay server. You will also want to design the configuration file rights so that as few people as necessary can read them.

On the LAN

If your mail server has to serve a large number of clients, you will typically want to release all local IPs for external sending. This can be accomplished either using tables, as before [3], or directly:

accept from source 10.0.0.0/24 for any relay

Relaying can also be varied on the basis of the sender address if multiple email addresses need to be managed via the local server. Placeholders can of course be used here. In Listing 4, @mail.com would thus send all email messages whose sender addresses contain mail.com via the mail.com server. However, you will want to restrict the source IP field to avoid creating a partially open relay.

Listing 4: Relaying with Multiple Addresses

accept from source 10.0.0.0/24 sender 'max.muster@web.com' for any relay via tls+auth://web@smtp.mail.com
accept from source 10.0.0.0/24 sender 'max.muster@gmail.com' for any relay via tls+auth://gmail@mail.google.com

A Modicum of Protection

OpenSMTPD aims to provide more security than its competitors. One action designed to achieve this is encrypting the email queue on the disk:

queue encryption key [Key]

OpenSSL generates the required 16-byte key with:

openssl rand -hex 16

Of course, a plain text key in a configuration file that can be read by root is not the ideal solution security-wise. Any attacker who has escalated their privileges to root can read it. However, the key makes random sniffing by curious users more difficult and provides protection if the file permissions are not set up correctly. The queue can also be compressed using

queue compression

which also adds to the level of security provided by encryption security – as long as the key is protected. Entering

max-message-size 20M

then helps with email that is too large.

Test, Test, Test

Once you've configured your new mail server to the best of your knowledge, it's time for some testing. One tried and trusted method is manually tracing the SMTP dialogs. Simulating the delivery of email from external sources where TLS is required proves to be tricky – even authentication is not exactly trivial. The following line solves the encryption problem:

openssl s_client -starttls smtp -connect mail.example.org:587 -crlf

You now just need to base64-encode the password and username; the SMTP Auth command supports several variants for this, but passing in as a parameter is probably the easiest way. Base64 encoding is done like this:

perl -MMIME:Base64 -e 'print encode_base64("\000Username\000Password");'

After the obligatory ehlo, you now just need AUTH PLAIN <Output>, where <Output> is the result of Base64 encoding. It should then be possible to send email from an external source.

Protection Against Viruses

OpenSMTPD scans the incoming or outgoing email for viruses and for spam, if so desired (e.g., using AMaVis – A Mail Virus Scanner [4]). Although the server does not have an extra email filter interface, that is not a big drawback: AMaVis listens to the mail server on a non-privileged port, accepts the email to be tested, and sends it back to OpenSMTPD again on a different localhost port. OpenSMTPD accepts the mail, tags it, and puts it back into the normal delivery queue with just a slight delay.

This process works just as well with ClamAV and ClamSMTP [5], the SMTP proxy for clamd. Compared with AMaVis, Clam has the advantage of being much more compact and is thus a better match in terms of the OpenSMTPD philosophy. Because the configuration is ultimately the same for AMaVis and clamsmtpd from the perspective of MTA, I will be using the lean open source scanner as an example.

If you want to scan incoming and outgoing email, you can run two ClamSMTP scanners to distinguish between the two streams again later. Otherwise, you could be letting an open relay creep in through the back door. The OpenSMTPD configuration then looks just like Listing 5: Untagged mail ends up with the virus scanner. The scanner then sends it back again via ports 30025 and 40025, depending on whether the mail was incoming or outgoing. The messages are tagged internally here, which leads to a special delivery.

Listing 5: Configuration with Antivirus

listen on all port 25
listen on 127.0.0.1 port 30025 tag scanned_out
listen on 127.0.0.1 port 40025 tag scanned_in
table aliases db:/etc/aliases.db
accept for local alias <aliases> deliver to mbox
accept tagged scanned_in for domain "example.com" virtual <users> deliver to mbox
accept tagged scanned_out for any relay
accept from any for domain "example.com" relay via smtp://127.0.0.1:10025
accept from source 10.0.0.0/24 for any relay via smtp://127.0.0.1:20025

Setting up ClamSMTPD

ClamSMTPD needs two configuration files to distinguish between incoming and outgoing email. The supplied configuration file in /etc/ is the Ubuntu sample configuration; all other ClamAV configuration files are in /etc/clamav. Anyone who has a problem with this can move clamsmtpd_out.conf and clamsmtpd_in.conf to the correct directory.

In contrast to some solutions [6], I prefer a Unix socket for local communication rather than a TCP port on localhost; Ubuntu sets this up inherently. The changes compared with the standard clamdsmtpd.conf are shown in Listings 6 and**7. The two commands

clamsmtpd -f /etc/clamav/clamsmtpd_in.conf
clamsmtpd -f /etc/clamav/clamsmtpd_out.conf

start the proxy. For testing, it is possible to send the EICAR test virus [7], which does no harm but must be detected.

Listing 6: clamsmtpd_out.conf

OutAddress: 30025
Listen: 127.0.0.1:20025
PidFile: /var/run/clamsmtp/clamsmtpd_out.pid

Listing 7: clamstmpd_in.conf

OutAddress: 40025
Listen: 127.0.0.1:10025
PidFile: /var/run/clamsmtp/clamsmtpd_in.pid

Header or Not?

Others suggest [4] including an additional SMTP header via the configuration file: X-AV-Checked: ClamAV using ClamSMTP. For testing purposes, this is fine, but a production environment – where attackers can easily discover that a virus scanner is running and even identify the scanner – rules this approach out, at least for outgoing email. Virus scanners can also be victims of attacks; just think of zip bombs. You might argue that concealing the scanner type is only security by obscurity; on the other hand, hiding a potential attack vector will certainly not detract from your overall security posture.

Anyone who is that paranoid will want to operate two email servers anyway: one that only accepts mail from external sources, the other scans. If you are still worried about a breach through the scanner, you can use a third computer to host the mailboxes. Email worth protecting can thus be isolated really well in IMAP mailboxes.

There is also a legal argument against the note header: A recipient could understand it as an indication that the email is safe and virus free and therefore stop exercising caution. However, all virus scanners are only as good as their last signature update (freshclam does this for ClamAV via a cronjob, by the way). There is always a risk that malware can slip through. Depending on the legal interpretation, a note header could mean liability.

Spam Filter

A configuration guide for SpamAssassin [8] is available for anyone wanting to filter spam as well as malware. Caution is advised here: Mail server experts regularly warn against just getting rid of spam. It is more sensible to move it into a spam folder where users can look at the suspect email, as required. No filters are perfect, and incorrectly filtered business email could cause exposure.

I take an even stricter legal view: Excessive filtering is a crime. It violates confidentiality of telecommunications legislation – if it exists in your locality – by suppressing correspondence. Others might think that local mail server users, if they agree to filtering, have given consent, thus ruling out any legal worries. However, the senders of email that are filtered out by a server cannot give their consent, and they are protected by the same legislation.

Conclusions and a Look at BSD

As this short guide has shown, OpenSMTPD is an email transfer agent that is both extremely simple to configure and sufficiently powerful. It appears to be much more clear-cut than the established players on the Linux platform. Those who do not have years of experience with Sendmail will achieve their objective much more quickly with OpenSMTPD.

OpenSMTPD users who also choose the OpenBSD operating system will benefit from spamd, which is tied to the firewall and thus not portable, to fight spam. Spamd is a smart and efficient gray-listing solution with a tarpit.