Ubersmith Mail Container: How to Configure OpenDKIM Estimated Reading Time: 8 Minutes Ubersmith's mail container runs Postfix. The container also has OpenDKIM software. OpenDKIM signs outgoing mail. This makes it possible for receiving mail servers to confirm that the mail came from you. By default, the container does not sign mail. This document tells you how to configure the container to sign mail with OpenDKIM. What the Image Already Has The image has the OpenDKIM software installed. The image is not yet configured to sign mail for your domain. The image does these tasks for you: The image adds the postfix user to the opendkim group. This lets Postfix connect to the OpenDKIM socket. The image creates the directory /etc/opendkim/keys. The owner of this directory is opendkim:opendkim. Use this directory to store your private keys. The image creates the directory /var/spool/postfix/opendkim. The owner of this directory is opendkim:postfix. OpenDKIM creates its socket file in this directory. The image starts OpenDKIM when the container starts. The image runs the command opendkim -x /etc/opendkim.conf before it starts Postfix. The image includes a default file at /etc/opendkim.conf. This default file does not have a domain, a selector, or a key file set. You must replace this file. Refer to Step 6. Postfix runs in a chroot at /var/spool/postfix. Because of this, the socket address local:opendkim/opendkim.sock refers to this file on disk: /var/spool/postfix/opendkim/opendkim.sock Your opendkim.conf file must use this full path as the value of the Socket setting. If you do not use this full path, Postfix cannot find the socket. What You Must Provide The image does not sign mail until you provide these five items and turn on the milter connection. You can complete the steps in any order. Complete all steps before you restart the container. File in the container Purpose of the file /etc/opendkim.conf The main OpenDKIM configuration file /etc/opendkim/TrustedHosts A list of hosts and networks that are allowed to send signed mail /etc/opendkim/KeyTable A table that links a key name to a domain, a selector, and a key file /etc/opendkim/SigningTable A table that links sender addresses to a key in the KeyTable file /etc/opendkim/keys/<domain>/<selector>.private Your DKIM private key Step 1: Generate a DKIM Key Pair Do this step for each domain that you want to sign. Run this command on the host computer, or run it inside the running container: mkdir -p keys/example.com opendkim-genkey -b 2048 -d example.com -D keys/example.com -s mail -v This command creates two files: keys/example.com/mail.private — This is the private key. OpenDKIM uses this file to sign mail. keys/example.com/mail.txt — This is the public key. The file has the correct format for a DNS TXT record. The option -s mail sets the selector name to mail. You can use a different selector name. If you use a different name, you must use the same name in the KeyTable file and in the DNS record. Step 2: Set the Owner and the Permissions of the Key The opendkim user must be able to read the private key file. Other users must not be able to read the private key file. Run these commands: chown -R opendkim:opendkim keys/example.com chmod 400 keys/example.com/mail.private Step 3: Create the KeyTable File Add this line to the KeyTable file: mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private Step 4: Create the SigningTable File Add this line to the SigningTable file: *@example.com mail._domainkey.example.com Step 5: Create the TrustedHosts File List the hosts and the networks that can send mail through this container without a signing error. Add your local host and your internal network to this list. 127.0.0.1 localhost ::1 10.0.0.0/8 Step 6: Create the opendkim.conf File Use this configuration. This configuration matches the way the image connects OpenDKIM to Postfix. Syslog yes UMask 002 Mode sv Canonicalization relaxed/simple PidFile /var/run/opendkim/opendkim.pid SignatureAlgorithm rsa-sha256 UserID opendkim:opendkim KeyTable /etc/opendkim/KeyTable SigningTable refile:/etc/opendkim/SigningTable ExternalIgnoreList /etc/opendkim/TrustedHosts InternalHosts /etc/opendkim/TrustedHosts Socket local:/var/spool/postfix/opendkim/opendkim.sock The setting UMask 002 is necessary. This setting makes the socket file writable by its group. The postfix user is a member of the opendkim group, so the setting lets Postfix connect to the socket. Step 7: Turn On the Milter Connection in Postfix The entrypoint script in the image reads all environment variables that start with POSTCONF_. The script applies each variable as a Postfix setting before Postfix starts. The image has the milter connection available, but the connection is off by default. Set these four environment variables in docker-compose.override.yml to turn it on: POSTCONF_SMTPD_MILTERS='local:opendkim/opendkim.sock' POSTCONF_NON_SMTPD_MILTERS='$$smtpd_milters' POSTCONF_MILTER_DEFAULT_ACTION='accept' POSTCONF_MILTER_PROTOCOL='6' If you do not set these variables, Postfix does not send mail to OpenDKIM. In this case, OpenDKIM does not sign mail, even if the rest of your configuration is correct. Step 8: Start the Container With Your Files and VariablesThis example shows the same configuration in a docker-compose.yml file: services: mail: [...] environment: POSTCONF_SMTPD_MILTERS: 'local:opendkim/opendkim.sock' POSTCONF_NON_SMTPD_MILTERS: '$$smtpd_milters' POSTCONF_MILTER_DEFAULT_ACTION: 'accept' POSTCONF_MILTER_PROTOCOL: '6' volumes: - /usr/local/ubersmith/conf/mail/opendkim.conf:/etc/opendkim.conf:ro - /usr/local/ubersmith/conf/mail/TrustedHosts:/etc/opendkim/TrustedHosts:ro - /usr/local/ubersmith/conf/mail/KeyTable:/etc/opendkim/KeyTable:ro - /usr/local/ubersmith/conf/mail/SigningTable:/etc/opendkim/SigningTable:ro - /usr/local/ubersmith/conf/mail/keys:/etc/opendkim/keys:ro Note: In a docker-compose.override.yml file, write $smtpd_milters as $$smtpd_milters. If you do not double the dollar sign, Compose tries to replace the variable itself, and this causes an error. Caution: The image sets the owner and the permissions of /etc/opendkim/keys when it builds the image. A bind mount replaces this owner with the owner from the host computer. Confirm that the opendkim user in the container can read the key files. If the opendkim user cannot read the key files, OpenDKIM does not sign mail. Step 9: Publish the Public Key in DNS Open the file keys/example.com/mail.txt. Copy the text in this file. Publish the text as a DNS TXT record at this name: mail._domainkey.example.com For example: mail._domainkey.example.com. IN TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." Some DNS providers require you to split a long TXT value into separate quoted parts. The file mail.txt already has this format. Step 10: Restart the Container and Confirm the Result Restart the container. This action makes the container run the command opendkim -x /etc/opendkim.conf again, with your new configuration. cd /usr/local/ubersmithdocker-compose restart mail Before you test mail flow, confirm that the milter connection is on: docker-compose exec mail postconf smtpd_milters non_smtpd_milters milter_default_action milter_protocol docker-compose exec mail ls -la /var/spool/postfix/opendkim/opendkim.sock Send a test message through the container. Open the message headers. Look for a DKIM-Signature header that is similar to this example: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com; s=mail; t=...; bh=...; h=Subject:From:To:Date; b=... After the DNS record from Step 9 is available, confirm the key with the opendkim-testkey command: docker-compose exec mail opendkim-testkey -d example.com -s mail -k /etc/opendkim/keys/example.com/mail.private -vvv If the command is successful, the key in KeyTable matches the key in DNS. If the DNS record is not yet available on the internet, the command reports No key. This result is normal. It does not mean that your OpenDKIM configuration has an error. Troubleshooting The mail does not have a DKIM-Signature header. First, confirm that the milter connection is on. Run this command: docker exec mail postconf smtpd_milters non_smtpd_milters. If the result is empty, you did not set the POSTCONF_* variables from Step 7, or you did not restart the container after you set them. If the result is correct, check the container log with docker logs mail. Look for OpenDKIM startup errors. Common errors are wrong key file permissions and syntax errors in the KeyTable or SigningTable files. OpenDKIM does not start, or the socket file does not appear. Confirm that the file /etc/opendkim.conf exists and has no errors. The entrypoint script runs OpenDKIM before it starts Postfix. If OpenDKIM has a configuration error, Postfix starts without a milter connection. If the smtpd_milters and non_smtpd_milters settings are on, but the socket does not exist, Postfix rejects mail. Postfix writes this message to the log: "Milter … local:opendkim/opendkim.sock: unable to connect". Confirm that the socket file exists at /var/spool/postfix/opendkim/opendkim.sock. Confirm that the Socket setting in opendkim.conf uses this exact path. Confirm that the postfix user is a member of the opendkim group, and that opendkim.conf has the setting UMask 002. The DNS check does not succeed. Confirm that the selector name in KeyTable and SigningTable matches the selector name in the DNS record name, in this format: <selector>._domainkey.<domain>. Confirm that the published TXT record matches the file mail.txt exactly.