Table of Contents
OpenDKIM on Postfix with virtual domains
In this tutorial we will install OpenDKIM on a GNU/Linux mail server based on Debian 11 Buster. The mail service is provided by Postfix configured for virtual domains using virtual_alias_domains.
apt install opendkim opendkim-tools
In Debian 11 Bullseye the service is controlled (enable, start, stop, etc.) by Systemd:
systemctl status opendkim.service
Because Postfix is running into a chroot, it cannot access the /run/opendkim/opendkim.sock
Unix socket to communicate with opendkim, so we change the Socket
option into /etc/opendkim.conf and make the daemon to be listening on port 127.0.0.1:8891/TCP:
Socket inet:8891@localhost
The same daemon is used both for signing and verifying. Signing is performed when the client connecting to the MUA is authenticated and the From: address matches the domains to be signed (see the command line option -d or the SigningTable option of the /etc/opendkim.conf
configuration file), verifying is performed in other cases.
Create the keys in /etc/dkimkeys/
The canonical directory to keep the keys is /etc/dkimkeys/. For each domain we can have more than one key (e.g. when a key is to be renewed, etc.), so each key is identified by the domain name and by an arbitrary selector; it is a common practice to use the current year as the selector.
We create one subdirectory for each virtual domain:
DOMAIN='rigacci.org' SELECTOR='2022' mkdir /etc/dkimkeys/"$DOMAIN" chown opendkim:opendkim /etc/dkimkeys/"$DOMAIN" chmod 700 /etc/dkimkeys/"$DOMAIN" sudo -u opendkim opendkim-genkey -D /etc/dkimkeys/"$DOMAIN" -d "$DOMAIN" -s "$SELECTOR"
This will create two files:
- /etc/dkimkeys/$DOMAIN/$SELECTOR.private - The RSA private key.
- /etc/dkimkeys/$DOMAIN/$SELECTOR.txt - The public key, already in TXT format to be inserted into the DNS zone.
Add the private key in /etc/dkimkeys/keytable
It is necessary to tell OpenDKIM what is the private key to use when it wants to sign a mail from a domain. We must add one line for each domain into /etc/dkimkeys/keytable:
[SELECTOR]._domainkey.[DOMAIN] [DOMAIN]:[SELECTOR]:/etc/dkimkeys/[DOMAIN]/[SELECTOR].private
Add the public key into the DNS zone
Now it is necessary to publish the public key into the DNS. Just copy and paste the .txt file into the zone file:
2022._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; " "p=MIIBIjANBgkqhkiG9w0BAQ..." "W0CdtxNd+xRgCopJCp93CLiD..." ) ; ----- DKIM key 2022 for rigacci.org
Add the domain (or single sender) to be signed
Into the file /etc/dkimkeys/signingtable we declare that mails originating from that domain must be signed:
*@[DOMAIN] [SELECTOR]._domainkey.[DOMAIN]
NOTICE: The use of the wildcard (to indicate all the senders from a domain) is possibile if signingtable is declared with refile (regular expression file) into the configuration file. Otherwise you have to specify every single sender address where signing is to be applied.
Remember to reload OpenDKIM after changing the signingtable:
systemctl reload opendkim.service
Configure OpenDKIM
Into the /etc/opendkim.conf file we inform OpenDKIM to look into a KeyTable to find keys and into a SigningTable to know which domains require signing. The service will listen on port 8891/TCP (should use Unix domain socket instead? Better performances? More painfull because Postfix runs in chroot).
# We use virtual domains, so we use KeyTable and SigningTable KeyTable file:/etc/dkimkeys/keytable SigningTable refile:/etc/dkimkeys/signingtable # Match a list of hosts whose messages will be signed. # By default, only localhost is considered as internal host. #InternalHosts refile:/etc/dkimkeys/trustedhosts # Socket for the MTA connection (required). Socket inet:8891@localhost
NOTICE: refile means that the file contains regular expressions (e.g. asterisk wildcard to indicate all the mail addresses into a domain).
Test the OpenDKIM configuration
Reload the DNS Bind service and test that OpenDKIM can properly use the keys (it is not necessary to reload the OpenDKIM service):
# opendkim-testkey -v -v opendkim-testkey: using default configfile /etc/opendkim.conf opendkim-testkey: record 0 for '2022._domainkey.rigacci.org' retrieved opendkim-testkey: checking key '2022._domainkey.rigacci.org' opendkim-testkey: key 2022._domainkey.rigacci.org not secure opendkim-testkey: 1 keys checked; 1 pass, 0 fail
Signing message test
cat message.txt \ | opendkim-testmsg -d "$DOMAIN" -k "/etc/dkimkeys/$DOMAIN/$SELECTOR.private" -s "$SELECTOR.$DOMAIN"
Configure Postfix
Message signing with OpenDKIM is performed as a milter (mail filter) in Postfix; milters are declared into the /etc/postfix/main.cf configuration file.
Using the non_smtpd_milters directive we may add DKIM for locally generated mails, i.e. local submissions via sendmail command line, submissions to the qmqpd, (Quick Mail Queuing Protocol daemomn), re-injected mails. More generally we may apply DKIM signature for all the messages received by the SMTP daemon, using the smtpd_milters directive.
Using custom settings in /etc/postfix/master.cf, you can declare specific milters for messages received from your users over the submission protocol only (port 587/TCP). In this snippet of master.cf
we use a custom mua_milters directive:
submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_milters=$mua_milters -o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_relay_restrictions=$mua_relay_restrictions
Having done this, we define the custom mua_milters directive in main.cf
to apply SpamAssassin and DKIM filtering on sumbitted messages:
# Locally generated mails (e.g. from command line Mutt) are filtered with OpenDKIM. non_smtpd_milters = inet:localhost:8891 # Mails received via SMTP protocol are filtered with OpenDKIM; # messages created using SoGO webmail go through this milter. smtpd_milters = inet:localhost:8891 # Filters applied (as smtpd_milters) to messages received via SUMBISSION/587; mua_milters = unix:spamass/spamass.sock, inet:localhost:8891
Another important Postfix setting is milter_default_action, the default is tempfail which means that if the milter does not respond, the message will be held into the queue and retried later. Other settings can be accept or reject:
milter_default_action = tempfail
Logging
When a message passes through the OpenDKIM filter, you get the following line into mail.log:
opendkim[983999]: 37FDD7D659: DKIM-Signature field added (s=2022, d=rigacci.org)
If a message does not match any entry in /etc/dkimkeys/signingtable, it will not be signed; the log is:
opendkim[983999]: 4778D7D610: no signing table match for 'testmail@rigacci.org' opendkim[983999]: 4778D7D610: no signature data