Author: Mike Tubby Date: To: exim-users Subject: Re: [exim] SPF and DKIM error processing when receiving emails
On 06/11/2020 11:53, Mark Elkins via Exim-users wrote: > I've got the following in exim.conf....
>
> acl_check_dkim:
> deny dkim_status = fail
> message = DKIM validation failed: $dkim_verify_status
> log_message = DKIM validation failed: $dkim_verify_status \
> (address=$sender_address, domain=$dkim_cur_signer), \
> signature is bad
> defer dkim_status = invalid
> message = DKIM signature invalid: $dkim_verify_status
> log_message = DKIM signature invalid: $dkim_verify_status \
> (address=$sender_address, domain=$dkim_cur_signer), \
> invalid signature
> # NOTE: dkim_status = none should never happen in this ACL
> accept
> # Add an X-DKIM header to the message
> add_header = :at_start: X-DKIM: DKIM validation passed: \
> (address=$sender_address domain=$dkim_cur_signer), \
> signature is good
> logwrite = DKIM validation passed
>
> This is breaking some of my customers...
> How can I soften the blow? - so they can get their incorrectly signed
> emails from these broken servers (some of which live in my countries
> banking system and are otherwise completely valid)
>
> I've got something similar for SPF....
>
> # SPF Checks
> acl_check_mail:
>
> # SPF validation
> deny spf = fail : softfail
> message = SPF validation failed: \
> $sender_host_address is not allowed to send mail from \
> ${if def:sender_address_domain \
> {$sender_address_domain}{$sender_helo_name}}
> log_message = SPF validation failed\
> ${if eq{$spf_result}{softfail} { (softfail)}{}}: \
> $sender_host_address is not allowed to send mail from \
> ${if def:sender_address_domain \
> {$sender_address_domain}{$sender_helo_name}}
> deny spf = permerror
> message = SPF validation failed: \
> syntax error in SPF record(s) for \
> ${if def:sender_address_domain \
> {$sender_address_domain}{$sender_helo_name}}
> log_message = SPF validation failed (permerror): \
> syntax error in SPF record(s) for \
> ${if def:sender_address_domain \
> {$sender_address_domain}{$sender_helo_name}}
> defer spf = temperror
> message = temporary error during SPF validation; \
> please try again later
> log_message = SPF validation failed temporary; deferred
> # Log SPF none/neutral result
> warn spf = none : neutral
> log_message = SPF validation none/neutral
>
> # Use the lack of reverse DNS to trigger greylisting. Some people
> # even reject for it but that would be a little excessive.
>
> warn condition = ${if eq{$sender_host_name}{} {1}}
> set acl_m_greylistreasons = Host $sender_host_address \
> lacks reverse DNS\n$acl_m_greylistreasons
>
> accept
> # Add an SPF-Received header to the message
> add_header = :at_start: $spf_received
> logwrite = SPF validation passed
>
> I think I have to allow this sort of stuff through for now - but would
> love it to come through with readable error messages for now - give
> people a chance to fix their errors.
>
Many suggest that DKIM be used as part of wider DMARC and with layered
policy and the equivalent of 'soft fail' etc. however I developed my
DKIM support and processing very early on, i.e. early days of DKIM and
before DMARC, hence I have an implementation with our company policy
baked in.
We support several hundred domains and everything has a MySQL backend.
Mail processing is shared between three front-end relay boxes [relay1,
relay2 and relay3] with two on one site and one on a different site
which gives us server, site and power resilience. Relay1 is the primary
and holds the master database rel2 and rely3 replicate from it.
We have the concept of 'known signers' - these are sites (domains) that
we know correctly sign and therefor we can hard enforce, for example:
We have the concept of 'whitelisted domains' - ones we will allow in
with broken DKIM (and potentially other broken stuff).
Our policy can be described:
if you are known good signer - test for good signatures only - drop
everything else
if you are white listed, let you in with bad signatures
if you are neither 'known signer' or 'whitelisted' and have a
signature treat it at face value:
pass => let you in
invalid => can't get public key => defer
fail => reject
if you don't have a signature, let you in
To get a sample of what's going on out there, Relay1 writes back all the
DKIM it sees to the database - this gives us an insight into what's
failing and why. We find house-hold names with misconfigured servers
including Microsoft, Amazon, HSBC, Plenty-of-Fish, Capita and government
departments.
In every case that I have investigated from our capture database there
has been a genuine problem at the other end.
You might care to pick some bits out of my DKIM ACL (below).
DKIM_KNOWN_SIGNERS = select domain from dkim_known_signers where active=1;
domainlist dkim_known_signers = ${lookup
mysql{DKIM_KNOWN_SIGNERS}{${sg{$value}{\\n}{ : }} }}
###
### acl_check_dkim: this ACL is used for checking DKIM
###
#
# acl_m2 set to zero on start for normal/full checks, set to 1 if
white-listed domain
#
acl_check_dkim:
#
# start of DKIM debug message and clear macro
#
#
# Only do MySQL INSERT on Relay1 !!!
#
warn set acl_m_dummy = ${lookup mysql{INSERT INTO dkim_log
(status,reason,host,domain,identity,selector,algo) VALUES
('${quote_mysql:$dkim_verify_status}',
'${quote_mysql:$dkim_verify_reason}','${quote_mysql:$sender_fullhost}',
'${quote_mysql:$dkim_domain}', '${quote_mysql:$dkim_identity}',
'${quote_mysql:$dkim_selector}', '${quote_mysql:$dkim_algo}' )}}
log_message = DKIM START: domain=$sender_address_domain
possible_signer=$dkim_cur_signer status=$dkim_verify_status ${if
def:dkim_verify_reason {(reason=$dkim_verify_reason)
#
# strict checking on known signers...
#
deny sender_domains = +dkim_known_signers
dkim_status = none:invalid:fail
message = Message from $sender_address_domain (known
signer) with invalid or missing signature
log_message = DKIM DENY: Rejected
$sender_address_domain is known signer (in database) but has
invalid/missing signature
accept sender_domains = +dkim_known_signers
dkim_status = pass
log_message = DKIM PASS: Accepted
$sender_address_domain is known signer and has good signature
add_header = :after_received:X-DKIM-Result:
Domain=$sender_address_domain Result=Good and Known Domain
#
# ignore noise where we have no signature
#
accept dkim_status = none
# log_message = DKIM SKIP: Skipping DKIM checks - no
signature for: $dkim_cur_signer
#
# skip DKIM if domain whitelisted for DKIM, i.e. known good
domain that has broken DKIM
#
accept sender_domains = +dkim_whitelist_domains
log_message = DKIM SKIP: Skipping DKIM checks for
whitelisted domain: $sender_address_domain
set acl_m2 = 1
#
# skip DKIM checks on hosts we relay for
#
accept hosts = +relay_from_hosts
log_message = DKIM SKIP: Skipping DKIM checks for relay
host: $sender_fullhost
#
# skip DKIM checks on authenticated hosts (that we also relay for)
#
accept authenticated = *
log_message = DKIM SKIP: Skipping DKIM checks for
authenticated host: $sender_fullhost
#
# defer when message not testable, e.g. can't get public key, etc.
#
defer dkim_status = invalid
message = Message from $sender_address_domain cannot be
verified
log_message = DKIM DEFER: domain=$sender_address_domain
cannot obtain public key