Mailing List Archive

Full SPF/DKIM/DMARC validation policies for Exim on CentOS 8 (feedback please)
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

# 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@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
Re: Full SPF/DKIM/DMARC validation policies for Exim on CentOS 8 (feedback please) [ In reply to ]
Hi, Adrian -

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.

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.

Cheers,
Mike B-)

On Sun, 12 Jul 2020 at 23:09, Adrian (Aad) Offerman via Exim-users <
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
>
> # 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@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
>
> --
> ## List details at https://lists.exim.org/mailman/listinfo/exim-users
> ## Exim details at http://www.exim.org/
> ## Please use the Wiki with this list - http://wiki.exim.org/
>


--
*My normal working days are Tuesdays, Wednesdays and Thursdays.*

Systems Administrator working in Teaching & Learning
IT Services, University of York, Heslington, York YO10 5DD, UK
Tel: +44-(0)1904-323811
Email Disclaimer: www.york.ac.uk/about/legal-statements/email-disclaimer/

Web: www.york.ac.uk/it-services
Disclaimer: www.york.ac.uk/docs/disclaimer/email.htm
--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Full SPF/DKIM/DMARC validation policies for Exim on CentOS 8 (feedback please) [ In reply to ]
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@exim.org <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@example.com
> <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
Re: Full SPF/DKIM/DMARC validation policies for Exim on CentOS 8 (feedback please) [ In reply to ]
To answer my own questions here: you are supposed to change the '-all'
ending of your SPF TXT record into '~all', which means that a failure to
validate results in a softfail, allowing DMARC validation to start/continue.


On 07/14/2020 12:43 PM, Adrian (Aad) Offerman wrote:
>
> 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?
>
>
>> On Sun, 12 Jul 2020 at 23:09, Adrian (Aad) Offerman via Exim-users
>> <exim-users@exim.org <mailto:exim-users@exim.org>> wrote:
>>
>> 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.