How I do per-address blocklists with Exim
Yesterday I wrote about the power of per-address blocklists. This is all well and good, but part of the challenge of this is actually implementing them. We use Exim, so my implementation is for that (and it's not entirely original with me; I believe I copied much of this approach from an ex-co-worker).
Since we want to reject at SMTP time, we need to do this in one of
Exim's SMTP ACLs. Since we want to reject on a per-address basis,
this has to go in the RCPT TO
ACL. Exim has good general support
for blocklists based on IP origin, MAIL FROM
, and so on, so the
important trick is to figure out how to easily support per-address
ones. We use the simplest brute force approach: a directory hierarchy.
# The top level directory for per-local-address blocks. BLOCKSDIR = CFGDIR/blocks # The per-user directory. Using $local_part is safe # because we restrict this to valid addresses. UBLOCKDIR = BLOCKSDIR/${lc:$local_part}
Then in the actual RCPT TO
ACL, we need some rules to match against
this. Assuming that you have already rejected any non-local, non-valid
addresses:
# Actual per-address block files for hosts and senders are in UBLOCKDIR: # UBLOCKDIR/hosts # UBLOCKDIR/senders deny hosts = ${if exists{UBLOCKDIR/hosts} {+ignore_unknown : +ignore_defer : UBLOCKDIR/hosts}} message = mail from host $sender_host_address not accepted by <$local_part@$domain>. log_message = blocked by personal hosts blacklist. deny senders = ${if exists {UBLOCKDIR/senders} {UBLOCKDIR/senders}} message = mail from <$sender_address> not accepted by <$local_part@$domain>. log_message = blocked by personal senders blacklist.
As I discovered the hard way, the
+ignore_unknown
and +ignore_defer
are very important for
making the host-based blocklist work the way you expect it to.
You could use another naming scheme for how to find the per-address
host and blocklist files, but this one gives you a simple two-level
namespace; you wind up with, say, /var/local/mail/blocks/cks/hosts
.
In theory it is easy enough to make subdirectories for particular
users and then give them to the users to maintain on their own.
Even without that, it keeps things nicely organized and lets you
see at a glance (or at a ls
) which local addresses even theoretically
have some filtering.
Now, I have a confession: I don't know if this is actually secure
enough to allow arbitrary users to directly manipulate their hosts
and senders
files (and our current setup doesn't directly expose
these directories to users). Exim host and address list file
expansion
allows people to insert anything that can normally occur in host
and address lists, and this is quite powerful. Probably you don't
want to allow users to directly edit these files but instead force
them to go through some interface or preprocessing step that limits
what they can do. At the same time, giving people as much power here
as is safe is nice, because you can do a lot of handy things with
wildcards and even regular expressions.
(Locally, we have one persistent spammer that hits one of our administrative addresses using changing domains with a pattern that we wound up writing a regular expression to match. I was very happy to discover that we could actually do this, even in a host list read from a file; it was handy to make the spammer go away.)
PS: If you put this in the RCPT TO
ACL before you verify that the
local part is a valid username, you'll want to pre-filter things so
that you block local parts with 'dangerous in filename' characters
like ..
and /
.
|
|