Categories
OS tricks

Exim Configuration Zen

Alright. So I’m administering this Debian virtual machine at Bytemark which runs as a mailserver for me and some friends. Having it in a vhost gives us a reasonable level of security against power-failuers and hardware crash. And especially at that price. It’s a great deal, and great service.

One of the reasons we pay money for the virtual host is to be able to run some decent spam filtering, and have a shell account for checking mail. Additionally we’re all a bunch of geeks that have our own domains and won’t settle for a simple webmail/pop/imap interface, noooo, we need procmail and pine too. 😉

Me beeing the spam-fighter I am, and the ubergeek, took on the responsibility of configuring the server. And noone else will gets the root password. I’m in control! 😉 This will be a post about my current config, and some of the tweaks I needed to do before I was satisfied. I didn’t understand the mindset of Exim at all in the beginning, but things are more understandable now. BTW, I’m running Exim4.

As I said I’m running Debian (testing), so most software setup was done by APT. Easy peasy. This will be instructions for the integration.

Virtual hosts
Since we’re a bunch of people with separate domains we needed to set up virtual-hosts that allowed each user to edit his own alias file for that domain. First step were the domain files. Each domain has it’s own file for lookup that’s stored in /etc/mail/virtual/$domainname . It contains a key-value pair that looks like:

dude:happytom

Where the key is the localpart of the emailadress dude@domain.no, and happytom is the real user account. You can also have entries like:

dude2:happytom@otherdomain.no

Which would forward the mail to another domain. Plain and simple really. The magic to make this work was the following director:

virtual:
  driver = redirect
  domains = dsearch;/etc/mail/virtual
  data = ${lookup{$local_part}lsearch{/etc/mail/virtual/$domain}}

What this does is first search for a file named the same as the domain, and then use this file for looking up the localpart key. No big magic.

Spamfiltering
The virtual host setup was a breeze really. I found the code somewhere on the web, but don’t remember where. I just copied it and it worked. Now for the spamfiltering: Spamassassin is quite common, so I decided to try that out. This turned out to be a bit tricky.

Installing spamassassin was easy thanks to the Debian package system (APT). It was the Exim integration that was hard. First off, if you’re not familiar with spamassassin here’s a few basic things:

  • It runs as a daemon
  • A small program called spamc interfaces this daemon to get mail scanned and uses the result to report back to exim
  • When Spamassassin scans a mail it can use the system defaults
  • If the option is enabled it can use the users private settings
  • When scanning it always attempts to use the users private Bayesian training files

Spamassassin was working quite fine with some code I found on the web, or at least I thought. A problem was that it didn’t correctly identify which user the mail were to, and therefore used the root settings for all mails scanned. The reason it didn’t identify the correct user was that it was trying to use the localpart of the email as the username, while the virual host system meant that most of the time the localpart did not equal the local user. This also meant training of the Bayesian part was impossible for the users, and that they could not alter their settings. So I went to work, there had to be some way to figure this out. What came out is the following Exim router and transport:

Router:

spamcheck_router:
  no_verify
  domains = dsearch;/etc/mail/virtual
  local_parts = passwd;$local_part : lsearch;/etc/mail/virtual/$domain
  condition = "${if and { {!eq {$received_protocol}{spam-scanned}} 
         {!eq {$received_protocol}{{local}} } {1}{0}}"
  driver = accept
  transport = spamcheck
  pass_on_timeout

Transport:

spamcheck:
  debug_print = "T: spamassassin_pipe for $local_part@$domain"
  driver = pipe
  command = /usr/sbin/exim4 -oMr spam-scanned -bS
  transport_filter = /usr/bin/spamc -u "
          ${lookup {$local_part}lsearch{/etc/passwd}{$local_part}
          {${lookup{$local_part}lsearch{/etc/mail/virtual/$domain}}}}"
  use_bsmtp
  home_directory = "/tmp"
  current_directory = "/tmp"
  user = Debian-exim
  group = Debian-exim
  return_path_add = false
  log_output = true
  return_fail_output = true
  message_prefix =
  message_suffix =

What this does is check if the localpart equals a user in the passwd file, if not it checks the virtual host configs for the correct username. For those of you familiar with Exim you probably wonder why I just didn’t put the virtual domain router before the Spamassassin so it would lookup the username and change delivery before it reached Spamassassin. Well, in the case of the virtual domain setup forwarding to another domain, Spamassassin wouldn’t be run on those mails at all. What happens now is that it can’t find a valid user for those addresses (they resolve to something like user@otherdomain.com) but are still scanned by Spamassassin as the user the daemon is running under. This is good, cause all the other people using our domains but with accounts elsewehere, also wants their mails scanned.

Antivirus
ClamAV seems to be a fairly good and free virus scanner. In with the clamav-daemon packages and off we go.

Integration with exim is done with the exiscan-acl patch for Exim. Luckily Debian has a package (exim4-daemon-heavy) which is already patched with it, so there’s no problem there. You could also probably integrate it through a transport, but the advantage of this integration is that mail is scanned as it is received, and then rejected at SMTP time. This actually means that ClamAV isn’t one of those pesky AV packages that sends a mail to the address in the From field, it only tells the delivering server that the mail wasn’t delivered because of an virus. I’m not sure if the From: address will get a bounce message though. Pretty pesky those too.

Then we modify Exim settings to include (ClamAV running local thorugh UNIX sockets):

av_scanner = clamd:/var/run/clamav/clamd.ctl

Then the ACL config needs some options:

deny message = This message contains a virus or 
        other harmful content ($malware_name)
     demime = *
     malware = *

Checked out my /var/log/clamav log, and hey. There it was, already rejecting virus-mails! 🙂

Alright. That’s it. This is mostly a scrapbook for myself to remember the config for later, but if anyone finds it useful it’s great.