Re: [exim] Full SPF/DKIM/DMARC validation policies for Exim …

Top Page
Delete this message
Reply to this message
Author: Adrian (Aad) Offerman
Date:  
To: Mike Brudenell, Exim Users
Subject: Re: [exim] Full SPF/DKIM/DMARC validation policies for Exim on CentOS 8 (feedback please)
Hi Mike,

On 07/14/2020 12:38 AM, Mike Brudenell wrote:
>
> Looking at the extract you posted, I think you are denying (rejecting)
> messages for which either SPF or DKIM verification fails? (For example,
> you're using *deny* used in the acl_check_mail ACL to refuse the MAIL FROM.)
>
> If so (it's late, and my tired eyes might be misreading things!), you
> may want to review and reconsider this as it is working against the
> ethos of DMARC, which passes if either SPF, DKIM or both pass. That is,
> DMARC passes even if one (bot not both) of SPF or DKIM fail.


Nothing wrong with your tired eyes :) I think you're absolutely right
from a DMARC-only perspective: using rigid SPF and DKIM validation both
before DMARC validation will reject messages that validate on only one
of SPF and DKIM, but would be accepted under DMARC validation (assuming
the additional display name alignment checks out).

That would plea for configurations (using switch variables) in which not
all three of SPF, DKIM and DMARC are enabled, e.g.
- SPF
- DKIM
- SPF + DKIM
- SPF + DMARC (= SPF + alignment)
- DKIM + DMARC (= DKIM + alignment)
- DMARC (directly, instead of contributing to a spam score)

The only combination that doesn't make sense is SPF + DKIM + DMARC, like
you say, as DMARC will never be validated if SPF or DKIM is invalid (and
the message rejected already).


> For example, our University is very selective about which external
> service providers we add to our SPF record because of the risk changes
> they make could invalidate it. Instead we tend to rely solely on
> messages third-party service providers send out on our behalf being
> correctly DKIM-signed. That is fine and perfectly allowable under a
> DMARC policy. However I think your configuration is going to reject such
> messages because it sees SPF fail, without then going on to also check DKIM.


Ah, so instead of aiming for the maximum possible, i.e. SPF, DKIM and
DMARC all being valid, you count on DMARC's OR-like validation and you
already know that messages sent by these external providers will fail on
SPF, as you only add their DKIM pubkeys and not the sending systems
(SPF) to your zone?
But this scheme will only work for receiving MX gateways if they
validate DMARC fully, not if they use only SPF, or SPF + DMARC -- if I
understand you correctly?

Thanks!
Adrian


> On Sun, 12 Jul 2020 at 23:09, Adrian (Aad) Offerman via Exim-users
> <exim-users@??? <mailto:exim-users@exim.org>> wrote:
>
>     Hello all,

>
>     I've been working on the implementation of SPF/DKIM/DMARC policies in
>     the configuration of Exim on CentOS 8 (8.1). It turns out that
>     everything is there (i.e. all functionality is compiled in in the
>     package that comes with the repository), but there are no policies at
>     all defined in '/etc/exim/exim.conf'.
>     (there are some 'dkim_disable_verify' and 'dmarc_disable_verify'
>     statements where DKIM/DMARC-validation should not take place, i.e. for
>     local/relayed/authenticated hosts; and SPF and DKIM are validated as
>     part of the (external) spam scan)

>
>     Please find below the policies that I've created and tested (checking
>     logs and headers of validating and non-validating messages in a test
>     environment that is, not in a volume/operational environment yet).

>
>     Any feedback on the polices themselves would be great. And maybe these
>     would be a good starting point to get some policies into the Exim
>     configuration that comes with CentOS, probably best with an on/off
>     switch variable.

>
>     Tnx, Adrian

>
>     ________

>
>
>     For SPF validation:

>
>     acl_check_mail:

>
>       # Hosts are required to say HELO (or EHLO) before sending mail.
>       # So don't allow them to use the MAIL command if they haven't
>       # done so.

>
>       deny condition = ${if eq{$sender_helo_name}{} {1}}
>            message = Nice boys say HELO first

>
>       # reject messages from senders listed in these DNSBLs
>       deny dnslists = zen.spamhaus.org <http://zen.spamhaus.org>

>
>       # 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

>
>     ________

>
>
>     For DKIM validation:

>
>     acl_smtp_dkim = acl_check_dkim

>
>     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

>
>     ________

>
>
>     For DMARC validation:

>
>     global:

>
>       # DMARC
>       dmarc_tld_file=/usr/share/publicsuffix/public_suffix_list.dat
>       dmarc_history_file=/var/spool/exim/opendmarc/history.dat
>       dmarc_forensic_sender=postmaster@???
>     <mailto:postmaster@example.com>

>
>
>     added to acl_check_data:

>
>     # DMARC
>     warn    dmarc_status = quarantine
>             !authenticated = *
>             log_message = Message from $dmarc_used_domain failed sender's
>     DMARC policy; QUARANTINE
>             control = dmarc_enable_forensic
>             set acl_m_quarantine = 1
>             # this variable to use in a router/transport
>     deny    dmarc_status = reject
>             !authenticated = *
>             message = Message from $dmarc_used_domain failed sender's DMARC
>     policy; REJECT
>             control = dmarc_enable_forensic
>     warn    add_header = :at_start: ${authresults {$primary_hostname}}

>
>
>     changed in 'localuser' router:

>
>     #transport = local_delivery
>     transport = ${if =={$acl_m_quarantine}{1}
>     {local_delivery_quarantine}{local_delivery}}

>
>
>     added a new transport:

>
>       local_delivery_quarantine:
>         driver = appendfile
>         #file = /var/mail/$local_part
>         directory = /home/mail-quarantine/Maildir
>         user = mail-quarantine
>         home_directory = /home/mail-quarantine
>         current_directory = /home/mail-quarantine
>         maildir_format
>         delivery_date_add
>         envelope_to_add
>         return_path_add
>         group = mail
>         mode = 0660