Do you use let's encrypt?





6388 votes ~ 21 comments

 

Validating SPF and DKIM at SMTP-time with exim

Posted by Steve on Tue 4 Aug 2015 at 07:58

Tags: , ,

In our recent articles we've discussed creating SPF-records to avoid spoofed mails, and the creation and setup for DKIM-signing emails, for a similar purpose. Here we'll look at the other side of the coin; performing DKIM and SPF testing on your incoming email.

To recap, briefly:

  • SPF is used to list IP addresses, and hosts, which are permitted to send mail for a given domain.
  • DKIM allows mail-headers to be signed, such that spoofs cannot fake them, and if valid signatures are found (using a public key stored in DNS) you can trust i

Configuring a mail-server can be done in numerous ways, even if you stick to the default MTA that Debian would select, which is exim4. To simplify things somewhat we will assume that you're using the "split config", which involves a bunch of configuration snippets stored beneath /etc/exim4/conf.d/ - The snippets are concatenated to create a complete configuration which the daemon will actually use.

Whenever you make changes to these configuration-snippets you'll need to run two commands:

# update-exim4.conf
# service exim4 restart

(The first command rebuilds the configuration file, the second reloads the service such that the new configuration will be applied.)

SPF-Checking at SMTP-time

Providing you have the exim4-daemon-heavy package installed, as opposed to the exim4-daemon-light variant of exim, as supplied by Debian, you can check SPF records at SMTP-time.

The configuration is very simple, but you do need to install some supporting software before you can enable it:

# apt-get install spf-tools-perl

The spf-tools-perl package provides you with a simple daemon, and a simple application which allows you to test that a given IP address may send mail from a particular domain. If you wish to perform such a test you can do so like so:

# spfquery.mail-spf-perl --ip-address 1.2.3.4 --mfrom steve@example.com
example.com: Sender is not authorized by default to use 'steve@example.com' in 'mfrom' identity (mechanism '-all' matched)

That command showed that the IP address 1.2.3.4 was not permitted to send mail for the domain example.com. By contrast this example shows that the IP address 212.110.179.70 is permitted to send mail from this domain:

# spfquery.mail-spf-perl --ip-address 212.110.179.70 --mfrom steve@debian-administration.org
debian-administration.org: 212.110.179.70 is authorized to use 'steve@debian-administration.org' in 'mfrom' identity (mechanism 'ip4:212.110.179.65/28' matched)

Back on-topic, once you have the appropriate supporting package present you can add the following to the file /etc/exim4/conf.d/main/00_local_macros, creating that file if necessary:

CHECK_RCPT_SPF=true

Once you've done that, and applied the change, you'll find that SPF-failures will be rejected at SMTP-time.

DKIM-Checking at SMTP-time

DKIM-checking requires no additional software to be installed, but it does have a slightly more complex configuration. Edit /etc/exim4/conf.d/acl/00_exim4-config_header, adding the following to the start of the file:

acl_smtp_dkim = acl_check_dkim

All being well this will then be the complete contents:

acl_smtp_dkim = acl_check_dkim

######################################################################
#                       ACL CONFIGURATION                            #
#         Specifies access control lists for incoming SMTP mail      #
######################################################################
begin acl

After this create the file /etc/exim4/conf.d/acl/10_local_dkim_check, with this content:

acl_check_dkim:

      # Deny failures
      deny
           dkim_status = fail
           logwrite = DKIM test failed: $dkim_verify_reason
           add_header = X-DKIM: DKIM test failed: (address=$sender_address domain=$dkim_cur_signer), signature is bad.


      # Deny invalid signatures
      deny
           dkim_status = invalid
           add_header = X-DKIM: $dkim_cur_signer ($dkim_verify_status); $dkim_verify_reason
           logwrite = DKIM test passed (address=$sender_address domain=$dkim_cur_signer), but signature is invalid.

      # Accept valid/passed sigs
      accept
           dkim_status = pass
           logwrite = DKIM test passed
           add_header = X-DKIM: DKIM passed: (address=$sender_address domain=$dkim_cur_signer), signature is good.


      # And anything else.
      accept

This concludes the configuration of SMTP-time DKIM checking, for exim. Once you've updated the configuration, and restarted the service you'll see log-entries like so:

..
2015-08-02 19:34:06 1ZLy5G-0001rA-Lh DKIM: d=googlemail.com s=20120113 c=relaxed/relaxed a=rsa-sha256 [verification succeeded]
2015-08-02 19:34:06 1ZLy5G-0001rA-Lh DKIM test passed
2015-08-02 19:34:06 1ZLy5G-0001rA-Lh <= steve.example.kemp@googlemail.com ..
..

As you can see from the snippet incoming emails will have a new header X-DKIM added to them logging the result, but failures will result in SMTP-time rejection, and the logging will only end up in the mailserver logfile (/var/log/exim4/mainlog).

My only disappointment is that it doesn't seem possible to check DMARC configuration, at SMTP-time, using stock exim, without using a proxy of some kind, or complex and site-specific configuration. I'd certainly appreciate any pointers to such a thing.

 

 


Re: Validating SPF and DKIM at SMTP-time with exim
Posted by Matlib (83.20.xx.xx) on Wed 12 Aug 2015 at 22:00

If you do not really like the perl thing for performance reasons, you may use the native spfquery tool.

The package spfquery has to be installed and the following rules should be added to acl_check_mail:

defer set acl_m_spf = ${run{/usr/bin/spfquery \
                      -ip "$sender_host_address" \
                      -sender "$sender_address" \
                      -helo "$sender_helo_name"}{}{}}
      set acl_m_spf = $runrc
      logwrite      = ++ spf=$acl_m_spf \
                      ip=$sender_host_address \
                      sender=$sender_address \
                      helo=$sender_helo_name
      message       = SPF record for $sender_address \
                      cannot be verified at the moment
      condition     = ${if ={$acl_m_spf}{6}}

deny  message       = SPF policy prohibits sending from \
                      address $sender_host_address
      condition     = ${if ={$acl_m_spf}{3}}
(Note I had to add line breaks due to the code not fitting the screen so they were not tested)

spfquery returns the following rcs:

  • 1 = neutral
  • 2 = success
  • 3 = failure
  • 4 = soft failure
  • 5 = no SPF entry for the domain
  • 6 = NS unreachable -- defer in Exim's terms
  • 7 = error on parsing SPF record(s); functionally equivalent to 5

Now what's really interesting from the practical point of view is the third option, which requires the -all flag to be set in SPF rule. Not many domains have this in fact. I've analysed today's log at the company I work for, and exactly 14 checks reported code 3 out of maybe 100-200 botnet spam messages that went through the system. So the SPF is unfortunately very inefficient way to filter incoming mail, even for botnet spam which is rather simple to filter out by other means. It will never work for professional corporate spammers sending targeted messages to dozens of valid addresses at a time.

Due to bad design of the protocol each SPF query may require several DNS queries (the spfquery tool has default limit of 10). On busy servers that's a lot. Server receiving 10 mails per second may potentially generate 50-100 additional DNS queries per second.

For example the rule for mozilla.com has 2 includes at root level, then 3 + 3 for the 2nd level, that's 7 queries in total ending up with ~all, a soft failure which basically means... accept. And so the whole rule with its 7 queries is completely useless anyway.

I think these are the reasons why so few sysadmins actually bother writing SPF rule for their mail relays let alone checking them.

--
M.

[ Parent | Reply to this comment ]