Authenticating Your Mailserver With SPF and DKIM
| ~8 minute read
I recently did a video on setting up an email server, with Postfix for SMTP and Dovecot for IMAP, which you can check out here. Now this much isn't enough since you want to make sure no one can spoof your domain and/or tamper with the content of your E-mails. This is where SPF (Sender Policy Framework) and DKIM (DomainKeys Identified Mail) come to the rescue.
What are SPF and DKIM?
SPF is used to limit which servers are allowed to send emails from your domain. It protects your domain against domain spoofing. Even if you don't use E-mail on your domain, I recommend using SPF to specify that you won't be sending any emails from this particular domain, so no one can spoof your domain and pretend to be you while sending emails.
DKIM can be used to sign the emails with a key pair (where the public key is added to your domain's TXT records) so your E-mails' contents cannot be tampered with. OpenDKIM is an open source implementation of DKIM, we will be setting up OpenDKIM in this tutorial (because I have a natural urge to use anything which has "open" in its name)
Setting up SPF for outgoing emails on your domain
You need to add a TXT record to your domain containing the SPF syntax. the SPF record should always start with the SPF version number you are using.
For me it is v=spf1
. Then, you can add all the SPF "mechanisms" that need to be evaluated in order to identify the E-mail.
Most of the time you just need one or two mechanisms, which are mx
or mx:your-domain.tld
and ip4:your_servers_ipv4
and ip6:your_servers_ipv6
(you don't need all of them, it's kinda overkill)
mx
means the email is legit if it comes from a server which is in the MX Records of this domainmx:your-domain.tld
means the email is legit if it comes from a server which is in the MX Records of your-domain.tldip4:ipv4_address
means the email is legit if the sending server has this particular IPv4 addressip6:ipv6_address
means the email is legit if the sending server has this particular IPv6 address
You can prepend any of these with -
to reject emails which meet this condition (by default it accepts the ones passing this condition); You can use -all
to make sure
all the other unauthorised emails don't pass the SPF test.
So this is what a valid SPF record would look like:
v=spf1 mx -all
Now I did say using all of these at ones is pretty overkill, but here is what mine looks like:
v=spf1 mx ip4:172.105.59.60 ip6:2400:8904::f03c:93ff:feac:ca67 mx:m.vidhukant.xyz -all
I don't think using many mechanisms is a bad thing, so you can just copy mine and replace the IP4 and IP6 addresses with your server's IP4 and IP6 addresses. Which is enough for most usecases. To apply this SPF record just add a TXT record to your root domain. It should look like this:
NOTE: If you are using multiple domains for a single mail server, you should use the exact same SPF record for all the domains.
If you don't plan to use E-mail with any of your domains, set the SPF record to v=spf1 -all
.
ALWAYS add this to your root domain since it tells every receiving server that this domain doesn't
send E-mails and if you receive one that means it's probably malicious.
This website has all of the other info about structuring an SPF record.
Setting up DKIM (OpenDKIM)
Install the necessary packages
apt install opendkim opendkim-tools
Configure OpenDKIM
The OpenDKIM config file located at /etc/opendkim.conf
should look like this:
Syslog yes
SyslogSuccess yes
Canonicalization relaxed/simple
Mode sv
SubDomains no
OversignHeaders From
Socket local:/var/spool/postfix/opendkim/opendkim.sock
PidFile /var/run/opendkim/opendkim.pid
AutoRestart yes
AutoRestartRate 10/1M
Background yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
UserID opendkim
UMask 002
KeyTable /etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
ExternalIgnoreList /etc/opendkim/trusted.hosts
InternalHosts /etc/opendkim/trusted.hosts
TrustAnchorFile /usr/share/dns/root.key
This is what works for me on Debian 11. The data files for OpenDKIM will be stored in /etc/opendkim/
. If you want to store them anywhere else,
edit the KeyTable
, SigningTable
, ExternalIgnoreList
, InternalHosts
parameters in /etc/opendkim.conf
Setting up the Signing Table
Make a list of all the domains you want to handle E-mails for, and add them to /etc/opendkim/signing.table
like this:
*@example.tld mail._domainkey.example.tld
Replace both occurances of example.tld to your domain name, and mail to whichever subdomain sends emails. mail._domainkey.example.tld
would act
as the DKIM selector for example.tld
If you have any other subdomain set up for the mail server (for example I have m.vidhukant.xyz
while the standard would be mail.vidhukant.xyz
),
replace "mail" with that.
You can add as many domains you want, But don't add domains which won't be sending emails.
Setting up the Key Table
Now edit /etc/opendkim/key.table
and add lines for each domain like this:
mail._domainkey.example.tld example.tld:mail:/etc/opendkim/keys/example.private
Add one line like this for all the domains added to the signing.table file, replace mail
and example.tld
respectively.
Decide a short name for the private key for this domain, /etc/opendkim/keys/example.private
in this example holds the private key for example.tld
You should decide different names for each domain you're adding and remember that name, you will need that later.
Setting up the Trusted Hosts
Edit the /etc/opendkim/trusted.hosts
file and add these contents:
127.0.0.1
::1
localhost
hostname
hostname.example.tld
*.example.tld
Replace hostname with your server's hostname (defined in /etc/hostname
), and example.tld with your domain. If you have more than one domain,
you can append those to the file prepended with a *.
, just like in *.example.tld
.
No need to add another entry for hostname.example.tld
, just append *.yourdomain.tld
to the file as many times (for as many domains) you want.
Set file permissions
Make sure opendkim user has access to the required files/directories:
chmod u=rw,go=r /etc/opendkim.conf
chown -R opendkim:opendkim /etc/opendkim
chmod -R go-rwx /etc/opendkim/keys
Generating the keys
Run this command, replacing example.tld with your domain name, and mail with the subdomain for email:
opendkim-genkey -b 2048 -h rsa-sha256 -r -s mail -d example.tld -v
mv /etc/opendkim/mail.private /etc/opendkim/keys/example.private
mv /etc/opendkim/mail.txt /etc/opendkim/keys/example.txt
Replace "example" in the example.private
and example.txt
files with the short name you entered for the domain in the key.table
file.
Repeat this step for all the domains if you have multiple of them!
NOTE: Never share the contents of the generated ".private" files!
Start OpenDKIM
systemctl restart opendkim
Add the TXT Records
Read the contents of the TXT file generated by opendkim-genkey
:
cat /etc/opendkim/keys/example.txt
The output would be something like this:
mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxzNTZydyFiNljq/Md1cXNEqemKDk9CKhZGHSzEH6x0zxtdcv5ROzaytJ4OsatDOdk+Pygkj6Qq9PiLCc3HlWPTcvMEs+M8YvRergTATFNoAmXLXvpbi+DD0oXAsbz2dM/klObY9OSNlJqFpzmGjgRbtSnvCbot8Smg5LreCjmkuHo/sxyynRHGwRHUM6jokm2YGIGATZBIVqtS"
"jM418Gtxx9MZUbwcQTlchk1hSQgbXlAAl5tagle3bq/2GwrwrdaghRH750qLjnBQhzdFnH+GjHTmRl2drQ/2zG1L0GlufipZ1UkWulidox2RtIykv2VxDlBYb77G4PAiiJsSar+wIDAQAB" ) ; ----- DKIM key mail for example.tld
Carefully delete everything outside the parenthesis (including the parenthesis) and join all three lines into one. Now, remove all the quotes and also the whitespaces between line 2 and 3, so both lines get merged into one, without any space between them.
Update (04 May 2024): Use this command to easily turn this output into a valid txt record:
cut -d'"' -f2 example.txt | tr -d '\n'
The result should look like this:
v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxzNTZydyFiNljq/Md1cXNEqemKDk9CKhZGHSzEH6x0zxtdcv5ROzaytJ4OsatDOdk+Pygkj6Qq9PiLCc3HlWPTcvMEs+M8YvRergTATFNoAmXLXvpbi+DD0oXAsbz2dM/klObY9OSNlJqFpzmGjgRbtSnvCbot8Smg5LreCjmkuHo/sxyynRHGwRHUM6jokm2YGIGATZBIVqtSjM418Gtxx9MZUbwcQTlchk1hSQgbXlAAl5tagle3bq/2GwrwrdaghRH750qLjnBQhzdFnH+GjHTmRl2drQ/2zG1L0GlufipZ1UkWulidox2RtIykv2VxDlBYb77G4PAiiJsSar+wIDAQAB
This is your public key for DKIM.
Now go to your DNS editor, click on Add TXT Record, the hostname should be mail._domainkey
or whatever the first few characters the original example.txt showed.
In the value field copy and paste the public key. Make sure there are no errors.
Configuring postfix to use OpenDKIM
Set up postfix to process outgoing E-mails with OpenDKIM
postconf -e "milter_default_action = accept"
postconf -e "milter_protocol = 6"
postconf -e "smtpd_milters = local:opendkim/opendkim.sock"
postconf -e "non_smtpd_milters = local:opendkim/opendkim.sock"
Add this to your /etc/default/opendkim
file to set the socket for postfix
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
And then create the socket directory
mkdir /var/spool/postfix/opendkim
chown opendkim:postfix /var/spool/postfix/opendkim
Testing if DKIM is working
Wait for the DNS records to propagate (shouldn't take long!) and send an email with your SMTP server, on the receiving end, your email client should have an option
to "view source" or "show email headers", open that, you should see a DKIM-Signature:
field there.
You can now send a test email to a GMail account, and on the GMail web app you can view the email's source. It will show detailed information about your DKIM and SPF setup. GMail is by far the best test to verify that your authentication mechanisms (SPF, DKIM, etc) are working properly.
Use DMARC to tell receiving servers how to handle your email
Domain-based Message Authentication, Reporting and Conformance, short for DMARC can be used to specify a set of instructions for the receiving email servers on how
to handle the email. You can use it to specify which authentication mechanism (SPF, DKIM or both) is in place, what to do with emails which fail authentication
(you can reject, send, or quarantine them), how to check the From:
field of the email. It can also be used by receiving servers to report back to your own server,
in case any of the checks fail.
All DMARC records start with v=DMARC1
, and the p=
field would specify what to do with emails that fail the SPF or the DKIM test. p=
can have these three values:
- none: do nothing
- quarantine: quarantine the email (e.g send it to the spam folder)
- reject: reject the incoming email
It is best to use p=none
while testing your SPF and DKIM records, after that you can choose between p=reject
or p=quarantine
.
You can also optionally use rua=mailto:your_email
to get DMARC fail reports to your email. Just replace your_email
with your E-mail.
You can specify multiple E-mail addresses seperated with a comma.
Here's what an example DMARC record should look like:
v=DMARC1; p=quarantine; rua=mailto:user@example.com
Add this to your domain's TXT records and set the hostname to _dmarc
and you should be good to go!
Use this DKIM test to test your SPF, DKIM and DMARC configuration.