====== OpenDKIM on Postfix with virtual domains ======
In this tutorial we will install **[[http://www.opendkim.org/|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**, ([[wp>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
===== Web References =====
* **[[https://wiki.archlinux.org/title/OpenDKIM]]**
* **[[https://wiki.debian.org/opendkim]]**