Some simple spam rejection with exim4 ACLs

I regularly receive email from a few persistent spammers who don't get caught by blackhole lists or Bayesian filters. Fortunately the spam I receive from them has enough consistent features that it can be rejected during the SMTP session at RCPT time, before the message content has been transferred. In an ideal world, the repeated failed deliveries would result in my address being removed from the spammers’ mailing lists. This is unlikely to happen in reality, but at least rejecting the mail early means that they’re wasting fewer of my resources.

Useful references: Exim manual, chapter 9: file and database lookups; Exim manual, chapter 43: access control lists.

Here are the relevant ACL configuration statements, which in turn refer to four external blacklist files:

# Use the following rules whenever an SMTP RCPT command is received.


# Reject mail from hostnames on the domain blacklist.
# We use nwildlsearch for a wildcarded, single-key lookup without
# string expansion, and the *@ to do an additional search with the
# left-hand part of the key replaced by "*".

deny    domains       =
        hosts         = nwildlsearch*@;ETC_DIR/exim4/blacklist/spam_hosts
        message       = Mail rejected

# Reject mail from IP addresses on the domain blacklist.
# We use net-iplsearch to search for the host's IP address rather than
# its name.

deny    domains       =
        hosts         = net-iplsearch;ETC_DIR/exim4/blacklist/spam_ips
        message       = Mail rejected

# Reject mail from sender addresses on the domain blacklist.

deny    domains       =
        senders       = nwildlsearch*@;ETC_DIR/exim4/blacklist/spam_senders
        message       = Mail rejected

# Reject mail to specified addresses on the domain blacklist.

deny    domains       =
        recipients    = nwildlsearch*@;ETC_DIR/exim4/blacklist/spam_recipients
        message       = Mail rejected

All of the configuration stanzas restrict the filtering to a chosen recipient domain. They also specify rejection messages; in the example these are the entirely generic Mail rejected. The rejection message may be anything. Options include the honest Rejected as spam (helpful for the sender in the case of a false positive) and the mendacious Invalid recipient address, which might (very) optimistically be hoped to increase the chance of an address being removed from the spammer’s mailing list.

The spam_hosts file is a list of host names, with optional leading * wildcards. Any mail originating from one of these hosts is rejected. Here is an example of the format:

The spam_ips file works like spam_hosts, but for IP addresses. A sample:

The spam_senders file lists (optionally wildcarded) source addresses. Note that these are not the From: address specified in the headers, but the (frequently different) address given by the MAIL command, which usually shows up as the envelope-from address in an email’s Received: header. (The From: address is not available at RCPT time.) An example spam_senders file:

Note that the * wilcards only work for the leading part of the address. For any other wildcarding – for example, ignoring the trailing part of the address – we have to use a regular expression (cued by starting the line with ^).

The spam_recipients lists recipient addresses for which mail will be denied. An example:

The spam_recipients list is useful if is configured with a ‘catch-all’ address; if exim is configured only to accept mail to specific known addresses at, the spam_recipients list is of course redundant.

Populating the blacklist files themselves was a gradual, iterative process of adding new hostnames, IPs, etc. whenever something got through the existing blacklists. After about a month of occasional additions, things stopped getting through. There’s a little judgement involved in deciding which parameters to filter; for instance, one of the spammers seems to use a fresh hostname for every attempt, but they only have a limited selection of IPs and From addresses.