Build Your Email Server on Ubuntu Part 2: Setting up DKIM and SPF

In the 1st part of our build your own email server on ubuntu tutorial series, we have a basic Postfix email server up and running and can send plain text email, read incoming emails from the command line.

I tested that tutorial again on my Ubuntu 16.04 server and found that although I set the correct MX, A and PTR record, my emails were flagged as spam by Gmail and Outlook mail. So in part 2, we are going to look at how to ensure email delivery to recipient’s inbox by setting up SPF and DKIM records.

What are SPF and DKIM Records?

SPF and DKIM are two types of TXT records in DNS that can help prevent email spoofing and ensure legitimate emails are delivered into the recipient’s inbox instead of spam folder. If your domain is abused by email spoofing, then your emails are likely to landed in recipient’s spam folder if they didn’t add you in address book.

SPF (Sender Policy Framework) record specifies which hosts or IP addresses are allowed to send emails on behalf of a domain. You should allow only your own email server or your ISP’s server to send emails for your domain.

DKIM (DomainKeys Identified Mail) uses a private key to add a signature to emails sent from your domain. Receiving SMTP servers verify the signature by using the corresponding public key, which is published in your DNS manager.

Create an SPF Record in DNS

In your DNS management interface, create a new TXT record like below.

TXT  @   v=spf1 mx ~all

create spf record in DNS

Explanation:

  • TXT indicates this is a TXT record.
  • Enter @ in the name field.
  • v=spf1 indicates this is a SPF record and the SPF record version is SPF1.
  • mx means all hosts listed in the MX records are allowed to send emails for your domain and all other hosts are disallowed.
  • ~all indicates that emails from your domain should only come from hosts specified in the SPF record. Emails that are from other hosts will be flagged as forged.

Note that some DNS managers require you to wrap the SPF record with quotes like below.

TXT  @   "v=spf1 mx ~all"

To check if your SPF record is propagated to the public Internet, you can use the dig utility on your Linux machine like below:

dig your-domain.com txt

The txt option tells dig that we only want to query TXT records.

use dig utility to query spf record

You can also use online SPF validator such as spf.myisp.ch to see which hosts are allowed to send emails for your domain and debug your SPF record if any error occurs. The dmarcian SPF surveyor can help test your SPF record syntax.

Configuring SPF Policy Agent

We also need to tell our Postfix SMTP server to check for SPF record of incoming emails. This doesn’t help ensure outgoing email delivery but help with detecting forged incoming emails.

First install required packages:

sudo apt install postfix-policyd-spf-python

Then edit the Postfix master process configuration file.

sudo nano /etc/postfix/master.cf

Add the following lines at the end of the file.

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

Save and close the file. Next, edit Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Append the following lines at the end of the file. The first line specifies the Postfix policy agent timeout setting. The following lines will impose restriction on incoming emails by rejecting unauthorized email and checking SPF record.

policyd-spf_time_limit = 3600
smtpd_recipient_restrictions =
   reject_unauth_destination,
   check_policy_service unix:private/policyd-spf

Save and close the file. Then restart Postfix.

sudo service postfix restart

or

sudo systemctl restart postfix

Next time, when you receive an email from a domain that has an SPF record, you can see the SPF check results in the raw email header. The following header indicates a successful check against SPF.

Received-SPF: Pass (sender SPF authorized).

You can also send an email from your domain to Gmail or Microsoft Mail to see if SPF check can be passed. Note that setting an SPF record alone won’t ensure your email land in recipient’s inbox.

Setting up DKIM

First install OpenDKIM which is an open source implementation of the DKIM sender authentication system.

sudo apt install opendkim opendkim-tools

Then add postfix user to opendkim group.

sudo gpasswd -a postfix opendkim

Edit OpenDKIM main configuration file.

sudo nano /etc/opendkim.conf

Uncomment the following lines. Replace simple with relaxed/simple.

Canonicalization   simple
Mode               sv
SubDomains         no

Then add the following lines below #ADSPAction continue line. If your file doesn’t have #ADSPAction continue line, then just add them below SubDomains  no.

AutoRestart         yes
AutoRestartRate     10/1M
Background          yes
DNSTimeout          5
SignatureAlgorithm  rsa-sha256

Add the following lines at the end of this the file. (On Ubuntu 18.04, the UserID is already set to opendkim)

#OpenDKIM user
# Remember to add user postfix to group opendkim
UserID             opendkim

# Map domains in From addresses to keys used to sign messages
KeyTable           /etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# Hosts to ignore when verifying signatures
ExternalIgnoreList  /etc/opendkim/trusted.hosts
InternalHosts       /etc/opendkim/trusted.hosts

The final configuration file is as follows:

# This is a basic configuration that can easily be adapted to suit a standard
# installation. For more advanced options, see opendkim.conf(5) and/or
# /usr/share/doc/opendkim/examples/opendkim.conf.sample.

# Log to syslog
Syslog			yes
# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
UMask			002

# Sign for example.com with key in /etc/mail/dkim.key using
# selector '2007' (e.g. 2007._domainkey.example.com)
#Domain			example.com
#KeyFile		/etc/mail/dkim.key
#Selector		2007

# Commonly-used options; the commented-out versions show the defaults.
Canonicalization	relaxed/simple
Mode			sv
SubDomains		no
#ADSPAction             continue
AutoRestart     	yes
AutoRestartRate     	10/1M
Background      	yes
DNSTimeout      	5
SignatureAlgorithm  	rsa-sha256

# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others) between the signer
# and the verifier.  From is oversigned by default in the Debian pacakge
# because it is often the identity key used by reputation systems and thus
# somewhat security sensitive.
OversignHeaders		From

# List domains to use for RFC 6541 DKIM Authorized Third-Party Signatures
# (ATPS) (experimental)

#ATPSDomains		example.com

#OpenDKIM user
# Remember to add user postfix to group opendkim
UserID                 opendkim

# Map domains in From addresses to keys used to sign messages
KeyTable            /etc/opendkim/key.table
SigningTable        refile:/etc/opendkim/signing.table

# Hosts to ignore when verifying signatures
ExternalIgnoreList  /etc/opendkim/trusted.hosts
InternalHosts       /etc/opendkim/trusted.hosts

Save and close the file.

Create Signing table, key table and trusted hosts file

Create a directory structure for OpenDKIM

sudo mkdir /etc/opendkim

sudo mkdir /etc/opendkim/keys

Change owner from root to opendkim and make sure only opendkim user can read and write to the keys directory.

sudo chown -R opendkim:opendkim /etc/opendkim

sudo chmod go-rw /etc/opendkim/keys

Create the signing table.

sudo nano /etc/opendkim/signing.table

Add this line to the file. Replace your-domain.com with your real domain.

*@your-domain.com default._domainkey.your-domain

Then create the key table.

sudo nano /etc/opendkim/key.table

Add the following line. Replace your-domain with your actual domain.

default._domainkey.your-domain    your-domain.com:default:/etc/opendkim/keys/your-domain.com/default.private

Save and close the file.

Configure Trusted Hosts

Create the file.

sudo nano /etc/opendkim/trusted.hosts

Add the following lines to the newly created file.

127.0.0.1
localhost

*.your-domain.com

The above means that messages coming from the above IP addresses and domains will be trusted and signed.

Generate Private/Public Keypair

Since DKIM is used to sign outgoing messages and verify incoming messages, we need to generate a private key for signing and a public key for remote verifier. Public key will be published in DNS.

Create a separate folder for the domain.

sudo mkdir /etc/opendkim/keys/your-domain.com

Generate keys using opendkim-genkey tool.

sudo opendkim-genkey -b 2048 -d your-domain.com -D /etc/opendkim/keys/your-domain.com -s default -v

The above command will create 2048 bits keys. -d (domain) specifies the domain. -D (directory) specifies the directory where the keys will be stored and we use default as the selector (-s), also known as the name. Once the command is executed, the private key will be default.private and default.txt will be the TXT record that contains public key.

Make opendkim as the owner of the private key.

sudo chown opendkim:opendkim /etc/opendkim/keys/your-domain.com/default.private

Add Public Key in DNS Records

Display the public key

sudo cat /etc/opendkim/keys/your-domain.com/default.txt

The string after the p parameter is the public key.

add dkim record

In you DNS manager, create a TXT record, enter default._domainkey in the name field. Then copy everything in the parentheses and paste it into the value field. Delete all double quotes and white spaces. If you don’t delete them, then the key test in the next step will fail.

dkim record

Test your configuration

Enter the following command on Ubuntu 16.04 server to test your key.

sudo opendkim-testkey -d your-domain.com -s default -vvv

If everything is OK, you will see

key OK

Connect Postfix to OpenDKIM

Postfix can talk to OpenDKIM via a Unix socket file. The default socket file used by OpenDKIM is /var/run/opendkim/opendkim.sock. But the postfix SMTP daemon shipped with Ubuntu runs in a chroot jail, which means that the SMTP daemon resolves all filenames relative to the Postfix queue directory (/var/spool/postfix). So we need to change the socket file.

Create a directory to hold the OpenDKIM socket file and only allow opendkim user and postfix group to access it.

sudo mkdir /var/spool/postfix/opendkim

sudo chown opendkim:postfix /var/spool/postfix/opendkim

Then edit the socket configuration file.

sudo nano /etc/default/opendkim

Find the following line:

SOCKET="local:/var/run/opendkim/opendkim.sock"

Replace it with:

SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"

opendkim socket

Save and close the file. Note: On Ubuntu 18.04, the opendkim systemd service doesn’t use /etc/default/opendkim file. You need to change the socket file in /etc/opendkim.conf file.

sudo nano /etc/opendkim.conf

Find the following line:

Socket                  local:/var/run/opendkim/opendkim.sock

Replace it with:

Socket                  local:/var/spool/postfix/opendkim/opendkim.sock

Next, we need to edit Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Add the following lines after smtpd_recipient_restriction section.

# Milter configuration
# OpenDKIM
milter_default_action = accept
milter_protocol = 2
smtpd_milters = local:/opendkim/opendkim.sock
non_smtpd_milters = local:/opendkim/opendkim.sock

Save and close the file. Then restart opendkim and postfix service.

sudo service opendkim restart

sudo service postfix restart

or

sudo systemctl restart opendkim

sudo systemctl restart postfix

DKIM Check

You can send a test email to [email protected] and get a free email authentication report.

echo "test email" | sendmail [email protected]

Here’s the report I got from port25.com

==========================================================
Summary of Results
==========================================================
SPF check:           pass
"iprev" check:       pass
DKIM check:          pass
SpamAssassin check:  ham

You can see that my email passed both SPF and DKIM check. iprev check is used to see if the reverse (IP to hostname) and forward (hostname to IP) lookup results were returned and were in agreement. ham, which means the email is not spam, is a terminology used by Apache SpamAssassin.

You can also send a test email to your Gmail account to see if DKIM test is passed.

echo "test email" | sendmail your-account@gmail.com

If you click the show original button in Gmail, you should see

dkim=pass

in the authentication-results header.

In my test, the email landed in my Gmail inbox. However, it’s stilled labeled as spam in my outlook.com email although both SPF and DKIM are passed. If your message is not signed and DKIM check failed, you may want to check postfix log (/var/log/mail.log) to see what’s wrong in your configuration.

In part 3, we will be looking at how to create DMARC record. As always, if you found this post useful, please subscribe to our free newsletter or follow us on Google+, Twitter or like our Facebook page

Rate this tutorial
[Total: 39 Average: 4.5]

16 Responses to “Build Your Email Server on Ubuntu Part 2: Setting up DKIM and SPF

  • Could you please write about how to forward only email setup with postfix or any other email server.

    • Xiao Guo-An (Admin)
      2 years ago

      You mean you want your email server to forward every incoming email to another email service like Gmail? After Postfix is installed, you simply need to create a .forward file in your home dirctory and add the destination address where the email will be forwarded.

    • Emran
      2 years ago

      Nice, Please add it on a section of this article 🙂

      Thank you

    • Xiao Guo-An (Admin)
      2 years ago

      I think it will be more appropriate to add this tip in the first part of this tutorial series and I added it in the bonus tip section of this article.

    • Emran
      2 years ago

      Yes, It will be great 🙂

  • Natasha Stellar
    2 years ago

    Thanks for the guide!
    I have a forwarding/relay address and I’ve been trying to set up postsrsd, but for the life of me it still fails DKIM checks! Wondering if this series can be expanded to provide Sender Rewriting Scheme (SRS)?

    eg:
    mydomain.com -> gmail.com DKIM PASS
    [email protected] -> [email protected] -> [email protected] DKIM FAIL

    Cheers

  • this tutorial rocks! Got DKIM to work..finally!! …Thanks

  • Ajay Krishna Teja Kavuri
    1 month ago

    Great tutorial cheers!! I have followed all the instructions to setup opendkim on aws ubuntu 14.04. Now, I am using Route 53 for DNS records management. I got KEY OK as described in the tutorial. But I have my mail server pointing to mail.mydomain.com and I don’t see dkim=pass when I test my installation. What should I change to make it work for subdomains?

  • You might want to suggest to add to opendkm.conf
    LogWhy yes
    So it will print what the problem is. I spent several hours figuring out permissions issues.

  • wak jek
    1 month ago

    hello, cannot start/restart dkim
    I got this error:
    opendkim[9027]: opendkim: /etc/opendkim.conf: /etc/opendkim/key.table SigningTable refile:/etc/opendkim/signing.t
    able: dkimf_db_open(): Unknown database type

    please help. thank you

  • Anne-Catherine AYE
    1 month ago

    I have a problem with the file sudo nano /etc/default/opendkim and i can’t check the DKIM
    This post saved me
    https://serverfault.com/a/847442/438721

  • Joseph Alvini
    1 month ago

    OK So I have a question. I followed everything step by step and even double and triple checked everything but every time I go to sudo opendkim-testkey -d your-domain.com -s default -vvv it tells me that ‘default._domainkey.MYDOMAIN.com’ record not found… Is there a reason its doing this and if so is there a way that I can uninstall and reinstall DKIM so that I can start over?

  • very Nice tutorial. Everything works fine for me but can I use the smtp variable on a mailer. Like gammadyne mailer

  • OpenDKIM Filter: Unable to bind to port local:/var/run/postfix/opendkim/opendkim.sock: No such file or directory

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • * Some of my previous answers are lost after I uninstalled Disqus comment system from my website. I try to recover those answers whenever I can.