Biz & IT —

Taking e-mail back, part 3: Fortifying your box against spammers

We load up OpenDKIM, SpamAssassin, ClamAV, and get Sieve filtering operational.

Taking e-mail back, part 3: Fortifying your box against spammers
Aurich Lawson

It's time for part 3 of getting your e-mail server up and running! We have six main tasks with this segment, each of which has its own chunk of subtasks. We'll go through them in roughly this order:

  • Install OpenDKIM so that we can use DomainKeys Identified Mail to help recipient domains validate that the e-mail we send actually comes from us
  • Install SpamAssassin for spam filtering
  • Install ClamAV for virus scanning (though this is somewhat optional, as we'll discuss when we get there)
  • Configure SpamAssassin and ClamAV and talk a bit about spam filtering
  • Configure our external DNS and talk a bit about how to handle internal DNS as well (as part of this, we'll set up DKIM and SPF records and also talk about reverse-lookups)
  • Set up some server-side mail filters with Sieve to toss spam and do some other neat tricks

After completing part 2, our e-mail server has an almost fully configured Postfix and Dovecot stack. However, there is additional functionality that we need to bolt on to Postfix in order to make it behave like a big-boy SMTP server. We need it to be able to filter out spam and viruses, and we need to be able to tag our outgoing e-mails with a cryptographic hash to help prove that they're legit e-mails that we're actually sending.

To add this functionality to Postfix, we're going to be installing a number of milters—mail filter applications. You might have noticed a commented-out line in Postfix's main.cf config file in part 2 that mentioned milters. Once we have them all installed, we'll uncomment that line to get them working.

The first thing we need to get operational is DKIM: DomainKeys Identified Mail. But before we do that, a quick note on virtual users: several readers have written in asking if the folder structure required for the virtual users we created back in part 2 needs to be manually created. The answer, fortunately, is no. The first time a virtual user receives mail, Dovecot will automatically create everything the virtual user needs under /var/mail/vmail/*.

Now, let's get to it!

OpenDKIM

OpenDKIM is one of the methods we'll use to help legitimize our mail server. When you send an e-mail with OpenDKIM, your mail server first generates a hash of the message's contents, then encrypts that hash with a private key and stores it in the e-mail's headers. On receipt of the e-mail, the recipient's mail server checks your domain's public DNS records for a specially constructed TXT record containing your private key's public counterpart; the recipient server then uses that public key to decrypt the encrypted message hash. Finally, the recipient server hashes the message and compares the value to the decrypted hash your server attached. If the two hashes are the same, then the recipient can have some degree of assurance that the message was indeed sent by an authorized sender from your domain.

We'll be using the OpenDKIM package to provide DKIM functionality to Postfix. It's a quick install with apt-get or aptitude:

aptitude install opendkim opendkim-tools

(I'm going to stop mentioning that most of this needs to be done with root privilege; either preface your commands with sudo or open a root shell via the method of your choice.)

The OpenDKIM package creates the accounts and Upstart jobs we need, but we're also going to create a directory to hold our OpenDKIM config files and private keys (hat tip to Drew Crawford and his excellent, in-depth tutorial for this bit). First, create the directory and set its ownership to the OpenDKIM user and group:

mkdir /etc/opendkim
chown opendkim:opendkim /etc/opendkim

Next, we'll chdir into the directory and create our DKIM private/public keypair there with one of the OpenDKIM utilities:

cd /etc/opendkim
opendkim-genkey -r -h sha256 -d mail.yourdomain.com -s mail

The command line parameters we're using with opendkim-genkey restrict the key we're generating so that it can only be used to sign mail, specify SHA256 as our hashing algorithm, and tell opendkim-genkey the hostname to use and the output file names to generate.

After you run this command, you'll have a pair of files in your directory: the private key in mail.private, and the public key in mail.txt. Note also that the public key file is actually structured as a DNS TXT record—this is super handy since we need to set just such a TXT record on our public DNS server. Keep this file around, and we'll come back to it shortly.

The next step is to rename the private key file from mail.private to just mail, and then we'll create a number of lookup tables so that OpenDKIM knows who's allowed to use the private key to sign its messages. Then we'll edit the OpenDKIM configuration file to get all our changes integrated.

First, that bit of renaming:

mv mail.private mail

Now create a file named /etc/opendkim/KeyTable and make its contents look like the following:

mail.yourdomain.com mail.yourdomain.com:mail:/etc/opendkim/mail

This table will be used to associate mail.yourdomain.com with the specific private key for that domain. You can also do this directly in the OpenDKIM configuration file, but using a table gives you the flexibility to manage multiple e-mail domains. If that's a future goal, you can just add the additional domains and keys below the first.

Next, create /etc/opendkim/SigningTable and set its contents thusly:

*@yourdomain.com mail.yourdomain.com

This table will tell OpenDKIM which signature to attach to which messages—in this case, sign all e-mails from all addresses ending in @yourdomain.com with the signature for mail.yourdomain.com. Again, the use of a table here lets you easily add additional domains if needed.

The last file to create is /etc/opendkim/TrustedHosts, which just needs to contain the standard link local IP address:

127.0.0.1

This table will serve a couple of different purposes: it will be used to tell OpenDKIM the list of servers for which it should sign mail rather than verify mail and also which servers it will allow to send signed mail without providing credentials. Once again, using a table here rather than hard-coding values directly into OpenDKIM's config file gives you room to add additional domains if needed.

That's it for our tables and key files. However, before we leave this directory and move on to OpenDKIM's main configuration file, we need to set their owners and groups so that the OpenDKIM user can access them (since they're probably owned by root or by your account right now):

chown -R opendkim:opendkim /etc/opendkim

The last OpenDKIM step is to modify the configuration file and plug in our tables, as well as set a few other options to make OpenDKIM do what we want. That file is at /etc/opendkim.conf, and it should already have a few uncommented parameters in it. Leave the file's existing contents alone and append the following at the bottom:

## OpenDKIM conf stuff that I'm adding
Canonicalization        relaxed/relaxed
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
LogWhy                  Yes
PidFile                 /var/run/opendkim/opendkim.pid
Socket                  local:/var/spool/postfix/opendkim/opendkim.sock
SyslogSuccess           Yes
TemporaryDirectory      /var/tmp
UserID                  opendkim:opendkim

From the top down, we're setting Canonicalization to relaxed to deal with the ways that e-mail headers occasionally get changed in transit. The next two entries tell OpenDKIM to use our TrustedHosts file (containing just the link local IP address) in the fashion described above—saying mail coming from addresses listed in that file should actually be signed and so on. KeyTable points to our KeyTable file, and SigningTable points to the SigningTable file. LogWhy tells OpenDKIM to be more verbose when explaining its signing and verification decisions. Next, we specify a pidfile for the OpenDKIM daemon and then a socket within Postfix's chroot jail in order to receive connections from Postfix. Finally, we tell OpenDKIM to log successful signatures and verifications, set our temp directory, and set the UID/GID under which OpenDKIM will run.

Now, about that socket: there isn't a /var/spool/postfix/opendkim directory yet, so we're going to create it and then set it to be owned by the OpenDKIM user and root group to match how the rest of the directories under /var/spool/postfix are permissioned:

mkdir /var/spool/postfix/opendkim
chown opendkim:root /var/spool/postfix/opendkim

Restart the OpenDKIM daemon to make our config changes live:

service opendkim restart

We face one final issue with OpenDKIM. Currently, the unix socket at /var/spool/postfix/opendkim/opendkim.sock won't allow public writes—only its owner and group can write to it. But we need Postfix to be able to write to the socket in order to communicate with OpenDKIM, and we don't have a lot of crazy granularity with Unix permission bits. The quickest way to overcome this problem is to add the Postfix user to the OpenDKIM group, like this:

usermod -G opendkim postfix

What are the potential security implications of this? An attacker compromising the Postfix account wouldn't have access to the OpenDKIM private key (since the permissions on that file only allow read and write by its owner, the OpenDKIM user), but anything that bridges account privilege separation barriers is a less-than-desirable thing. Still, this is a small workaround. One other way to handle this situation would be to abandon unix sockets and have OpenDKIM listen on a TCP port instead (and, indeed, that's what Drew Crawford's walkthrough suggests).

Channel Ars Technica