What is DKIM?
DKIM is short for Domain Keys Identified Mail and is an internet standard, that ensures, that an email is in fact authorized by the owner of the domain, and, that its content is authentic and has not been modified. DKIM is available for FreeBSD as the OpenDKIM package or port. OpenDKIM is a milter for Sendmail, which is the default mail server in FreeBSD.
How does DKIM work?
The mail server of the sender automatically signs the email with a private key and adds a Domain Keys Internet Mail (DKIM) signature header field to the email. The signature is automatically verified with the public key by the mail server of the receiver. The public key is linked to the domain, because it is stored in a TXT record in DNS for the domain.
What is the difference between DKIM and SPF?
DKIM is installed on the mail server. SPF is installed on the DNS server. DKIM ensures, that the email message is authentic. The sender information and the message has not been forged nor modified during transmission. SPF ensures, that the email was sent by an authorized mail server. The email was not sent from a spam server nor unauthorized mail server.
OpenDKIM for Sendmail on FreeBSD.
OpenDKIM, which is the package or port for DKIM in FreeBSD, can sign outgoing email and validate incoming email. If the mail server handles mail for just a single domain, then OpenDKIM can use a key to sign mail for the domain. If the mail server handles mail for multiple domains, then OpenDKIM can use the same key to sign mail for the domains. I this example, I will configure OpenDKIM to use the same key for signing mail for multiple domains.
The documentation for OpenDKIM claims, that OpenDKIM can use multiple domain keys to sign mail for multiple domains. I have tested this, but it did not work. I tested configuration with inline settings. I tested configuration with external key tables and signing tables. It did not work. This resulted in parsing errors and segmentation fault.
I was a bit sad to learn, OpenDKIM documantation was so poorly written and out-dated. This became clear over and over again, during my installation and reading documentation. In fact, DKIM and OpenDKIM system administration remains a fairly unknown concept, despite being widely used by major mail services, such as Google and Microsoft. I hope, that my guide can be helpful contribution to OpenDKIM and FreeBSD users.
Install OpenDKIM for FreeBSD.
Install OpenDKIM from packages or ports. Note, that the message suggests, that the milter identifier is dkim-filter. This is unfortunately not correct. The milter identifier, that Sendmail will use for OpenDKIM, is opendkim. I have notified the FreeBSD maintainer.
# pkg search opendkim opendkim-2.10.3_12 DKIM library and milter implementation # pkg install opendkim Message from opendkim-2.10.3_12: In order to run this port, write your opendkim.conf and: if you use sendmail, add the milter socket `socketspec' in /etc/mail/<your_configuration>.mc: INPUT_MAIL_FILTER(`dkim-filter', `S=_YOUR_SOCKET_SPEC_, F=T, T=R:2m')
Generate the DKIM private and public keys.
OpenDKIM comes with the DKIM filter key generation tool opendkim-genkey. The tool generates a private key for signing messages and a public key for verifying messages. The default key length is 1024 bit. The default selector is “default”. The key can also sign subdomains. The public key comes in the form of a TXT record. The format is compatible with a zone file for BIND DNS.
Create a directory for storing the DKIM keys and generate a DKIM key. In FreeBSD, OpenDKIM will runs as the non-priviledged user and group “mailnull”. This is not mentioned in the documentation.
# mkdir -p /usr/local/etc/mail/opendkim-keys/default
# cd /usr/local/etc/mail/opendkim-keys/default
# opendkim-genkey
# chown mailnull:mailnull *
# chmod 600 *
I tested a higher key length of 2048 bit, but this resulted in truncated DNS answers via UDP, which probably further resulted in failed SPF and DKIM tests for some recipient mail servers.
Configure OpenDKIM on FreeBSD.
OpenDKIM does supply a sample configuration with comments. However, it is not documented, where to find this file, but a quick search can find it. Let it be known though, that the comments are poorly written and probably also incorrect. Following caused critical failure and segmentation fault for me. I highly recommend creating one instead.
# find / -type f -name 'opendkim.c*' /usr/local/etc/mail/opendkim.conf.sample # cp /usr/local/etc/mail/opendkim.conf.sample /usr/local/etc/mail/opendkim.conf
In this example, I will create a new configuration, that makes OpenDKIM use the same key to sign any email for domains, that are hosted by the mail server.
# nano /usr/local/etc/mail/opendkim.conf
Domain foobar.com,foobaranother.com
Selector default
KeyFile /usr/local/etc/mail/opendkim-keys/default/default.private
Socket inet:8891@localhost
Syslog Yes
SyslogSuccess Yes
I also tested the use of “Domain *”, but that caused DKIM test to fail at the recipient mail server.
If the sample configuration was used, the effective settings can be listed with cat and grep.
# cat /usr/local/etc/mail/opendkim.conf | grep -v '^#' | grep -v '^$'
Start OpenDKIM milter on FreeBSD.
Configure the OpenDKIM milter as a service in FreeBSD for automatic start. The service name is milteropendkim.
# nano /etc/rc.conf milteropendkim_enable="YES" milteropendkim_flags="-x /usr/local/etc/mail/opendkim.conf"
Identify the service script, that starts OpenDKIM. Note, that the service name can not be used to call the service script. The service script has a hyphen in its name, which makes it different from the service name. I have notified the FreeBSD maintainer.
# service -e | grep dkim /usr/local/etc/rc.d/milter-opendkim
Start the OpenDKIM milter.
# service milter-opendkim start Starting milteropendkim.
Confirm, that OpenDKIM is running and listening on the port.
# sockstat -4 -l | grep opendkim
mailnull opendkim 81337 3 tcp4 127.0.0.1:8891 *:*
OpenDKIM logs.
OpenDKIM writes entries to the system mail log.
# tail /var/log/maillog
Nov 13 13:37:42 foobar opendkim[96831]: OpenDKIM Filter v2.10.3 starting (args: -x /usr/local/etc/mail/opendkim.conf -l -u mailnull -P /var/run/milteropendkim/pid -x /usr/local/etc/mail/opendkim.conf)
Sendmail also writes DKIM related entries to the system mail log.
# cat /var/log/maillog | grep dkim | more
Configure Sendmail for OpenDKIM milter.
When OpenDKIM is configured and running as a service, then it is time to implement the OpenDKIM milter in Sendmail. Read more about the Sendmail configuration format in the Makefile in /etc/mail and the M4 stream configuration format in README in /usr/share/sendmail.
# cd /etc/mail/ # nano `hostname`.mc INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@localhost, F=T, T=R:2m') # make /usr/bin/m4 -D_CF_DIR_=/usr/share/sendmail/cf/ /usr/share/sendmail/cf/m4/cf.m4 foobar.com.mc > foobar.com.cf # make install install -m 444 foobar.com.cf /etc/mail/sendmail.cf install -m 444 foobar.com.submit.cf /etc/mail/submit.cf # make restart
Add DKIM TXT record to DNS zone.
In this example, the DKIM public domain key, that was generated and written as a TXT record, is added to the domain name zone in BIND DNS on a FreeBSD server. A lower time-to-live (TTL), such as 5 minutes, might be used in the initial test phase, before returning to a normal, such as 1 day.
# cat /usr/local/etc/mail/opendkim-keys/default/default.txt >> /usr/local/etc/namedb/master/foobar.com
# nano /usr/local/etc/namedb/master/foobar.com
$TTL 5M
default._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbwVD83oQqGVi14RWfbs6hfy5ZWTzv7tAPh7Gdw8humyLOQPcBhmD6OnMbZfEvacm75OqxD2SB5Tx2iq8xtsNcvPgvNF5Bta3gCrMSwdMZIFkwvWnQoAQjj2ZYc0loakivahqwr+b7PBmA0qR202ihG5zrSMBw/5SODjbnE4/+wQIDAQAB" )
Add DMARC TXT record to DNS zone.
The DMARC record defines, how failed DKIM signatures should be handled. The valid options are none, quarantine and reject. When the test phase is over, the value can be changed from none to reject. This record also has an optional email address for DMARC aggregate reports from mail servers.
Add a DMARC policy and optional DKIM aggregate report email address.
# nano /usr/local/etc/namedb/master/foobar.com
_dmarc IN TXT "v=DMARC1; p=none; rua=mailto:"
Reload the DNS.
Test the DNS zone.
# named-checkzone foobar.com /usr/local/etc/namedb/master/foobar.com
zone foobar.com/IN: loaded serial 13371377
OK
Reload DNS.
# service named restart
Test DNS with DKIM TXT record.
A DNS query consist of a name and a type. The name is a domain name, that is looked up, and the type is the type of resource record (RR), that is looked up. If just the name foobar.com was looked up, then that name would be different from the name default._domainkey.foobar.com, that has the DKIM key, and would not return the DKIM key.
Test the DNS by making a DNS query with the DNS lookup utilify drill for the new TXT record, that contains the DKIM public domain key.
# drill default._domainkey.foobar.com @localhost TXT
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 15126
;; flags: qr aa rd ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; default._domainkey.foobar.com. IN TXT
;; ANSWER SECTION:
default._domainkey.foobar.com. 300 IN TXT "v=DKIM1; k=rsa; " "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbwVD83oQqGVi14RWfbs6hfy5ZWTzv7tAPh7Gdw8humyLOQPcBhmD6OnMbZfEvacm75OqxD2SB5Tx2iq8xtsNcvPgvNF5Bta3gCrMSwdMZIFkwvWnQoAQjj2ZYc0loakivahqwr+b7PBmA0qR202ihG5zrSMBw/5SODjbnE4/+wQIDAQAB"
;; AUTHORITY SECTION:
;; ADDITIONAL SECTION:
;; Query time: 0 msec
;; SERVER: ::1
;; WHEN: Wed Nov 12 13:37:42 2024
;; MSG SIZE rcvd: 295
When the updated DNS zone has been adopted by DNS servers on the internet, then DNS and DKIM for the domain can be tested on Internet as well.
Test OpenDKIM signing.
OpenDKIM works, when the receiving mail server is able to lookup the DKIM key in DNS and then confirm the signature in the header of the email.
Send a test mail to a recipient, that can read the headers of the email. Confirm, that there is a DKIM signature, and, that it passed the authentication. GMail can be used to examine the headers and DKIM authentication of an email. This is done by opening the email and selecting to view it as original message.
Example of email header with passed DKIM authentication results and DKIM signature.
The following is an example from an email in GMail. The email header contains ARC authentication results, that confirm, that the email passed DKIM and SPF authentication. The email header also contains the DKIM signature.
ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@foobar.com header.s=default header.b=13371337; spf=pass (google.com: domain of designates 13.37.13.37 as permitted sender) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=foobar.com; s=default; t=1707280015; bh=ATFNXTdps/lpxZOwwqUpGoGBuiD+IO1337KfY2wcvwM=; h=Date:From:To:Subject; b=XL7nF+IGeTa521337caPAO1337NEXmtIm4uknaxCLa7/klLML3vtRzLD1337NJlKu /mANY71yXFLt7Evq3my367s0jKNhXhhNY1Sqzb3sEK9nq/DBYvLmEJn4ug7+FmU5+1 62HI7w/7Pwo9i1JCOWe92IoKPekJ813378CpeD97Yjp/MJz7iPVMP16vs3sdkVGl2t U+m7P2qEOpixzSgMLBIiBkCTDAE+ZJW+Asz80aDxX8qtDw1Top1zLoNYIU37ZwgskC 9xvywBEUhFwqP/FKJH/nBthkQbDwNC01uU7LMsi98lBtSC6Kb0RfUyqf5fagM89Z8n uFpjX1o2mrFng==
Change TTL and OpenDKIM policy.
When the DKIM signature has been confirmed to pass email, the TTL should be returned to normal the and DKIM policy should be changed, so spam mail is rejected.
# nano /usr/local/etc/namedb/master/foobar.com
$TTL 1D
_dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:"
Reload the DNS.
# named-checkzone foobar.com /usr/local/etc/namedb/master/foobar.com
zone foobar.com/IN: loaded serial 13371338
OK
# service named restart
How to raise log levels of Sendmail and OpenDKIM milter.
The log levels of Sendmail and OpenDKIM milter can be raised to include more details and help identify problems. This is done by configuring the Sendmail configuration file.
define(`confLOG_LEVEL', `9') define(`confMILTER_LOG_LEVEL', `18')
Attribution and sharing.
Feel free to link to this guide, if you find it useful. Contributions and feedback is always appreciated.
More about DKIM.
- https://www.dkim.org/
- https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail
- http://opendkim.org/
- https://freebsd.pkgs.org/13/freebsd-amd64/opendkim-2.10.3_16.pkg.html
- https://www.freshports.org/mail/opendkim
- https://man.freebsd.org/cgi/man.cgi?query=opendkim
- https://www.micski.dk/2024/02/09/how-to-configure-spf-policy-record-in-bind-dns/
- https://www.appmaildev.com/en/dkim
- https://www.mail-tester.com/
- https://www.isc.org/bind/
- https://www.isc.org/docs/2022-webinar-dns-wildcards.pdf
- https://groups.google.com/g/comp.protocols.dns.bind/c/TuMPtBAfRLA
- https://www.spamresource.com/2021/11/opendkim-and-postfix-signing-dkim-for.html