Howto – Exim

Using MailScanner with Exim

How mailscanner works with Exim

From the Exim FAQ:

Accepting and delivering a message are two entirely separate, independent processes, which communicate only by writing/reading the message on the disc.

MailScanner separates these two parts even further, by requiring them to use separate queues. Incoming mail is accepted into one queue, and outgoing mail is sent only from the other queue. The only way mail can get from one queue to the other is through MailScanner.

Since there is no way to tell Exim to use two separate queues in this manner, we have to use two separate Exim processes. Each of these processes must have its own configuration file, so that the spool directories can be different.

To ensure that all mail is scanned, the “accepter” process (which accepts incoming messages from the network, or from the local command line) must be prevented from actually sending any mail out, at least in normal use. This implies that we must use the compiled-in default path for the Exim configuration file for the accepter process. Otherwise local users would evade MailScanner when sending mail using the command line interface to Exim. Don’t forget that many MTAs will also use the command line interface without specifying a path for the configuration file, so this really is a must.

Configuration of Exim

In the examples below I’m going to use the paths in Exim’s default src/EDITME build time configuration. These will probably be different from the paths used by your operating system’s packaged version of Exim, so you’ll need to alter them as necessary.

Start off by copying your working Exim configuration from /etc/exim/exim.conf to /etc/exim/exim_out.conf for use by the outgoing Exim. This file can probably remain unmodified.

You need to make two changes to the default configuration:

– tell it to use a different spool directory

– prevent Exim from immediately delivering messages

Spool directory location

To change the spool directory you need to add this line to the first part of /etc/exim/exim.conf:

spool_directory = /var/spool/exim.in

By default Exim puts its logs in $spool_directory/log/%slog which means that with this MailScanner setup the log lines related to a message’s reception and its delivery will be in different places, which is inconvenient. Your operating system may package Exim to put its logs in a different place, in which case you will not have this difficulty. To fix this problem put this in the first section of both /etc/exim/exim.conf and /etc/exim/exim_out.conf:

log_file_path = /var/spool/exim/log/%slog

There is a similar problem with the exiwhat utility, which relies on a process status log that Exim puts in its spool directory. You can fix this in Exim-4.21 and greater by setting:

process_log_path = /var/spool/exim/exim-process.info

Deferring incoming messages

There are a couple of ways to prevent Exim from delivering messages immediately. The simplest is to add this line to the first part of the configuration file:

queue_only = true

However this allows Exim’s admin users to bypass MailScanner using Exim options such as -q and -M, and if you have prod_requires_admin = false then any user can do this.

In Exim-4.21 and greater you should use an additional setting to make the queue_only option stronger:

queue_only_override = false

Exim 4
If you are using an older version of Exim, you can make a more complicated change that causes all messages to be deferred when Exim tries to deliver them. To do this in Exim 4, add this router immediately after the begin routers line, so that it is the first router:

defer_router:
driver = redirect
allow_defer
data = :defer: All deliveries are deferred
verify = false

This causes all addresses to be deferred so that each message remains on the queue. The verify = false clause means that it is ignored when Exim is checking addresses for validity so the address verification logic ends up being the same with MailScanner as it is without.

Running Exim

As described above, you need to run two Exim daemons: one to listen for SMTP connections, and one to do queue runs on the outgoing spool directory. You will need to modify the Exim startup script (e.g. /etc/init.d/exim) to do this. By default it will contain a line like:

/usr/bin/exim -bd -q15m

You need to change this to:

/usr/bin/exim -bd
/usr/bin/exim -q15m -C /etc/exim/exim_out.conf

The SMTP listener will by default create its pid file in /var/spool/exim.in/exim-daemon.pid, but the queue-running Exim will not create a pid file. You can make it do so by changing its command line to:

/usr/bin/exim -q15m -C /etc/exim/exim_out.conf -oP /var/spool/exim/exim-daemon.pid

You should also have a crontab for Exim that cycles the logs and cleans up the hints databases. You will have to update the database cleaning jobs to clean both the incoming and the outgoing databases. It might not be necessary to clean all the databases for both Exims but it’s simpler to assume that you do. In addition to the standard commands:

/usr/bin/exim_tidydb /var/spool/exim callout > /dev/null
/usr/bin/exim_tidydb /var/spool/exim retry > /dev/null
/usr/bin/exim_tidydb /var/spool/exim reject > /dev/null
/usr/bin/exim_tidydb /var/spool/exim wait-smtp > /dev/null

You also need:

/usr/bin/exim_tidydb /var/spool/exim.in callout > /dev/null
/usr/bin/exim_tidydb /var/spool/exim.in retry > /dev/null
/usr/bin/exim_tidydb /var/spool/exim.in reject > /dev/null
/usr/bin/exim_tidydb /var/spool/exim.in wait-smtp > /dev/null

Instead of running Exim as a daemon, some people run it from inetd for incoming SMTP and cron for queue runs, though this disables some of Exim’s load management features. If you do this then you do not need to change inetd.conf, but you do need to modify the queue running command in the crontab to

/usr/bin/exim -q -C /etc/exim/exim_out.conf

Configuration of MailScanner

MailScanner itself needs to know how to invoke Exim to send mail. It does this to send warning messages to sender, recipients and postmaster when a virus is detected, and to initiate an immediate delivery attempt for a message when it has been placed in the outgoing queue. There are two settings in the MailScanner configuration that tell it how to invoke a mailer (in this case Exim); one for each of these cases.

The “Sendmail” setting is used to send mail that has been freshly created by MailScanner (i.e. warnings). You can use a simple setting such as this:

Sendmail = /usr/bin/exim

However that causes warnings to be re-scanned before being sent out. To bypass this you can set:

Sendmail = /usr/bin/exim -C /etc/exim/exim_out.conf

You might also like to get Exim to mark messages that have been generated by MailScanner in the log like this:

Sendmail = /usr/bin/exim -oMr MailScanner

The “Sendmail2” setting is used to initiate a delivery attempt for a message that has just been scanned. It defaults to being the same as the “Sendmail” setting, but you need to tell Exim to use the outgoing configuration:

Sendmail2 = /usr/bin/exim -C /etc/exim/exim_out.conf

MailScanner also needs to be told where the Exim incoming and outgoing spool directories are. In the simple case these settings will work; note that MailScanner needs to be explicitly told the input subdirectory which is implicit in the Exim configuration.

Incoming Queue Dir = /var/spool/exim.in/input
Outgoing Queue Dir = /var/spool/exim/input

If you have split_spool_directory in your Exim configuration the configuration is slightly different:

Incoming Queue Dir = /var/spool/exim.in/input/*
Outgoing Queue Dir = /var/spool/exim/input
Split Exim Spool = yes

The latter option is near the bottom of the default MailScanner.conf. You need to ensure that all the spool directories and subdirectories are created before starting MailScanner for the first time.

You will also need to specify the Exim user, which will typically be:

Run As User = exim
Run As Group = exim

One Exim configuration file

If you do not need to scan locally-generated mail (e.g. because you don’t have any users on the machine so locally-generated mail only comes from cron and MailScanner) then it is possible to have a setup with only one Exim configuration file by using a macro trick. You only need to make a small addition to the configuration file:

SPOOL = /var/spool/exim
spool_directory = SPOOL

The first line sets a macro which can be overridden on the command line. It should be set to the default spool directory so that in the normal case the lines have no effect. As explained above you may also want to set log_file_path and process_log_path. That is all you need to do to /etc/exim/exim.conf.

The other changes you need to make are in how you run Exim. In the startup script you should put:

/usr/bin/exim -bd -odq -DSPOOL=/var/spool/exim.in
/usr/bin/exim -q15m

The first line uses -odq to turn on the queue_only option, and specifies the location of the incoming spool directory by overriding the SPOOL macro setting. The second line is as before, but there’s no need for an alternate configuration file.

Similarly in MailScanner.conf you don’t need the -C option, so you can simply set:

Sendmail = /usr/bin/exim
Sendmail2 = /usr/bin/exim

The rest of the description above remains the same, e.g. the optional use of -oMr and -oP, etc.

If there is any software running on the machine that generates email that should be scanned (e.g. a webmail application) you can still use this setup, but you must configure the application either to submit email via SMTP, or to use the extra command line options -odq -DSPOOL=/var/spool/exim.in. You will have to balance the inconvenience of two Exim configuration files against the inconvenience of configuring everything that submits email in a peculiar manner.