Friday, June 29, 2012

Exim with a whitelist of recipients

At IOLA, we have a couple of servers that we use for a lot of different things - just not running live code.

Now, with a standard install of Linux with something like monit installed to respawn dead processes, whenever there's a problem a local mail server is used to send an email about it. That's a good thing because it means you never hear from the server unless it's got a problem, so you don't have to monitor it at such, except for reachability.

If you're on Debian, you'll get Exim installed as the default mail server, together with a semi-scary, semi-friendly series of debconf questions to configure it. Unless you configure one of the questions, it's locked down so it won't accept connections from the outside.

So if you remember to configure your real email address as an alias for the local users (e.g. with a root: my-email-adresse@example.com in /etc/aliases/) things actually work quite well.

The only problem is when you actually run a web site project on the server, either for development or as a test instance before putting it live. It's not uncommon for a web site to have some corners where it's sending email. Which you definitely don't want to be able to happen by accident during testing.

So in the past, we've configured Exim to not deliver any mail remotely. No accidents possible. But this means we never got any problem reports from the server either.

What we really want is to be able to whitelist a number of adresses that Exim can freely deliver email to, the rest should be discarded.

One way to do that on Debian is to select the split configuration, then put these lines in the file /etc/exim4/conf.d/rewrite/01_rewrite-everything-but-whitelisted_LOCALCHANGE:
my-email@example.com * T
some-other-email@example.com * T
*@* thisaddressdoesnotexist@unrouteabledomain T
This means: skip all rewriting of a To envelope address of my-email@example.com and some-other-email@example.com but rewrite all other To envelope addresses to thisaddressdoesnotexist@unrouteabledomain.

The latter is of course not routeable so will fail and end up in the queue until Exim gives up. This is a bit ugly, so you can add the following lines to a new router in /etc/exim4/conf.d/router/01_drop-unrouteable_LOCALCHANGE:
drop_unrouteabledomain:
  driver = redirect
  domains = unrouteabledomain
  data = :blackhole:
This will match the domain unrouteabledomain and discard everything sent to that right away.

If you're not on Debian, the rewrite rule needs to go in the begin rewrite section of exim.conf and the router rule in the begin routers section.

I'm quite happy with this setup, it's actually a problem that has been bugging me for years. At some point I made a hacky Python script called from cron that would check the local mail and forward it through SSH to another machine with a mail server allowed to send remote email, but that turned out to be a error-prone and high-latency idea.