====== 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]]**