Mail Scanning With Exim And The Exiscan ACL

With all the spam and viruses circulating the Internet these days, any network admin worth his or her salt will have appropriate filters in place to prevent these irritants from getting to users and customers. My predecessor, unfortunately, was worth far less than that, so my first task upon assuming the role of a systems administrator for a small ISP was to establish a mail filter.

With no previous experience with a mail filtering system, I dug in and started my research. After reviewing open source solutions such as AmaViS and MailScanner and commercial solutions such as Postini and Mail Warden, I settled on Exim with the Exiscan-ACL plugin.

We already had Exim in place on our FreeBSD servers, so the ability to stay with the same system rather than test something new had a lot of appeal. It had been installed a while back for performance and ease-of-use reasons, but had not been upgraded since version 3.36, now long obsolete. I also wanted an open source program if possible, as the fees for a commercial solution would have forced us to increase our service fees, which in turn may have cost us customers.

Exiscan is actually a patch for the Exim MTA (version 4), with installation on most systems requiring use of the patch command, though it is available as an RPM. FreeBSD users will find the Exiscan-ACL patch is already included in the Exim port. While a number of the other open-source filtering solutions are also included in the FreeBSD ports tree, the ability to maintain mail and scanning configuration in one configure file appealed to me.

Exim uses a series of Access Control Lists in the configure file (in FreeBSD, this file is found at /usr/local/etc/exim/configure), a well-commented text file containing all the server settings to be set by the server administrator (see the Exim manual for more information). One such ACL is the acl_smtp_rcpt option, which examines the sending and receiving information of the email message. It is here that messages are rejected if they are included in administrator-defined blacklists, are not permitted relay hosts, and other rules. For example, the following rule rejects mail if the local part of the recipient’s address contains @ or % or / or | or ! (note the use of regular expressions – the colon is a delimiter):

deny local_parts = ^.*[@%!/|] : ^\\.

These ACL rules allow for some rudimentary filtering, but not near enough to handle full spam and virus protection. The Exiscan patch adds its functionality to the acl_smtp_data ACL, which further scans mail during the data transmission portion of the SMTP session. As Exim is performing the scanning and not handing delivering messages into a queue for scanning by a separate program, unwanted mail is refused before it is even accepted, negating the need for bounce messages. For example, if a local user transmits a message with a blocked regular expression, the mail never really leaves his outbox; he instead receives an error message from his mail client (I have also seen rejection messages appear in Norton AntiVirus dialogs on Win32 systems). In the case of remote senders, their MTA will provide a rejection message.

The Exiscan patch has four major features: MIME filtering, spam filtering with SpamAssassin, antivirus filtering, and regular expression bocking.

MIME filtering can be simple or powerful, depending on how detailed the administrator wants to get. There may be a simple list of extensions to block, such as .scr or .pif, or the admin can set up an acl_smtp_mime ACL for finer control, such as blocking specific content types or character sets. If a message includes an illegal attachment or a bad MIME container, it is rejected. Here is an example of a simple MIME rule:

deny message = This message contains an unwanted \
extension ($found_extension)
demime = scr:vbs:bat:lnk:pif:exe:hta

In this example, the deny message is the error presented to the sender by Exim when the message is rejected, with the $found_extension variable notifying the sender which specific attachment the server refuses. The demime line is the list of extensions refused.

Antivirus scanning requires the user have a third-party virus scanner installed on their system. According to the Exiscan website, Exiscan works with several different scanners. We have chosen to use ClamAV locally. As one would expect, if a virus is found in an attachment, the message is rejected. The AV daemon’s IP address (if not local host) and port (if not the default) are specified earlier in the configure file, and the ACL rule is very simple:

deny message = This message contains malware \
malware = *

In this instance, any malware discovered by Clam is rejected and the sender is notified.

Spam filtering is performed with SpamAssassin and the spamd daemon, and a spam score is generated for every incoming message. Exiscan can be configured with two thresholds for this score: a flag threshold and a rejection threshold. If a message is higher than the flag threshold, a header is attached to the message designating it as spam. The recipient can then configure their MUA to deal with these messages as they see fit. Similarly, with a little extra tweaking, Exiscan can also be configured to rewrite the subject line or body of a message for more obvious labeling. If a message receives a higher score than the bounce threshold, then the message is rejected outright.

Using this capability we have been able to configure our mail server to reject the obvious spam and still allow our users the flexibility to manage the rest on their own with rules and filter sets on their mail client. As with Clam, the server address and port are established earlier in the Exim configure file. Its rule is a little more complex:

deny message = This message scored $spam_score points.\
spam = nobody:true
condition = ${if >{$spam_score_int}{63}{1}{0}}

The spam = nobody:true line specifies that spam scanning will be performed for all users. If the message is greater than the bounce threshold, the message is rejected and the sender notified. In this case, we reject if the message scores over 6.3 points in SpamAssassin tests. Note the threshold is multiplied by ten for placement within the rule.

Regular expression blocking gives the sysadmin further control without having to tweak SpamAssassin configure files. Simply put, by using regular expressions to create a blocked string, messages containing those strings will be rejected outright. The following is one of the examples included with Exiscan:

deny message = This message matches a blacklisted \
regular expression ($regex_match_string)
regex = [Vv] *[Ii] *[Aa] *[Gg] *[Rr] *[Aa]

In this case, the word “Viagra” with any mix of case and whitespace appearing in the body of the message is rejected.

Exiscan comes with thorough documentation and sample configurations for each facility to allow users to get up and running quickly. A HOWTO is also available at the Exiscan website. Further support can be found through the Exiscan-users email list; though primarily a low-traffic list, the active users – including Exiscan author Tom Kistner – are quick at providing answers to questions. From what I’ve seen, Kistner is also very responsive in adding functionality and providing bug fixes for Exiscan.

On our network, CPU utilization on a temporary filtering box (a 2.4GHz P4 single proc w/1 GB of RAM) consistently floats between 60-80% with antivirus turned off, but we have high traffic. I have not (yet) found a good traffic monitor, but some simple command line scripts to parse logs show we bounce an average of 7500-8500 messages per hour each day.

Overall, we have been very pleased with the Exim/Exiscan combination in scanning email for our user base of approximately 3500 dial-up, ISDN, and DSL users. Without scanning in place, our technicians received several calls per day from customers complaining about spam. Since implementing Exiscan, calls have been few and far between.

It’s hard to argue with results like that, especially for a free product.