--- title: "Authenticating Your Mailserver With SPF and DKIM" date: 2022-09-14T18:52:08+05:30 highlight: true --- 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.](https://odysee.com/@MikunoNaka:d/self-hosting-email-server:0) 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 domain - `mx:your-domain.tld` means the email is legit if it comes from a server which is in the MX Records of your-domain.tld - `ip4:ipv4_address` means the email is legit if the sending server has this particular IPv4 address - `ip6: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: ![SPF Record Screenshot](/images/spf-record-example.png) **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](https://www.spf-record.com/syntax) has all of the other info about structuring an SPF record. ### Setting up DKIM (OpenDKIM) #### Install the necessary packages ``` fish 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: ``` fish 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: ``` fish 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 ``` fish systemctl restart opendkim ``` #### Add the TXT Records Read the contents of the TXT file generated by `opendkim-genkey`: ``` fish 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. 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 ``` fish 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 ``` fish 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](https://appmaildev.to/en/dkim) DKIM test to test your SPF, DKIM and DMARC configuration.