Exim4: using the connect ACL to improve security


Authorisation for mail happens at the start of the SMTP sequence, and because most of the security and spam checks happen later, the door is somewhat open for attempts at exhaustive decryption. My feeling is that if you’ve tried to hack my server, then I don’t want you back - thank you very much.

I’ve been picking up sites that fail authentication using various firewall patterns and then using the firewall to block them. I am assisted in this because my server is handling email for a very small number of people whose access doesn’t depend on them typing passwords into the system, so on balance, authentication failure is nearly always someone hacking and not someone forgetting their login name and password.

Exim helps, it generates some helpful messages that can be added to the exim4.patterns file in /etc/symbiosis/firewall/patterns.d. Here’s my starting point for rules for that file:

# Catch "TLS Required" messages
\[__IP__\] rejected AUTH LOGIN: SSL or TLS encryption required when authenticating.$

# auth fail
authenticator failed for .*\) \[__IP__\]: 535 Incorrect authentication data.*$

# bad guys whose robots keep coming trying to login when it's not allowed
# this needs +smtp_protocol_error added to log_selector
\[__IP__\] AUTH command used when not advertised.*$

I began to think about adding extra rules into the ‘connect’ ACL, basically to make these people to go away at the point that they knock on the door. I now have some new sections added to this ACL, which is unused in the standard Symbiosis distribution.

  • The first file allows known IPs to avoid further tests in the ACL. The Symbiosis distribution accepts connections from local hosts for this ACL, but access needs to be extended to allow known IP addresses through. I want to allow access to whitelisted IPs and relay hosts which are set up as IP addresses in various files. However, these lists are not spam checked later and I have one regular relay that passes mail in from the outside, and I want to apply local spam checking to mail from it. So I’ve added a new list of IPs which will be allowed past the connect checks, but will still be spam checked. The file can be missing.

  • The second file looks up the IP addresses in the Symbiosis blacklist database, and if this IP is a known bad site who has transgressed more than some threshold, then it blocks it then and there. The rule also creates a log message, which can added to the patterns.d file for exim, so there’s feedback to increase the badness of the IP.

  • The third file applies rate limiting to stop runaway systems from firing connects into the system. I am using 10 messages every 15 minutes, which is probably too low for more than lightly used servers. You do need to establish what’s normal for your system before using this - but I am also using a defer reply here which means that well-behaved mail systems will get their message through.

  • The fourth file looks up the IP in three of the prominent DNSBLs. Yes - this happens later, but later doesn’t block attempts to get passwords, which is what I am most concerned about. I put this in because it was evident that my server was continually being harassed by a well known botnet, whose signature is to put ‘ylmf-pc’ into the HELO command (or it used to, it now seems to using that and random strings). To find out about botnet, put ‘ylmf-pc’ into Google. The botnet tries to do exhaustive decryption of logins and passwords. It’s also the case that Spamhaus have most of the botnet IPs in their database. I began to realise that I was re-inventing Spamhaus by compiling a list of IP addresses that could be easily be eliminated by DNS lookup and I didn’t need to replicate the efforts of Spamhaus. If you want to see if these bots are attacking you, then turn on +smtp_protocol_error in the logging and you will see lots of the ‘AUTH command used when not advertised’. The bots just blast SMTP commands and don’t wait for replies, and Exim doesn’t like that, and I am guessing that this is why this message is triggered.

So that’s an idea of what is going on, let’s look at what is added.

First, I’ve added a new macro file in the setup section, so I can have some central constants. In /etc/exim4/symbiosis.d/00-main, add 21-connect-check:

# Hostlist to inhibit connection checking in
# 10-acl/10-acl-check-connect/20-accept-known
# IPs in the file will not be checked in the connect check,
# but will be checked for spam when the rcpt ACL is checked
hostlist   whitelist_connection_check = ${if exists{ETC_DIR/exim4/whitelist_connect_hosts}{net-iplsearch;ETC_DIR/exim4/whitelist_connect_hosts}}

# Symbiosis database check incident threshold
# see 10-acl/10-acl-check-connect/30-check-symbiosis-db

# DNSBL sites to check
# in 10-acl/10-acl-check-connect/60-checkdnsbls
CHECK_CONNECT_IP_DNSBLS = zen.spamhaus.org : b.barracudacentral.org : bl.spamcop.net

I’ve also moved the logging selector into a new file, 25-logging in the same 00-main directory.

  # Set logging values
  # can only appear once
  log_selector = +tls_sni +smtp_protocol_error

If you’ve followed Bytemark instructions ‘Enabling SNI for Exim on Symbiosis’ then you will have added a log_selector line there, and you need to comment it out, it can only occur once in the Exim config.

Having set things up, we can move into /etc/exim4/symbiosis.d/10-acl/10-acl-check-connect. Here’s the first file - 20-accept-known:

  # Allow whitelisted ips, known hosts and private addresses to be accepted
  accept  hosts = : +whitelisted_hosts_by_ip : +whitelist_connection_check : +relay_from_hosts : +private_addresses 

This adds the new whitelist_connect_hosts, and it doesn’t matter if it doesn’t exist. Having eliminated known good guys, we can check the Symbiosis database. (Warning this code section is long, so prepare to scroll the box).

  # Use of the sqlite3 database /var/lib/symbiosis/firewall-blacklist-count.db
  # this database has the history of all the bad ips the machine has seen and
  # a count of all the incidents that have been discovered

  # Note on threshold for incident count 
  # see 00-main/21-connect-check
  # ipv4 addresses are in the database 'as-is'
  deny  message = Blacklisted: Denied access - history of unwanted activity
	condition = ${if isip4{$sender_host_address}{true}{false}}
	# perform lookup, return 0 on failure
	set acl_m9 = ${lookup sqlite {/var/lib/symbiosis/firewall-blacklist-count.db \
	                select sum(count) from blacklist where ip = '$sender_host_address' group by ip; } \
		        {$value} {0} }
	condition = ${if >{$acl_m9}{SYMBIOSIS_INCIDENT_THRESHOLD} }

  # Lookups for ipv6 addresses need to be converted to /64 before lookup up
  # these addresses are stored as /64 normalised address with a trailing /64
  # simplest way of doing this is to truncate the address we have and use a like lookup in sqlite3

  # sq regex looks for two sections
  # first part of the ip address - four sets of possibly zero to four characters 0-9a-f terminated by :
  # second part is not captured
  # probably could be a more compact regex, but KISS - avoiding write-once, read-never

  deny  message = Blacklisted: Denied access - history of unwanted activity
	condition = ${if isip6{$sender_host_address}{true}{false}}
	# exim has a full ipv6 address - and we don't want that, normalise it
	set acl_m9 = ${ipv6norm:$sender_host_address}
	# lose the second half of the address
	set acl_m9 = ${sg {$acl_m9}{\N^([0-9a-f]{0,4}:[0-9a-f]{0,4}:[0-9a-f]{0,4}:[0-9a-f]{0,4}:)(?:.*)$\N}{\$1}}
	# perform lookup, return 0 on failure
	set acl_m9 = ${lookup sqlite {/var/lib/symbiosis/firewall-blacklist-count.db \
	                select sum(count) from blacklist where ip like '${acl_m9}%' group by ip; } \
		        {$value} {0} }
	condition = ${if >{$acl_m9}{SYMBIOSIS_INCIDENT_THRESHOLD} }

You may need to adjust the SYMBIOSIS_INCIDENT_THRESHOLD in the setup file for your own use. I do find that many offenders do return but often days later. I use the message in the patterns file to increase the badness level of the IP, and I am running my script which looks at what’s happening in the firewall and feeds back into the system ensuring that repeaters are blocked (Improving Symbiosis Firewall. They are persistent, I have two IPs that have been trying every hour since late December, these are retained in the firewall by my update script that checks iptables and registers attempts to get through.

Having dealt with bad guys in the database, here’s the ratelimit code, added as 50-ratelimit in /etc/exim4/symbiosis.d/10-acl/10-acl-check-connect.

  # Log all sender's rates
  #warn ratelimit = 0 / 15m / strict
  #     log_message = Sender rate $sender_rate / $sender_rate_period

  # Defer anyone else
  defer message = Sender rate exceeds system limits, please try later
  	ratelimit = 10 / 15m / strict
	log_message = Sender deferred $sender_rate / $sender_rate_period

If you install this, I suggest that you comment out the defer and enable the warn, which gives you a number for all connects to your server. You’ll get an idea of what is normal for you. I’ve chosen a 15 minute period, because that’s what the standard Symbiosis firewall checks use. This gets exercised a lot by the botnet, who tend to come in very quickly, get picked up by Exim spraying ‘AUTH command used when not advertised’ messages into the log, and then being junked by the DNSBL rule below. They are paused by this ruleset, and after a couple of defers they tend to give up.

Finally here’s the DNSBL setup, add this to 60-check-dnsbls:

  # Seems to me that I don't need to compile a list of
  # IPs that are already known by the various DNSBL lists

  # However I do want to get them before they test the authentication systems
  # See 00-main/21-connect-check for the macro for CHECK_CONNECT_IP_DNSBLS

  deny message = $sender_host_address is blacklisted at $dnslist_domain.\n\
                 Please see $dnslist_text
       dnslists = CHECK_CONNECT_IP_DNSBLS

I am using Spamhaus, Barracuda and SpamCop here. Spamhaus get most of the bad IPs, but Barracuda and SpamCop get some hits. If you use Barracuda, they ask you to sign up and tell them what IPs you’ll be calling from. It’s free - so please do. Of course, this now applies to all the domains on your server, and it loses the ability to specify the check on a per site basis.

Finally you can add three new lines to the pattern file: /etc/symbiosis/firewall/patterns.d/exim4.patterns, which feed miscreants back into the firewall system.

# returning bad guys being blocked - here to generate an entry in the database
\[__IP__\] .*Blacklisted: Denied access - history of unwanted activity.*$

# bad guys whose robots keep coming trying to login when it's not allowed
# this needs +smtp_protocol_error added to log_selector
\[__IP__\] AUTH command used when not advertised.*$

# Sender deferred ratelimiting
\[__IP__\] .* Sender deferred.*$

I am not sure that the last pattern is really needed, but I have it there for now.

Once you’ve added all these files to symbiosis.d, back up to /etc/exim4 and use ‘make test’ to check syntax, before using ‘make’ to install it all.

I’ve put all the files into a tar file - see https://cloudy.hillside.co.uk/firewall/hillside-exim.tar.gz.

Fwalldb: a tool to inspect the Symbiosis blacklist database