TL;DR: check mail-tester.com or Spam Checker. If you don't get 10/10, read this post.
In a previous post, we saw how to configure a Debian mail server with Postfix and Dovecot, but we did not address an important issue: spam. Major e-mail providers are defending their users against spam by requiring two extra credentials: Sender Policy Framework (SPF) and DomainKey Identified Mail (DKIM). We will see how to configure these two services. We will also configure a local defense against incoming spam with SpamAssassin.
Configuring SPF¶
Sender Policy Framework (SPF) provides e-mail envelope authenticity, that is, it helps verify that the sender address written in an e-mail's headers originates from a valid IP for its domain. It is one of the two services you should configure in order not to be considered as a spammer by major e-mail service providers. Its installation starts with:
sudo apt-get install postfix-policyd-spf-python
To enable the policy service in Postfix, append the following to
/etc/postfix/master.cf
:
policy-spf unix - n n - - spawn
user=nobody argv=/usr/bin/policyd-spf
Then, add the instruction check_policy_service unix:private/policy-spf
to your smtpd_recipient_restrictions
in /etc/postfix/main.cf
. A
complete list of recipient restrictions, including SPF, looks like:
smtpd_recipient_restrictions =
permit_sasl_authenticated,
reject_invalid_hostname,
reject_unknown_recipient_domain,
reject_unauth_destination,
reject_rbl_client zen.spamhaus.org=127.0.0.[2..11]
reject_rhsbl_sender dbl.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_helo dbl.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_reverse_client dbl.spamhaus.org=127.0.1.[2..99]
warn_if_reject reject_rbl_client zen.spamhaus.org=127.255.255.[1..255]
check_policy_service unix:private/policy-spf,
check_sender_access hash:/etc/postfix/sender_access,
check_recipient_access hash:/etc/postfix/recipient_access,
permit
Make sure the following files all exist:
/etc/postfix/recipient_access
/etc/postfix/recipient_access.db
/etc/postfix/sender_access
/etc/postfix/sender_access.db
If not, you can create them by:
echo "address.to.reject@spam.com REJECT Sorry, you cannot write to this address."
echo "mydomain.com OK" > /etc/postfix/sender_access
postmap /etc/postfix/recipient_access
postmap /etc/postfix/sender_access
A few rules apply: the policy service should always be after
reject_unauth_destination
, otherwise early responses from SPF can turn
your system into an open relay. Also, put the policy service after you permit
local senders (permit_sasl_authenticated
), as SPF should be applied to
inbound e-mail from the Internet, not outbound e-mail from your users.
You should also add the following line to main.cf
in order to avoid
timeouts:
policy-spf_time_limit = 3600s
Finally, you will need to setup an SPF record in your DNS. It is basically a TXT record that looks as follows:
600 IN TXT "v=spf1 a mx ip4:YOUR_IPv4_ADDRESS ip6:YOUR_IPv6_ADDRESS ~all"
Adding your IP addresses is not mandatory but, as we will see later on, GMail
will give you a softfail
score if it cannot find these.
Configuring DKIM¶
DomainKeys Identified Mail (DKIM) is the other service you need to configure in order not to be considered as a spammer by big e-mail providers. It ties your e-mail server to your domain name, and allows receivers to verify the authenticity of the body of e-mails originating from your domain (SPF checks headers, DKIM checks bodies). Start the installation with:
apt-get install opendkim opendkim-tools
DKIM is based on asymmetric cryptography. Basically, we will generate a pair of
public/private keys on your server, and publish the public key on your DNS
records. First, edit /etc/opendkim.conf
and make sure it contains the
following:
KeyTable /etc/opendkim/KeyTable
SigningTable /etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts /etc/opendkim/TrustedHosts
LogWhy yes
Create the directory /etc/opendkim
if it does not exist. Then, enter
all your domains, hostnames or IP addresses in
/etc/opendkim/TrustedHosts
:
127.0.0.1
localhost
mydomain.com
Now, edit /etc/default/opendkim
and uncomment the following line:
SOCKET="inet:12345@localhost" # listen on loopback on port 12345
Make sure to comment all other lines that define the variable SOCKET
.
Alternatively, you can edit this field in /etc/opendkim.conf
(but then
make sure to comment out all SOCKET
lines in
/etc/default/opendkim
as they have higher priority):
Socket inet:12345@localhost
Connect this to Postfix by appending the following block to
/etc/postfix/main.cf
:
# DKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:12345
non_smtpd_milters = inet:localhost:12345
Now, we need to generate the pair of public/private keys for your server. It goes as follows:
mkdir -p /etc/opendkim/keys/mydomain.com
cd /etc/opendkim/keys/mydomain.com
opendkim-genkey -s default -d mydomain.com
chown opendkim:opendkim default.private
Next, add the key to /etc/opendkim/KeyTable
:
default._domainkey.mydomain.com mydomain.com:default:/etc/opendkim/keys/mydomain.com/default.private
And to /etc/opendkim/SigningTable
:
mydomain.com default._domainkey.mydomain.com
Note: in some tutorials, you will see a regular expression
*@mydomain.com
instead of just mydomain.com
at the beginning of
this line. If you want to enable the parsing of regular expressions, add the
prefix "refile" (stands for "regular expression file") to the signing-table
line in /etc/opendkim.conf
(thanks to Tadashi for pointing this out):
SigningTable refile:/etc/opendkim/SigningTable
Finally, display your DNS key with:
$ cat /etc/opendkim/keys/mydomain.com/default.txt
default._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfM...long hash...DAQAB"
You need to enter the full line as a free text (TXT) record of your domain's DNS (how to do so depends on your provider). If you cannot edit your DNS record directly but instead need to go through a GUI, just remember that the field name starts with "default._domainkey". Once you have configured your DNS, you can check it with:
dig default._domainkey.mydomain.com TXT
The output should contain an "ANSWER" section with the same content as your
default.txt
file.
Testing your credentials¶
There are a few testing services available (as of writing this note in 2015)
to check the spammyness of your e-mails. A simple one is provided by Port 25:
send an empty e-mail to check-auth@verifier.port25.com
and you will
get an SPF/DKIM report by return mail that looks like:
SPF check: pass
DomainKeys check: neutral
DKIM check: pass
DKIM check: pass
Sender-ID check: pass
SpamAssassin check: ham
(Don't worry about the DomainKeys check, which is for a version of the protocol anterior to DKIM.) The second, and in my opinion more useful services, are mail-tester.com and Spam Checker. They perform similar checks, and also give you hints on how to improve your configuration. Thanks to the former, I could identify and fix the DKIM mis-configuration mentioned above, and learned about the following two improvements. The latter does not check for black lists, but instead verifies that your e-mails make it to Gmail inboxes.
Configuring DMARC¶
Once both SPF and DKIM are configured, it is easy, yet appreciated by other e-mail providers, to add a DMARC record to your DNS. Just create a new TXT field with the default values:
_dmarc IN TXT "v=DMARC1; p=none"
While SPF and DKIM are authentication schemes, used to verify e-mail headers
and bodies respectively, DMARC is a scheme management scheme that tells
e-mail clients what to do when e-mails don't pass authentication. Here we are
setting enforcement to p=none
, meaning nothing happens to e-mails that
don't pass authentication.
Once DKIM and SPF are working properly on your server, you can reject
unauthenticated messages by setting p=reject
. This is not mandatory but
it helps fight spoofing and phishing. To go further, check out for instance the
Recommended DMARC rollout
tutorial from the Google Workspace Admin Help.
Reverse DNS¶
Some major e-mail providers check the domain name in your e-mails by a reverse
DNS lookup. Consequently, you should make sure that your reverse DNS
(dig +short -x <your_IP_address>
) returns the domain name used in your
e-mails. If it is not the case, you will need to configure a pointer (PTR)
record in your DNS. This option was available in the web interface of my
registrar at the time of writing this note, but some registrars may require you
to contact customer support to do this.
The GMail test¶
GMail is probably the most thorough spam fighter
among public e-mail providers in 2015. If you have a GMail account (you can
just create an empty one for the task), you can send it a mail and look for
the Authentication-Results
field in the source code of the received
message. On my first trial, it looked like:
Authentication-Results: mx.google.com;
spf=softfail (google.com: domain of transitioning user@mydomain.com
does not designate IP_ADDRESS_OF_THE_SERVER as permitted sender)
smtp.mailfrom=user@mydomain.com;
dkim=pass header.i=@mydomain.com;
dkim=pass header.i=@mydomain.com;
dmarc=pass (p=NONE dis=NONE) header.from=mydomain.com
We see that GMail indeed checks for SPF, DKIM and DMARC. Here, it noticed
something that both previous tests had missed: my SPF record did not include my
server's IP address, which it considered suspicious ("domain of transitioning
email_address does not designate ip_address as permitted sender"). After
adding my IP to the SPF record, the softfail
became a pass
.
Configuring SpamAssassin¶
Sooner rather than later, your e-mail server will receive spam. Good e-mail clients have junk filters, but the fight against it starts at the server level. SpamAssassin is a renowned e-mail filter that does this job; plus, it is easy to install and configure. Start the setup with:
apt-get install spamassassin spamc
Let us first configure Postfix's master.cf
: we need to add the filter
-o content_filter=spamassassin
to the smtp
, smtps
and
submission
services:
smtp inet n - - - - smtpd
-o content_filter=spamassassin
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=$mua_client_restrictions
-o smtpd_helo_restrictions=$mua_helo_restrictions
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
-o content_filter=spamassassin
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=$mua_client_restrictions
-o smtpd_helo_restrictions=$mua_helo_restrictions
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
-o content_filter=spamassassin
Then, add the following at the end of the file:
spamassassin unix - n n - - pipe
user=debian-spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
Finally, you can uncomment the following line in
/etc/spamassassin/local.cf
to label spam mails:
rewrite_header Subject *****SPAM*****
Webography¶
At the time of writing this post, I learned from articles of the Debian Wiki and the Ubuntu Community Wiki, as well as from the following tutorials:
- How To Install and Configure DKIM with Postfix on Debian Wheezy
- Installing OpenDKIM in Debian Squeeze/Wheezy
- So You'd Like to Send Some Email (Through Code)
The random thought Why don't we do email verification in reverse? gives a great overview of the way e-mail works, including how DKIM, SPF and DMARC implement a verification process.
Discussion ¶
Feel free to post a comment by e-mail using the form below. Your e-mail address will not be disclosed.