How to configure SPF in Postfix

SPF, or Sender Policy Framework, is a way for domain owners to say: “these are the email servers my domain sends mail from; anyone else is attempting to impersonate me.” These days, a lot of email services implement it in some way (both for receiving and sending email), and, if you administer your own server, it’s quite easy to implement, as we’ll see.

There are actually two parts to SPF: configuring it for your domain (so that your outgoing emails have a better chance of being accepted and emails impersonating your domain have a better chance of being refused), and configuring your email server to check whether incoming emails come from an SPF authorized server or not. Without further ado:

1- Configuring SPF for your domain’s outgoing mail:

This is actually done in DNS, not in the email server. Simply add a TXT record for your domain, with something like this:

v=spf1 a:mailserver1.domain.com a:mailserver2.domain.com -all

There are other configuration parameters, such as getting the servers from the domain’s MX record (add “mx“), and copying another domain’s authorized servers (“include:otherdomain.com“). And, of course, if you only have one server sending mail, you don’t need two “a:” entries.

Pay special attention to the “-all” part at the end: that instructs other servers to refuse any email apparently coming from your domain that isn’t authorized for it. That should ideally be the final configuration, but if you’re doing this for the first time you may want to change it to “~all“, which means “softfail”: it suggests to the other side that the mail is probably not legit, but that it shouldn’t refuse it outright because of it. After testing it for a while, if you see no problems then you can change it to “-all“.

2- Configuring SPF checking for incoming mail in Postfix:

I’ll assume you already have a working Postfix server (hopefully already using TLS encryption, hint hint), and that you want to respect any “-all” configurations (see above) in incoming mail — in other words, if a domain says “all my mail comes from server X, refuse anything else” and you receive an email purportedly from that domain, but from a server Y, it’s refused (for “~all“, it’d generate a warning).

First, install pypolicyd-spf. On a Debian-based system, the package (as of this writing) still has its old name, so enter the following:

apt install postfix-policyd-spf-python

Edit /etc/postfix/master.cf, and add:

# spf
policyd-spf  unix  -       n       n       -       0       spawn
        user=policyd-spf argv=/usr/bin/policyd-spf

Then edit /etc/postfix/main.cf, and look for the “smtpd_recipient_restrictions” section. That section will probably begin by accepting mail from your networks and from authenticated users, then rejecting non-authorized relaying, possibly followed by some white- or blacklists, and maybe a couple of filters, finally ending with “permit“. Before that “permit“, add:

    check_policy_service unix:private/policyd-spf,

(note the comma at the end; without it, Postfix wouldn’t read the rest of that section.)

IMPORTANT: while you’re still in testing, it may be a good idea to add “warn_if_reject” to the beginning of the previous configuration line. With it, Postfix will still log SPF failures, but won’t actually reject an email because of it. When you’re certain everything is fine, you can then remove that prefix (and then reload Postfix, of course).

Add also the following to that configuration file, outside of any section:

policy-spf_time_limit = 3600

And you’re done. Reload or restart Postfix, and check your logs to see if everything is working as intended.

Note that other tools, such as SpamAssassin, may also use SPF to weigh whether an incoming mail is more or less likely to be spam. That, however, is unrelated to the Postfix configuration above, which does a binary “block/don’t block”.

How to configure TLS encryption in Dovecot

DovecotAs with most other internet services, Dovecot can be configured to use TLS encryption — and, unlike some others (such as web servers or SMTP servers), there’s little reason not to enforce it.

Notes:

  • Like other recent TLS tutorials on this blog, this is, of course, not a full Dovecot guide — that would be far too complex for a blog post. Instead, it’s just focused on enabling/configuring TLS encryption. I’ll be assuming you already have Dovecot configured, and are able to access mailboxes on your server using an email client.
  • I’ll be focusing on IMAP only, not POP3 or anything else.
  • The server will be configured to require encryption, for both privacy and security reasons. I don’t know of any modern email client that doesn’t allow encrypted connections (and even then, you might work around it by configuring an encrypted tunnel, but that falls out of this post’s scope).

1. Getting a certificate

While I believe most modern email clients will support ECDSA certificates, such clients are not a known quantity like, say, web browsers are, so I suggest that you create an RSA certificate, unless you already have one whose Common Name (CN) matches your server’s public name (e.g. mail.domain.com). Dovecot versions 2.2.31 and newer support configuring alternative certificates so that you could support both kinds at the same time, but there’s probably little gain in doing that, since most clients will likely default to RSA anyway (and, unless you’re using Let’s Encrypt, certificates are not free).

So, put your certificate and private key in /etc/dovecot/. Let’s assume the certificate is called myserver-full.crt 1, and the private key is myserver.key. You should protect the private key from any users on your server, so do, for instance,

chown root:dovecot /etc/dovecot/myserver.key
chmod 640 /etc/dovecot/myserver.key

2. Configuring Dovecot for TLS

I’m assuming your Dovecot installation has a basic configuration file in /etc/dovecot/dovecot.conf, but the real “meat” of the configuration is in included files in /etc/dovecot/conf.d/. Your system may be slightly different, but I’m sure you can adapt. 🙂 Also, the numbers at the beginning of the file names may differ in your system.

This one isn’t really related to TLS, but it’s a good idea: edit /etc/dovecot/conf.d/20-imap.conf, and look for a line like this:

mail_plugins = $mail_plugins

and add to it (separated by a space):

imap_zlib

After all, there’s no reason not to use compression here, and bandwidth (especially on mobile) is still precious.

Now, edit /etc/dovecot/conf.d/10-ssl.conf, and add 2:

ssl = required
ssl_cert = </etc/dovecot/myserver-full.crt
ssl_key = </etc/dovecot/myserver.key
ssl_cipher_list = ECDHE-RSA-CHACHA20-POLY1305:ALL:!LOW:!SSLv2:!EXP:!aNULL
ssl_protocols = !SSLv2 !SSLv3
ssl_prefer_server_ciphers = yes

(The ssl_cipher_list line, besides setting secure defaults, sets the ChaCha20 protocol as the first one to be tried, since it’s considered one of the fastest and most secure. Note that it’ll require Dovecot linked to LibreSSL or OpenSSL 1.1.x to use that cipher, though Dovecot won’t complain if it doesn’t have access to it; it’ll just use the normal, secure defaults.)

If you don’t change anything else from the default configuration, the server will be listening on port 143 (IMAP) and port 993 (IMAPS). “But,” you ask, “isn’t standard IMAP unencrypted? I thought you were enforcing encryption…” Yes, but the standard IMAP port can be “upgraded” to TLS by entering the STARTTLS command, and email clients not only support that, but typically default to it (if not, just make sure you enable it). The server will refuse to authenticate any users on port 143 before they’ve “STARTTLSed”.

If you wanted to use only IMAP+STARTTLS, or only IMAPS, just edit /etc/dovecot/conf.d/10-master.conf,  look for the “service” configuration, and disable the one you don’t want. But I see no problem with keeping both enabled.

Thoughts? Questions?

(To do the same to SMTP / Postfix, please see How to configure TLS encryption in Postfix.)

How to configure TLS encryption in Postfix

Although Postfix (and the SMTP protocol in general) can function without any kind of encryption, enabling TLS it can be a good idea in terms of both security and privacy, so let’s look at how it can be easily done.

We’ll actually be configuring two separate types of encryption:

  • Opportunistic encryption for regular SMTP (port 25), both incoming 1 and outgoing 2. “Opportunistic”, here, means that the server will ask for encryption and use it if the other side also supports it, but if it doesn’t then it’ll work without encryption. Although forcing it might sound tempting for security reasons, in reality many “smaller” servers around the world still don’t support it, so you would be unable to send or receive mail to/from those. Opportunistic TLS at least means that most “big” email services (e.g. Gmail) will communicate with you (and you with them) with encryption.
  • Forced encryption for the submission service (port 587). This is used by your own users (even if it’s just you) to send mail through your server, and typically has different restrictions (e.g. it’s authenticated, but on the other hand it can be used to send mail to the outside (unlike incoming port 25 which doesn’t, as that would make it an open relay)). Both the users’ authentication that would otherwise be in clear text, and the fact that only a limited number of users (your users, too) will be accessing that port through email clients (never other email servers that you don’t control) make forcing encryption here a no-brainer.

NOTE: naturally, this is not an exhaustive Postfix tutorial. I’ll be assuming you already have a working server, and just want to add TLS encryption to it. Also, some parts may not apply to your case.

1. Have/get a TLS certificate for your email host’s public name

If you don’t already have one, see how to create a new TLS certificate. We’ll be using an RSA certificate here (instead of going with ECDSA) since unlike, say, a web server accessed by standard browsers, you can’t control what other email servers support 3, therefore it’s best to choose the most common option. 4

Note that the certificate’s CN must match your email host’s public name (e.g. “mail.domain.com“). That will also probably correspond to (one of) the domain’s MX records.

2. Postfix configuration

Again, I’ll be assuming your non-TLS Postfix is already working fine.

Put your certificate and key on /etc/postfix (for instance). For this example, I’ll be calling them myserver-full.crt and myserver.key.

In /etc/postfix/main.cf, add the following lines:

# TLS configuration starts here

tls_random_source = dev:/dev/urandom

# openssl_path=/usr/local/libressl/bin/openssl
# uncomment and edit the above if you're using a different "openssl" than the system's
# (in this case, LibreSSL)

# SMTP from your server to others
smtp_tls_key_file = /etc/postfix/myserver.key
smtp_tls_cert_file = /etc/postfix/myserver-full.crt
smtp_tls_CAfile = /etc/postfix/myserver-full.crt
smtp_tls_security_level = may
smtp_tls_note_starttls_offer = yes
smtp_tls_mandatory_protocols=!SSLv2,!SSLv3
smtp_tls_loglevel = 1
smtp_tls_session_cache_database =
    btree:/var/lib/postfix/smtp_tls_session_cache

# SMTP from other servers to yours
smtpd_tls_key_file = /etc/postfix/myserver.key
smtpd_tls_cert_file = /etc/postfix/myserver-full.crt
smtpd_tls_CAfile = /etc/postfix/myserver-full.crt
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database =
    btree:/var/lib/postfix/smtpd_tls_session_cache

# TLS configuration ends here

And in /etc/postfix/master.cf, assuming you already have a “submission” section a bit like this:

submission  inet  n     -       n       -       -       smtpd
    -o smtpd_etrn_restrictions=reject
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
    -o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
    -o smtpd_helo_restrictions=permit_mynetworks,permit

Add to that section:

  -o smtpd_tls_security_level=encrypt

The above forces encryption for the submission service (remember that it’s not required for normal SMTP, it’s just desired).

In short:

  • your server tries to connect to others using TLS, but falls back to an unencrypted connection if the other side doesn’t support encryption;
  • other servers can connect to yours using TLS, but if they don’t attempt it then the connection will be unencrypted;
  • your users *must* use encryption (and authentication) to send mail through your server.

Restart your server, and check the logs: you should be getting mentions of TLS now. For instance, an email server starting an encrypted connection to yours looks like:

Sep 6 14:25:58 drax postfix/smtpd[22727]: Anonymous TLS connection established from lists.openbsd.org[192.43.244.163]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)

Questions? Suggestions?

(And what about securing mailbox access? Enjoy: How to configure TLS encryption in Dovecot)