A recipe to set up DKIM signing for grommunio.
Prerequisites:
- grommunio and grommunio Antispam must be set up and able to send and receive emails without error.
- This guide has been tested on the grommunio appliance running openSUSE 15.x and openSUSE 16.0.
- You need access to the DNS server to enter the DKIM public key as a TXT record.
Note: In this recipe, we use <DOMAIN.TLD> as a placeholder for the domain, such as kunde.at and <SELECTOR> as a placeholder for the selector, such as dkim2026, ds2026 or any other selector. Replace these placeholders with your domain and selector. dkim is the default selector.
Note: about the selector: it is a good idea to include the year in the appendix of the selector, e.g. dkim2026. When you need to replace an old key, you can increase the appendix number so that both keys are present in your DNS.
Keep the old DNS record available for at least the duration of your DNS TTL and any relevant message-delivery period.
1. Add the non_smtpd_milter to Postfix
So that Postfix can request DKIM signing from grommunio-antispam.
Add the line non_smtpd_milters=inet:[::1]:11332 to /etc/postfix/main.cf, or simply run this command:
postconf -e 'non_smtpd_milters = $smtpd_milters'
to add the existing smtp_milter as non_smtpd_milter.
Verify that both milters are exists:
postconf smtpd_milters
postconf non_smtpd_milters
You should see an output like this:
smtpd_milters = inet:[::1]:localhost:11332
non_smtpd_milters = inet:[::1]:11332
Or an output like this:
smtpd_milters = inet:localhost:11332
non_smtpd_milters = $smtpd_milters
Note: inet:[::1]:11332 and inet:localhost:11332 both point to localhost but with different spellings.
2. Restart Postfix to activate the new non_smtpd_milter
Before restarting Postfix, run a configuration check. If you see any errors, fix them.
postfix check
Once there are no errors, restart Postfix with the command:
systemctl restart postfix
3. Create: /etc/grommunio-antispam/override.d/dkim_signing.conf
This file contains the DKIM signing settings.
Example and explanation for DKIM signing
Use the file provided in Appendix A. it is already customized for grommunio.
The DKIM signing key directory is: /var/lib/grommunio-antispam/dkim/
To enable multi-domain signing, each domain needs to be added to the end of the configuration file in the domain { } section. This is done in step 7.
4. Create the DKIM signing key directory
mkdir -m 0700 /var/lib/grommunio-antispam/dkim
Thanks to @crpb. https://github.com/grommunio/admin-api/issues/70
5. Generate the DKIM signing key
You can create the signing key on the command line or in the Admin-UI. We will demonstrate both methods, beginning with the command line option.
Run this command on the command line for an 2048 bit RSA key:
rspamadm dkim_keygen -s '<SELECTOR>' -b 2048 -d <DOMAIN.TLD> -k /var/lib/grommunio-antispam/dkim/<DOMAIN.TLD>.<SELECTOR>.key > /tmp/dnsrecord_<DOMAIN.TLD>.txt
If you want to generate an Ed25519 key, use this command:
rspamadm dkim_keygen -s '<SELECTOR>' -t ED25519 -d <DOMAIN.TLD> -k /var/lib/grommunio-antispam/dkim/<DOMAIN.TLD>.<SELECTOR>.key > /tmp/dnsrecord_<DOMAIN.TLD>.txt
Note: Ed25519 keys are much shorter than RSA keys while providing a comparable or higher security level.
Note: An Ed25519 key is an elliptic-curve-based key, but DKIM does not support arbitrary ECDSA/EC keys. The DKIM extension specifically defines the ed25519 key type. The DNS record consequently uses: k=ed25519 not a generic k=ec.
To generate the signing key with the Admin-UI, go to DOMAINS -> <DOMAIN.TLD> -> DKIM -> GENERATE DKIM KEYPAIR. Enter a selector or leave it blank for 'dkim'. Click GENERATE and copy the certificate to the clipboard - you will need it later.
The private key can be found in the file /var/lib/grommunio-admin-api/<DOMAIN.TLD>.<SELECTOR>.key.
Finally, copy the private key from /var/lib/grommunio-admin-api/<DOMAIN.TLD>.<SELECTOR>.key to /var/lib/grommunio-antispam/dkim/.
Use this command:
cp -av /var/lib/grommunio-admin-api/<DOMAIN.TLD>.<SELECTOR>.key /var/lib/grommunio-antispam/dkim/
6. Most importantly, use the 'chown' commands to secure the key file(s)
Note: The easiest way to process all the signing keys in the /var/lib/grommunio-antispam/dkim/ folder is to use the following command:
chown -Rf groas:grommunio /var/lib/grommunio-antispam/dkim
Thanks to @crpb. https://github.com/grommunio/admin-api/issues/70
After securing the key, verify that the user groas can read the key:
sudo -u groas cat /var/lib/grommunio-antispam/dkim/<DOMAIN.TLD>.<SELECTOR>.key
If you can see the key, the user groas can read it. If not, you need to fix the problem.
7. Populate the domain list in the dkim_signing.conf file
Near the end of the /etc/grommunio-antispam/override.d/dkim_signing.conf file, you will find the domain { } section. Every domain, including its key and selector, needs to be listed here. See the example below:
domain {
<DOMAIN.TLD> {
# Private key path
path = "/var/lib/grommunio-antispam/dkim/<DOMAIN.TLD>.<SELECTOR>.key";
# Selector
selector = "<SELECTOR>";
}
}
8. Dump and inspect the DKIM signing configuration
Dump and inspect the DKIM signing configuration and fix any errors found.
To dump the configuration, use this command:
rspamadm configdump dkim_signing
9. Test the grommunio-antispam configuration
Run a configuration test and fix any errors.
rspamadm configtest
When no errors are shown, proceed with restart grommunio-antispam.
10. Restart grommunio-antispam to activate DKIM signing
systemctl restart grommunio-antispam
11. Populate the DKIM DNS record for your domain
- The result in
/tmp/dnsrecord_<DOMAIN.TLD>.txt can be used to create the corresponding DNS record.
- If you created the key with the Admin-UI, the required information will be displayed on the screen after the key has been generated.
- Please note that the RSA signature is split into two or more parts. Concatenate all the parts together. The monolithic signature looks like
MIIB<SIGNATURE><SIGNATURE_2._PART>AQAB. MIIB is the start sequence and AQAB is the end of the key.
The DNS record is a TXT record and looks like this (with an RSA key):
Name = <SELECTOR>._domainkey.<DOMAIN.TLD>
Type = TXT
Content = v=DKIM1; k=rsa; p=<SIGNATURE><SIGNATURE_2._PART>
Priority = 0
TTL = 3600
Note: If you use an Ed25519 key, replace k=rsa; with k=ed25519;
Note: For testing purposes, set a very short TTL, e.g. 300 seconds. If the key is not working, you may replace it within 5 minutes. Later, after testing, increase the TTL to a minimum of 3600 or higher.
Here's a note from @crpb about the DNS record: https://github.com/grommunio/admin-api/issues/81
12. Query the DKIM DNS TXT record
dig +short TXT <SELECTOR>._domainkey.<DOMAIN.TLD>
If the displayed DKIM record meets your requirements and is valid, you can proceed to test the DKIM signature. If the key is incorrect, go back to step 5 and try again.
13. Test the DKIM signature
One option is DKIMValidator.com. Send an email to the address displayed on the website. DKIMValidator.com will then decode the email and display any errors or confirm that the DKIM signature is valid.
In the field DKIM Information: you should see "Validating Signature" "result = pass". If you see these, then the DKIM signature is working.
If you have a Gmail account, the second option is to send an email to your Gmail account, open the email in the web interface, click on the three-dot menu and select 'Show original', then look for 'DKIM signature'. If this is present, the email is DKIM-signed.
14. Increase the TTL for the DKIM record
As mentioned in Step 11, increase the TTL for the DKIM DNS record to at least 3600, 7200, or ideally 86400.
15. Key rotation notice
When replacing an outdated DKIM key, do not overwrite an existing DKIM key immediately. Instead, generate a new key with a new selector like dkim2027, publish the new DNS record and switch grommunino-antispam to the new selector. Keep the old DNS record available for at least the duration of your DNS TTL and any relevant message-delivery period.
For example:
dkim2027._domainkey.example.com`
instead of reusing:
dkim._domainkey.example.com
This makes rotation much safer.
16. Done
DKIM signing is now working for your domain.
Appendix A: The /etc/grommunio-antispam/override.d/dkim_signing.conf
This version of the configuration file has already been customized for grommunio.
# local.d/dkim_signing.conf
# Enable DKIM signing - WH
enabled = true;
# If false, messages with empty envelope from are not signed
allow_envfrom_empty = true;
# If true, envelope/header domain mismatch is ignored
allow_hdrfrom_mismatch = false;
# If true, domain mismatch is ignored for local IPs
allow_hdrfrom_mismatch_local = false;
# If true, domain mismatch is ignored for sign_networks
allow_hdrfrom_mismatch_sign_networks = false;
# If true, multiple from headers are allowed (but only first is used)
allow_hdrfrom_multiple = false;
# If true, username does not need to contain matching domain
allow_username_mismatch = false;
# Default path to key, can include '$domain' and '$selector' variables
path = "/var/lib/grommunio-antispam/dkim/$domain.$selector.key";
# Default selector to use
selector = "dkim";
# If false, messages from authenticated users are not selected for signing
sign_authenticated = true;
# If false, messages from local networks are not selected for signing
sign_local = true;
# Map file of IP addresses/subnets to consider for signing
# sign_networks = "/some/file"; # or url
# Symbol to add when message is signed
symbol = "DKIM_SIGNED";
# Whether to fallback to global config
try_fallback = true;
# Domain to use for DKIM signing: can be "header" (MIME From), "envelope" (SMTP From), "recipient" (SMTP To), "auth" (SMTP username) or directly specified domain name
use_domain = "header";
# Domain to use for DKIM signing when sender is in sign_networks ("header"/"envelope"/"auth")
use_domain_sign_networks = "header";
# Domain to use for DKIM signing when sender is a local IP ("header"/"envelope"/"auth")
#use_domain_sign_local = "header";
# Whether to normalise domains to eSLD
use_esld = true;
# Whether to get keys from Redis
use_redis = false;
# Hash for DKIM keys in Redis
key_prefix = "dkim_keys";
# map of domains -> names of selectors (since rspamd 1.5.3)
#selector_map = "/etc/grommunio-antispam/dkim_selectors.map";
# map of domains -> paths to keys (since rspamd 1.5.3)
#path_map = "/etc/grommunio-antispam/dkim_paths.map";
# If `true` get pubkey from DNS record and check if it matches private key
check_pubkey = false;
# Set to `false` if you want to skip signing if public and private keys mismatch
allow_pubkey_mismatch = true;
# Sign inbound messages (not from local/authenticated)
# sign_inbound = false;
# Skip signing if message is marked as spam
# skip_spam_sign = false;
# Use milter headers instead of modifying message directly
# use_milter_headers = false;
# Allowed settings IDs for signing
# allowed_ids = [];
# Forbidden settings IDs (block signing)
# forbidden_ids = [];
# Domain specific settings
domain {
# Domain name is used as key
example.com {
# Private key path
path = "/var/lib/grommunio-antispam/dkim/example.com.ds2026.key";
# Selector
selector = "ds2026";
}
<DOMAIN.TLD> {
# Private key path
path = "/var/lib/grommunio-antispam/dkim/<DOMAIN.TLD>.<SELECTOR>.key";
# Selector
selector = "<SELECTOR>";
}
# Add the next domain here
}
Appendix B: Some ideas on how to debug DKIM signing
Check/verify the key using the following command:
openssl rsa -in /var/lib/grommunio-antispam/dkim/<DOMAIN.EXT> -check
Note: The command above only works for RSA private keys. Ed25519 is an elliptic-curve-based signature algorithm, but it is a separate OpenSSL key type and should not be checked with openssl ec.
Test the Grommunio-Antispam configuration using the command
rspamadm configtest
and repair any errors.
Show the configuration for dkim_signing logging:
rspamadm configdump dkim_signing logging
Create the file: /etc/grommunio-antispam/local.d/logging.inc with the following content:
debug_modules = ["dkim_signing"];
Restart grommunio-antispam to activate debugging:
systemctl restart grommunio-antispam
You should then see the DKIM signing in the log, use: journalctl -f
Appendix C:
I hope this recipe makes setting up DKIM signing easier!
Changes:
- Improved style
- The <SELECTOR> is now part of the key filename because the Admin-UI generates the key with the <SELECTOR>. This is also the RSpamd standard.
- Added: Key rotation
- Added: Explanation of Ed25519 keys
- Modified 5. Generate the DKIM signing key, Thanks to @crpb
- Modified 6. Most importantly, Thanks to @crpb
Enjoy grommunio!