Fixing DKIM Signing Issues with OpenDKIM and Postfix
If your mail server sends messages but external tools report "Your message is not signed with DKIM", the issue is usually related to a missing configuration in OpenDKIM.
A common situation is when the trusted.hosts file does not exist or when OpenDKIM does not know which domains and keys it should use for signing outgoing mail.
Below is a structured guide to correctly configure OpenDKIM together with Postfix and ensure that your outgoing email is properly signed.
1. Create the trusted.hosts File
This file defines which hosts are allowed to send mail that OpenDKIM should sign.
sudo nano /etc/opendkim/trusted.hosts
Add the following content:
127.0.0.1
localhost
::1
vindazo.nl
*.vindazo.nl
Save and exit the file.
2. Complete the OpenDKIM Configuration
Open the main configuration file:
sudo nano /etc/opendkim.conf
Ensure the following parameters are present:
Syslog yes
LogWhy yes
Canonicalization relaxed/simple
Mode sv
SubDomains no
Socket inet:8891@localhost
KeyTable /etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
InternalHosts /etc/opendkim/trusted.hosts
ExternalIgnoreList /etc/opendkim/trusted.hosts
These settings tell OpenDKIM:
- Where to find the DKIM keys
- Which domains should be signed
- Which hosts are trusted to send mail
- How Postfix communicates with OpenDKIM
3. Verify the Default Socket Configuration
Ubuntu may override the socket configuration through the default settings file.
sudo nano /etc/default/opendkim
Make sure this line is active:
SOCKET="inet:8891@localhost"
Other socket definitions should remain commented out.
4. Verify DKIM Private Key Permissions
OpenDKIM must be able to read the private key used for signing.
ls -l /etc/opendkim/keys/vindazo.nl/mail.private
If necessary, correct the permissions:
sudo chown opendkim:opendkim /etc/opendkim/keys/vindazo.nl/mail.private
sudo chmod 600 /etc/opendkim/keys/vindazo.nl/mail.private
5. Restart the Services
After updating the configuration, restart both services.
sudo systemctl restart opendkim
sudo systemctl restart postfix
Then confirm OpenDKIM is listening on port 8891:
systemctl status opendkim
ss -lntp | grep 8891
6. Check Logs While Sending a Test Email
Open a live log viewer:
tail -f /var/log/mail.log | grep -i opendkim
Send a test email. If everything works correctly, you should see messages indicating that a DKIM signature has been added.
7. Example Working Configuration
/etc/opendkim/key.table
mail._domainkey.vindazo.nl vindazo.nl:mail:/etc/opendkim/keys/vindazo.nl/mail.private
/etc/opendkim/signing.table
*@vindazo.nl mail._domainkey.vindazo.nl
/etc/opendkim/trusted.hosts
127.0.0.1
localhost
::1
vindazo.nl
*.vindazo.nl
/etc/postfix/main.cf
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
milter_default_action = accept
milter_protocol = 2
/etc/opendkim.conf
Syslog yes
LogWhy yes
Canonicalization relaxed/simple
Mode sv
SubDomains no
Socket inet:8891@localhost
KeyTable /etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
InternalHosts /etc/opendkim/trusted.hosts
ExternalIgnoreList /etc/opendkim/trusted.hosts
8. Testing DKIM Externally
Send a message to testing services such as:
test@mail-tester.com
or
check-auth@verifier.port25.com
If the configuration is correct, the message headers will contain a DKIM-Signature and the report will confirm:
DKIM = PASS
Additional Note
If your mail logs show errors such as:
smtp_connect_addr: bind 148.251.XX.XX: Cannot assign requested address
This indicates a separate network configuration issue related to outbound IP binding in Postfix. While it does not directly affect DKIM signing, it should still be corrected to ensure stable mail delivery.
While configuring DKIM signing with Postfix and OpenDKIM, the mail server was successfully adding a DKIM signature to outgoing messages, but external validation tools such as Mail-Tester and Gmail were returning:
DKIM_INVALID
signature verification failed
At first glance, everything appeared to be configured correctly:
- OpenDKIM was running
- Postfix was connected to the DKIM milter
- DNS contained a DKIM TXT record
- The server was adding a DKIM-Signature header
However, the cryptographic verification still failed.
Root Cause
The actual issue turned out to be a mismatch between the private DKIM key used by the server and the public key stored in DNS.
In other words:
- The mail server was signing emails using one private key
- The receiving mail servers were verifying the signature using a different public key from DNS
Since DKIM relies on asymmetric cryptography, both keys must belong to the same key pair. If they do not match exactly, verification will always fail.
Step 1 — Extract the Public Key From the Private DKIM Key
The first step was to derive the public key directly from the private DKIM key used by OpenDKIM:
openssl rsa -in /etc/opendkim/keys/vindazo.nl/mail.private -pubout -outform PEM > /tmp/mail.pub.pem
Then convert the key to DER format and generate a fingerprint:
openssl pkey -pubin -in /tmp/mail.pub.pem -pubout -outform DER | sha256sum
Step 2 — Extract the Public Key From DNS
Next, retrieve the DKIM TXT record from DNS and convert it into a usable public key:
dig +short TXT mail._domainkey.vindazo.nl | tr -d '"' | tr -d '\n' | sed 's/.*p=//' > /tmp/dkim_dns_key.b64
Convert it to a PEM file:
python3 - <<'PY'
from pathlib import Path
import textwrap
b64 = Path('/tmp/dkim_dns_key.b64').read_text().strip()
pem = "-----BEGIN PUBLIC KEY-----\n" + "\n".join(textwrap.wrap(b64, 64)) + "\n-----END PUBLIC KEY-----\n"
Path('/tmp/dkim-dns.pub.pem').write_text(pem)
PY
Then generate the fingerprint of the DNS key:
openssl pkey -pubin -in /tmp/dkim-dns.pub.pem -pubout -outform DER | sha256sum
Step 3 — Compare Both Keys
If the fingerprints differ, the keys do not belong to the same pair. This was exactly the issue in this case.
Once the correct public key was generated from the private key, the DNS DKIM record was replaced with the correct value.
Correct DKIM DNS Record
mail._domainkey.vindazo.nl TXT
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvU7VwDD22TW4xAT9r5cwkQNBAieW3ucRw2SMkH7bOPsjQFC2kynjTNAeJyEa0xev9Ub7tmGZRbTTAtSBrzy+kiTZKyN51OPo3eWiYRxbmnBfw...
Final Verification
After updating the DNS record and waiting for propagation, DKIM verification was successful.
Testing again with Mail-Tester produced:
DKIM = PASS
This confirmed that the private signing key and DNS public key were finally aligned.
Key Takeaways
- Always ensure the DKIM private key on the server matches the public key in DNS
- If DKIM signatures appear but verification fails, check for a key mismatch first
- Comparing fingerprints of the derived public key and DNS key is the fastest diagnostic method
- DNS TXT records may appear split across multiple quoted strings, which is normal
Once the keys match, DKIM signing becomes stable and mail authentication improves significantly.
Configuring Additional IP Addresses on a Hetzner Dedicated Server
After a server rebuild or disk replacement, the network configuration may be reset to a minimal setup that only contains the primary IP address. In this case, additional IPv4 addresses configured in the Hetzner Robot panel will not be active on the server itself, which prevents services like Postfix from binding to those IPs.
This guide describes how the issue was diagnosed and fixed by updating the correct network configuration file.
Identifying the Network Configuration Method
The first step was to determine how networking was managed on the system. Running the following command revealed that the server uses systemd-networkd through Netplan.
networkctl status
The output showed that the interface was configured using a Netplan-generated file:
Configuring with /run/systemd/network/10-netplan-enp4s0.network
This indicated that the actual configuration is defined in the Netplan directory:
/etc/netplan/
Locating the Netplan Configuration File
Listing the directory revealed the active configuration file:
ls /etc/netplan
01-netcfg.yaml
Opening the file showed the original configuration which only contained the primary IP address:
network:
version: 2
renderer: networkd
ethernets:
enp4s0:
addresses:
- 148.251.89.72/32
- 2a01:4f8:202:6447::2/64
routes:
- on-link: true
to: 0.0.0.0/0
via: 148.251.89.65
- to: default
via: fe80::1
Adding Additional IPv4 Addresses
Hetzner uses routed additional IP addresses with a /32 subnet mask. This means extra addresses can simply be added to the same interface.
The configuration was updated to include all assigned IPv4 addresses:
network:
version: 2
renderer: networkd
ethernets:
enp4s0:
addresses:
- 148.251.89.72/32
- 148.251.89.86/32
- 148.251.89.87/32
- 2a01:4f8:202:6447::2/64
routes:
- on-link: true
to: 0.0.0.0/0
via: 148.251.89.65
- to: default
via: fe80::1
nameservers:
addresses:
- 185.12.64.2
- 2a01:4ff:ff00::add:1
- 185.12.64.1
- 2a01:4ff:ff00::add:2
Applying the Configuration
After editing the Netplan file, the configuration was applied with:
netplan apply
The new addresses became active immediately.
Verification
Finally, the interface was inspected to confirm that all IP addresses were correctly attached:
ip addr show enp4s0
The output confirmed that all three IPv4 addresses were now active:
148.251.89.72/32
148.251.89.86/32
148.251.89.87/32
With this configuration in place, services such as Postfix can now bind to different IP addresses for outbound connections.
Conclusion
The issue was caused by a missing network configuration after the server rebuild. By locating the correct Netplan configuration file and adding the additional IP addresses with a /32 mask, the server was restored to its intended multi-IP setup.
This configuration allows applications to bind specific services to different IP addresses, which is particularly useful for mail servers and other network services that require multiple outbound identities.

Comments
Post a Comment