DomainKeys Identified Mail (DKIM)

 Published on 27 Feb 2025 .  Filed in Projects .  1134 words

Email sender spoofing involves pretending to control another person's email address. Scammers often send emails with a sender address like someone@paypal.com and hope that the recipient will fall for it and trust them. In fact, SMTP doesn't care what sender address you send from. Many email service providers require you to send emails only with your own email address. But some don't.

To avoid this problem, a new method was conceived that add a cryptographic signature to the header of an email, which the recipient can check to verify the authenticity of the sender and the integrity of the email.

The signature is created using a private key that only the sending mail server has. It can then be verified by the recipient by downloading the corresponding public key from the DNS zone of the sending domain and running a signature check. This works very similarly to PGP or S/MIME signing, but at the domain level. Also your mail server automatically sign all outgoing emails.

Configure 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

By default, OpenDKIM logs will be saved in /var/log/mail.log file. Add the following line so OpenDKIM will generate more detailed logs for debugging:

Syslog               yes
Logwhy               yes

Then, find the following lines. Uncomment them and 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

Next, add the following lines at the end of this file:

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

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

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

# A set of internal hosts whose mail should be signed
InternalHosts       /etc/opendkim/trusted.hosts

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 the 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 the following two lines to the file. This tells OpenDKIM that if a sender on your server is using a @example.com address, then it should be signed with the private key identified by default._domainkey.example1.com. The second line tells that your sub-domains will be signed by the private key as well.

*@example.com    default._domainkey.example.com
*@*.example.com    default._domainkey.example.com

Then create the key table:

  sudo nano /etc/opendkim/key.table

Add the following line, which tells the location of the private key:

default._domainkey.example1.com     example1.com:default:/etc/opendkim/keys/example1.com/default.private

Next, create the trusted hosts file:

  sudo nano /etc/opendkim/trusted.hosts

Add the following lines to the newly created file. This tells OpenDKIM that if an email is coming from localhost or from the same domain, then OpenDKIM should only sign the email but not perform DKIM verification on the email:

127.0.0.1
localhost

.example1.com

CAREFUL: You should not add an asterisk in the domain name like this: =.example1.com=. There should be only a dot before the domain name.*

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/example1.com

Generate keys using the opendkim-genkey tool, this will store the private key in a default.private file and the public key in the default.txt file:

  sudo opendkim-genkey -b 2048 -d example1.com -D /etc/opendkim/keys/example1.com -s default -v
  • -b: Create 2048 bits keys.
  • -d; Specifies the domain.
  • -D: Specifies the directory where the keys will be stored.
  • -s: Name of the selector.

Make opendkim as the owner of the private key:

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

And change the permission, so only the opendkim user has read and write access to the file:

  sudo chmod 600 /etc/opendkim/keys/your-domain.com/default.private

Publish Your Public Key in DNS Record

Display the public key:

  sudo cat /etc/opendkim/keys/example1.com/default.txt

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

Test DKIM Key

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

  sudo opendkim-testkey -d example1.com -s default -vvv

If everything is OK, you will see Key OK in the command output:

opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key 'default._domainkey.example.com'
opendkim-testkey: key secure
opendkim-testkey: key OK

Your DKIM record may need sometime to propagate to the Internet. Depending on the domain registrar you use, your DNS record might be propagated instantly, or it might take up to 24 hours to propagate. You can also test on some online 1 sites to check if the DKIM record is propagated by entering the selector name (in our case default).

If you see Key not secure in the command output, this is because DNSSEC isn’t enabled on your domain name. DNSSEC is a security standard for secure DNS query. Most domain names haven’t enabled DNSSEC.

If you see the query timed out error, you need to comment out the following line in /etc/opendkim.conf file and restart opendkim.service:

TrustAnchorFile       /usr/share/dns/root.key

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, as shown in /etc/opendkim.conf file. But the postfix SMTP daemon runs in a chroot jail, which means the SMTP daemon resolves all filenames relative to the Postfix directory (/var/spool/postfix). So we need to change the OpenDKIM Unix socket file.

Create a directory to hold the OpenDKIM socket file and allow only 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 OpenDKIM main configuration file:

  sudo nano /etc/opendkim.conf

And replace the Socket local:/run/opendkim/opendkim.sock with (or add) the following line:

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

If you can find any of the following line

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

Change it to:

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

Now we need to edit the Postfix main configuration file:

  sudo nano /etc/postfix/main.cf

Add the following lines at the end of this file, so Postfix can call OpenDKIM via the milter protocol:

# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

Finally restart opendkim and postfix service:

  sudo systemctl restart opendkim postfix

Footnotes