Exim worksheet


1. Install exim under FreeBSD

There are two approaches you can use:

1a. Using the exim port

We will not use this method:

# cd /usr/ports/mail/exim
# vi Makefile  (optional)
# make
# make install
# make clean

It would install the binary as /usr/sbin/exim and the configuration file in /usr/local/etc/exim/configure

Although it is easy to do, you will be stuck with whatever version of exim the port uses, which may not be the latest version. Also, you won't get a choice as to which features get compiled in (although editing the Makefile does let you turn on some features); and you won't have an opportunity to apply any patches.

So instead, we will compile from source. This method is applicable to all Unix machines, not just FreeBSD.

1b. Compiling from source

First, we are going to create an 'exim' user. This is so that we can build exim to give up its 'root' privileges as soon as possible after starting up, which improves security. The following command creates a user and group 'exim' with uid and gid of 90, or you can use 'vipw' to edit the password file directly.

$ su
# pw useradd exim -u 90 -d /usr/exim
# exit

Next, unpack and apply any patches. You can do all this as a non-root user, i.e. just within your normal home directory

See exim-users archive for a note on the exim-3.22 patch for a segfault when the ignore_target_hosts feature is used.

$ tar -xvzf /path/to/file/exim-3.22.tar.gz
$ cd exim-3.22
$ cat /path/to/file/exim-ignore_target_hosts.patch | patch -p1
...
Patching file src/host.c using Plan A...
Hunk #1 succeeded at 1794.
done.

Now set up files which tell exim how to build. Note - case sensitive - make sure you type Local and Makefile with an initial capital letter.

(we are still within the exim-3.22 directory)
$ mkdir Local
$ cp src/EDITME Local/Makefile
$ vi Local/Makefile

There are various things you can change in Local/Makefile. For our exercise, please make the following changes. Search for each of the settings and adjust it accordingly.

COMPRESS_COMMAND=/usr/bin/gzip                # change
ZCAT_COMMAND=/usr/bin/zcat                    # change
EXIM_UID=90                                   # uncomment + change
EXIM_GID=90                                   # uncomment + change
LOG_FILE_PATH=/var/log/exim/exim_%slog        # uncomment + change
LOOKUP_CDB=yes                                # uncomment
PID_FILE_PATH=/var/spool/exim/exim%s.pid      # uncomment + change
SPOOL_DIRECTORY=/var/spool/exim               # uncomment
SUPPORT_MAILDIR=yes                           # uncomment

Note that because we have told exim to give up its root privileges, we need to specify directories where it can write its log files and pid file. These directories will be owned by user 'exim' so that it will have sufficient privileges to create these files. (Some of these settings can be overridden in the configuration file, but it makes life easier to compile in the values we need)

Note that we will keep the default locations for the exim binaries and configure file, which are /usr/exim/bin/ and /usr/exim/configure

The following lets 'eximon' be built (only if your system is running X11):

$ cp exim_monitor/EDITME Local/eximon.conf

Now build and install exim:

$ make
...
>>> exim binary built

$ su
Password: <root password>
# make install
...
Exim installation complete

Still as root, we need to create a log directory owned by 'exim'. (We don't need to create /var/spool/exim because exim will do that itself the first time it is run)

# mkdir /var/log/exim
# chown exim:exim /var/log/exim
# exit

Now check that the exim binary and configuration file have been installed (a number of utilites are also installed)

$ ls -lR /usr/exim
total 17
drwxr-xr-x  2 root  wheel    512 Apr 18 11:30 bin
-rw-r--r--  1 root  wheel  16294 Apr 18 11:30 configure      <-- config file

/usr/exim/bin:
total 764
-rwxr-xr-x  1 root  wheel    6859 Apr 18 11:30 exicyclog
-rwxr-xr-x  1 root  wheel    2554 Apr 18 11:30 exigrep
-rwsr-xr-x  1 root  wheel  505954 Apr 18 11:30 exim          <-- main program
-rwxr-xr-x  1 root  wheel    8850 Apr 18 11:30 exim_dbmbuild
-rwxr-xr-x  1 root  wheel   24834 Apr 18 11:30 exim_dumpdb
-rwxr-xr-x  1 root  wheel   27124 Apr 18 11:30 exim_fixdb
-rwxr-xr-x  1 root  wheel   12509 Apr 18 11:30 exim_lock
-rwxr-xr-x  1 root  wheel   25475 Apr 18 11:30 exim_tidydb
-rwxr-xr-x  1 root  wheel    6764 Apr 18 11:30 eximon
-rwxr-xr-x  1 root  wheel  101392 Apr 18 11:30 eximon.bin
-rwxr-xr-x  1 root  wheel   21101 Apr 18 11:30 eximstats
-rwxr-xr-x  1 root  wheel    4431 Apr 18 11:30 exinext
-rwxr-xr-x  1 root  wheel    2886 Apr 18 11:30 exiqsumm
-rwxr-xr-x  1 root  wheel    2284 Apr 18 11:30 exiwhat

2. Test the configuration

Exim's configuration file is /usr/exim/configure. A default file is installed which is suitable for a simple installation, e.g. a workstation. To make it work under FreeBSD, you need to make a minor change:

...
local_delivery
  driver = appendfile
  file = /var/mail/$local_part
  delivery_date_add
  envelope_to_add
  return_path_add
  group = mail            <-- UNCOMMENT THIS LINE
# mode = 0660
...

Now we will use this default config file to check that exim is installed properly.

Firstly, use exim's address testing mode, -bt. This will make all the delivery decisions for a given address but not actually perform a delivery. Try testing both remote and local delivery. Before testing local delivery, make sure you have a local account to deliver to - for security reasons, exim will not deliver to 'root'

# /usr/exim/bin/exim -bt b.candler@pobox.com
b.candler@pobox.com
  deliver to b.candler@pobox.com
  router = lookuphost, transport = remote_smtp
  host mxa2.pobox.com [208.210.124.34]  MX=10
  host mxa1.pobox.com [208.210.124.46]  MX=10
  host max3.pobox.com [208.210.124.101] MX=10
  host mxa4.pobox.com [207.106.49.22]   MX=10
  host mxb.pobox.com  [207.8.152.160]   MX=30

# /usr/exim/bin/exim -bt brian
brian@pc1.t1.ws.afnog.org
  deliver to brian in domain pc1.t1.ws.afnog.org
  director = localuser, transport = local_delivery

Next, use the verbose delivery mode, -v to actually deliver a message. Again, try both local and remote.

# /usr/exim/bin/exim -v you@your.domain
From: brian@pc1.t1.ws.afnog.org
To: you@your.domain
Subject: test message

Testing
<Ctrl-D>
... you will see the delivery decision process and the SMTP exchange
LOG: 0 MAIN
   Completed

If there is a problem and you need even more output, you can use '-d1' to '-d9' in addition to '-v' for debugging output.

Next check the log files. Normal deliveries are recorded in the file exim_mainlog; if there are serious configuration errors which cause exim to abort, you may also have an exim_paniclog

# cd /var/log/exim
# ls
# cat exim_mainlog

3. Configure FreeBSD to use exim instead of sendmail

Changing mailer.conf will ensure that any local programs which try to invoke sendmail invoke exim instead. On systems without mailer.conf, you can rename /usr/sbin/sendmail to /usr/sbin/sendmail.old, and install a symlink from /usr/sbin/sendmail to /usr/exim/bin/exim. If you are using uucp, replace /bin/rmail with a symlink to /usr/exim/bin/exim too.

To make it obvious, we can also arrange that exim is started instead of sendmail at boot time (although actually this is not necessary, as once mailer.conf is changed, it will be exim that runs anyway)

/etc/mail/mailer.conf

# Execute exim rather than sendmail
#
sendmail     /usr/exim/bin/exim
send-mail    /usr/exim/bin/exim
mailq        /usr/exim/bin/exim
newaliases   /usr/exim/bin/exim_dbmbuild /etc/mail/aliases /etc/mail/aliases.db

/etc/rc.conf

sendmail_enable="NO"

/etc/rc.local

/usr/exim/bin/exim -bd -q30m

/etc/crontab

We need to ensure that log files are rotated daily, so we set up a crontab entry to do this. By default, 10 days of logs are kept. To change this, edit the exicyclog script and change 'keep=10'

0       0       *       *       *       root    /usr/exim/bin/exicyclog

Add a symlink

/usr/exim/bin is not in the search path. One way to save us having to keep typing '/usr/exim/bin/exim' is to add a symlink into /usr/local/bin, which is already in the default search path.

# ln -s /usr/exim/bin/exim /usr/local/bin/exim

Empty the sendmail queue

If you were doing this on a live machine which was already running sendmail, then you would kill the sendmail daemon and start exim instead. Check that there is no mail left in /var/spool/mqueue. If there is, then you should run /usr/libexec/sendmail/sendmail -q from time to time until the queue is empty.


4. Exim monitoring and maintenance commands

exim -bp

This shows the contents of Exim's mail spool, equivalent to 'mailq'

exim -M messageID [-v]

Force delivery of an individual message. With -v, delivery occurs in the foreground with verbose progress information. Without it, the delivery attempt occurs in the background.

exim -R string [-v]

Attempt another delivery to all destination addresses which contain string, for example:

# exim -R @hotmail.com -v

exim -qff [-v]

Force another delivery attempt on all queued messages, including those which are frozen.

exim -bh 1.2.3.4

Run a spoof SMTP session as if from IP address 1.2.3.4. This is useful for testing whether anti-relaying is configured properly.

# exim -bh 193.0.0.230

**** SMTP testing session as if from host 193.0.0.230
**** Not for real!

220 pc1.t1.ws.afnog.org ESMTP Exim 3.22 #1 Wed, 02 May 2001 16:42:36 +0000
helo wombat
250 pc1.t1.ws.afnog.org Hello wombat [1.2.3.4]
mail from:<>
250 <> is syntactically correct
rcpt to:<b.candler@pobox.com>
550 relaying to <b.candler@pobox.com> prohibited by administrator
quit
221 pc1.t1.ws.afnog.org closing connection

If you need to enable relaying for certain blocks of IP addresses, see host_accept_relay in configure. Do not turn your machine into an open relay!

eximstats logfile

Scan through an exim logfile and generate statistics. You can set up a cronjob to mail statistics to you every day; just make sure it runs a little after exicyclog has run, which means that yesterday's logs will be in exim_mainlog.01. This should be on a single line.

15      0       *       *       *       root    /usr/exim/bin/eximstats
   /var/log/exim/exim_mainlog.01 | mail -s "Exim stats" stats@your.domain

5. Deploying a new configure file

Exercise: change to Maildir delivery

In this exercise, we will change our Exim installation so that it delivers mail into separate files under $HOME/Maildir/ rather than a single mbox file /var/mail/$USER

We will do this by creating a new configuration file, testing it without touching the live configuration file, and when we are happy that it is OK, installing it.

Create updated configuration file

# cd /usr/exim
# cp configure configure.new
# vi configure.new

Find the local_delivery transport, and change it as follows:

local_delivery:
  driver = appendfile
  maildir_format
  create_directory
  directory = $home/Maildir/
  delivery_date_add
  envelope_to_add
  return_path_add
  # with Maildir, we do not add a "From " line, and do not need to
  # convert "From " to ">From "
  prefix =
  suffix =
  check_string =
  escape_string =

exim -C configure.new -bt address

Test routing to address using the new configure file. This should be sufficient to check the syntax of the file, and check that the mail routing is working. Check both local and remote delivery.

exim -C configure.new -v address

Actually perform a delivery using the new configure file. Do this for a number of different test addresses (e.g. local and remote) and check that the deliveries have completed. Use exim -bp to check that the messages have not remained in the queue, and check the log files too.

# exim -C /usr/exim/configurefile.new -v fred
Subject: test message

testing
.
# su - fred
$ ls Maildir/new
988935301.3615.pc1.t1.ws.afnog.org
$ cat Maildir/new/988935301.3615.pc1.t1.ws.afnog.org
... the message
$ exit

(Note: with -C you need to be running as root, otherwise exim runs as the user who started exim. It is a good idea to give the full pathname to the configuration file; this is because exim re-execs itself to perform delivery, and the second exim process could have a different working directory)

Roll in the new configure file

It's a good idea to keep a copy of the old configuration before rolling the new one into place, so that you can back out in case of problems.

# cd /usr/exim
# cp configure configure-20010509
# mv configure.new configure

The running exim daemon will still be using the old configuration file, so you need to send it a HUP signal (-1) to make it reread its configuration.

# cat /var/spool/exim/exim.pid
16379
# kill -HUP 16379

As a final check, make sure the exim daemon is still running. If it is configured to accept incoming mail, then check that it does so:

# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 pc1.t1.ws.afnog.org ESMTP Exim 3.22 #1 Wed, 02 May 2001 17:59:56 +0000
Ctrl-]
telnet> close
Connection closed.

In this example, there is something else we need to do: change all Mail User Agents (MUAs) so that they look for mail in $HOME/Maildir/ rather than /var/mail/$USER. If you are using Mutt, edit /usr/local/etc/Muttrc and include the following line:

set spoolfile="~/Maildir/"

If people collect mail using pop3 then you will need a different pop3d which is capable of reading Maildir format messages. In the next exercise we will install qmail-pop3d for this.


6. An efficient SMTP smarthost

This configuration can be used on a machine acting as a smarthost. It has absolutely no local delivery capability, and as a result it is configured to run without priviledges (i.e. it remains as user 'exim') which enhances security. A number of parameters have been altered to suit a large-scale server, and on a reasonable machine you should be able to handle several hundred thousand messages per day.

This configuration in turn reads two plaintext files which you need to create:

/usr/exim/host_accept_relay

# Allow relaying from localhost
127.0.0.1
# AFNOG workshop address space
213.172.132.0/23

/usr/exim/relay_domains

# List domains for which we have agreed to act as backup MX
t1.ws.afnog.org

Because there is no capability for local delivery, it's important that you set up forwarding so that any error messages and postmaster mail are sent to a remote mailbox. This is done in /etc/mail/aliases:

root:  noclog@example.net
postmaster:  root