DKIM, DMARC & SPF
Cloudflare has written an article about DKIM, DMARC and SPF that we highly recommend you to read to get acquainted with the topic.
Rspamd vs Individual validators
With v12.0.0, Rspamd was integrated into DMS. It can perform validations for DKIM, DMARC and SPF as part of the spam-score-calculation
for an email. DMS provides individual alternatives for each validation that can be used instead of deferring to Rspamd:
- DKIM:
opendkim
is used as a milter (like Rspamd) - DMARC:
opendmarc
is used as a milter (like Rspamd) - SPF:
policyd-spf
is used in Postfix'ssmtpd_recipient_restrictions
In a future release Rspamd will become the default for these validations, with a deprecation notice issued prior to the removal of the above alternatives.
We encourage everyone to prefer Rspamd via ENABLE_RSPAMD=1
.
DNS Caches & Propagation
While modern DNS providers are quick, it may take minutes or even hours for new DNS records to become available / propagate.
DKIM
What is DKIM
DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in email (email spoofing), a technique often used in phishing and email spam.
When DKIM is enabled:
- Inbound mail will verify any included DKIM signatures
- Outbound mail is signed (when you're sending domain has a configured DKIM key)
DKIM requires a public/private key pair to enable signing (via private key) your outgoing mail, while the receiving end must query DNS to verify (via public key) that the signature is trustworthy.
Generating Keys
You should have:
- At least one email account setup
- Attached a volume for config to persist the generated files to local storage
DKIM is currently supported by either OpenDKIM or Rspamd:
OpenDKIM is currently enabled by default.
The command docker exec <CONTAINER NAME> setup config dkim help
details supported config options, along with some examples.
Create a DKIM key
Generate the DKIM files with:
docker exec -ti <CONTAINER NAME> setup config dkim
Your new DKIM key(s) and OpenDKIM config files have been added to /tmp/docker-mailserver/opendkim/
.
LDAP accounts need to specify domains explicitly
The command is unable to infer the domains from LDAP user accounts, you must specify them:
setup config dkim domain 'example.com,example.io'
Changing the key size
The private key presently defaults to RSA-4096. To create an RSA 2048-bit key run:
setup config dkim keysize 2048
Opt-in via ENABLE_RSPAMD=1
(and disable the default OpenDKIM: ENABLE_OPENDKIM=0
).
Rspamd provides DKIM support through two separate modules:
- Verifying DKIM signatures from inbound mail is enabled by default.
- Signing outbound mail with your DKIM key needs additional setup (key + dns + config).
Create a DKIM key
Presently only OpenDKIM is supported with setup config dkim
. To generate your DKIM key and DNS files you'll need to specify:
-s
The DKIM selector (eg:mail
, it can be anything you like)-d
The sender address domain (everything after@
from the email address)
See rspamadm dkim_keygen -h
for an overview of the supported options.
- Go inside the container with
docker exec -ti <CONTAINER NAME> bash
- Add
rspamd/dkim/
folder to your config volume and switch to it:cd /tmp/docker-mailserver/rspamd/dkim
- Run:
rspamadm dkim_keygen -s mail -b 2048 -d example.com -k mail.private > mail.txt
(change-d
to your domain-part) - Presently you must ensure Rspamd can read the
<selector>.private
file, run: -chgrp _rspamd mail.private
-chmod g+r mail.private
DMS config volume support is not ready for Rspamd
Presently you'll need to explicitly mount rspamd/modules/override.d/
as an additional volume; do not use rspamd-modules.conf
for this purpose.
Create a configuration file for the DKIM signing module at rspamd/modules/override.d/dkim_signing.conf
and populate it with config as shown in the example below:
DKIM Signing Module Configuration Examples
A simple configuration could look like this:
# documentation: https://rspamd.com/doc/modules/dkim_signing.html
enabled = true;
sign_authenticated = true;
sign_local = true;
use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true;
check_pubkey = true; # you wan't to use this in the beginning
domain {
example.com {
path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";
selector = "mail";
}
}
As shown next, you can:
- You can add more domains into the
domain { ... }
section. - A domain can also be configured with multiple selectors and keys within a
selectors [ ... ]
array.
# ...
domain {
example.com {
selectors [
{
path = "/tmp/docker-mailserver/rspamd/dkim/example.com/rsa.private";
selector = "dkim-rsa";
},
{
path = /tmp/docker-mailserver/rspamd/example.com/ed25519.private";
selector = "dkim-ed25519";
}
]
}
example.org {
selectors [
{
path = "/tmp/docker-mailserver/rspamd/dkim/example.org/rsa.private";
selector = "dkim-rsa";
},
{
path = "/tmp/docker-mailserver/rspamd/dkim/example.org/ed25519.private";
selector = "dkim-ed25519";
}
]
}
}
Support for DKIM keys using Ed25519
This modern elliptic curve is supported by Rspamd, but support by third-parties for verifying Ed25519 DKIM signatures is unreliable.
If you sign your mail with this key type, you should include RSA as a fallback, like shown in the above example.
DKIM Signing config: check_pubkey = true;
This setting will have Rspamd query the DNS record for each DKIM selector, verifying each public key matches the private key configured.
If there is a mismatch, a warning will be omitted to the Rspamd log (/var/log/supervisor/rspamd.log
).
Restart required
After restarting DMS, outgoing mail will now be signed with your new DKIM key(s)
You'll need to repeat this process if you add any new domains.
RSA Key Sizes >= 4096 Bit
Keys of 4096 bits could denied by some mail servers. According to RFC 6376 keys are preferably between 512 and 2048 bits.
DNS Record
When mail signed with your DKIM key is sent from your mail server, the receiver needs to check a DNS TXT
record to verify the DKIM signature is trustworthy.
Configuring DNS - DKIM record
When you generated your key in the previous step, the DNS data was saved into a file <selector>.txt
(default: mail.txt
). Use this content to update your DNS via Web Interface or directly edit your DNS Zone file:
Create a new record:
Field | Value |
---|---|
Type | TXT |
Name | <selector>._domainkey (default: mail._domainkey ) |
TTL | Use the default (otherwise 3600 seconds is appropriate) |
Data | File content within ( ... ) (formatted as advised below) |
<selector>.txt
is already formatted as a snippet for adding to your DNS Zone file.
Just copy/paste the file contents into your existing DNS zone. The TXT
value has been split into separate strings every 255 characters for compatibility.
<selector>.txt
- Formatting the TXT
record value correctly
This file was generated for use within a DNS zone file. DNS TXT
records values that are longer than 255 characters need to be split into multiple parts. This is why the public key has multiple parts wrapped within double-quotes between (
and )
.
A DNS web-interface may handle this internally instead, while others may not, but expect the input as a single line_). You'll need to manually format the value as described below.
Your DNS record file (eg: mail.txt
) should look similar to this:
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ"
"5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
) ;
Take the content between ( ... )
, and combine all the quote wrapped content and remove the double-quotes including the white-space between them. That is your TXT
record value, the above example would become this:
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB
To test that your new DKIM record is correct, query it with the dig
command. The TXT
value response should be a single line split into multiple parts wrapped in double-quotes:
$ dig +short TXT dkim-rsa._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39" "KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
Troubleshooting
MxToolbox has a DKIM Verifier that you can use to check your DKIM DNS record(s).
When using Rspamd, we recommend you turn on check_pubkey = true;
in dkim_signing.conf
. Rspamd will then check whether your private key matches your public key, and you can check possible mismatches by looking at /var/log/supervisor/rspamd.log
.
DMARC
With DMS, DMARC is pre-configured out of the box. You may disable extra and excessive DMARC checks when using Rspamd via ENABLE_OPENDMARC=0
.
The only thing you need to do in order to enable DMARC on a "DNS-level" is to add new TXT
. In contrast to DKIM, DMARC DNS entries do not require any keys, but merely setting the configuration values. You can either handcraft the entry by yourself or use one of available generators (like this one).
Typically something like this should be good to start with:
_dmarc.example.com. IN TXT "v=DMARC1; p=none; sp=none; fo=0; adkim=4; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"
Or a bit more strict policies (mind p=quarantine
and sp=quarantine
):
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"
The DMARC status may not be displayed instantly due to delays in DNS (caches). Dmarcian has a few tools you can use to verify your DNS records.
SPF
What is SPF
Sender Policy Framework (SPF) is a simple email-validation system designed to detect email spoofing by providing a mechanism to allow receiving mail exchangers to check that incoming mail from a domain comes from a host authorized by that domain's administrators.
Disabling policyd-spf
?
As of now, policyd-spf
cannot be disabled. This is WIP.
Adding an SPF Record
To add a SPF record in your DNS, insert the following line in your DNS zone:
example.com. IN TXT "v=spf1 mx ~all"
This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox.
After verification, you might want to change your SPF record to v=spf1 mx -all
so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies.
In any case, increment the SPF record's TTL to its final value.
Backup MX & Secondary MX for policyd-spf
For whitelisting an IP Address from the SPF test, you can create a config file (see policyd-spf.conf
) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf
.
Example: Create and edit a policyd-spf.conf
file at docker-data/dms/config/postfix-policyd-spf.conf
:
debugLevel = 1
#0(only errors)-4(complete data received)
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
# Preferably use IP-Addresses for whitelist lookups:
Whitelist = 192.168.0.0/31,192.168.1.0/30
# Domain_Whitelist = mx1.not-example.com,mx2.not-example.com
Then add this line to docker-compose.yml
:
volumes:
- ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf