Ubuntu/Debian Exim 4 Configuration

This article is nothing more than step-by-step instructions of how I got a Debian mail server to run Exim 4 with Maildir format mailboxes, SpamAssassin, and ClamAV for multiple virtual domains, each with its own alias file and message size limits. This server was built and configured the end of Sept. 2004 through mid October 2004.

I am not an Exim expert! This article basically outlines the process so I can do it again if necessary. Nearly all the information within was already gathered from the internet. This is simply a collection of pieces that work together on my debian server all stored in a single page.

Toward the end of August of 2005, I configured another mail server using the Sarge release of Debian. The article was updated to reflect that system's setup. Now, in December 2012, I have also used this same process to install and configure a mail server using Ubunutu 12.10. Everything was smooth with that install, and that is why I am still keeping this article on my website.

Installing and configuring Exim 4 on Ubunutu/Debian

  1. First, install all the necessary packages on the system as the root user.

    apt-get install clamav-daemon \ clamav-freshclam exim4-daemon-heavy exim4 \ courier-base courier-authdaemon courier-imap \ courier-pop spamassassin wget spamc sa-exim

    When going through the exim4 config, be sure to select the multiple file configuration layout. If you didn't (or weren't prompted for it), simply set dc_use_split_config to true in the /etc/exim4/update-exim.conf.conf file.

  2. Create your Maildir directory

    maildirmake ~/Maildir/

  3. Configure exim4 to use Maildir format mailboxes. Modify the file /etc/exim4/update-exim4.conf.conf so that it contains:

    dc_localdelivery='maildir_home'

  4. We need to Edit /etc/default/spamassassin to enable spamd.

  5. Each user can set up their own filters by creating a .forward file in their home directory. If the first line of this file reads "# Exim filter" then Exim4 will treat it as a filter.

    Here is an example of an Exim filter that checks the headers that SpamAssassin adds and puts the mail in the appropriate Maildir folder:

    $h_X-Spam-Flag: CONTAINS "Yes"
    then
    save $home/Maildir/.Spam/
    finish
    endif

    Exim's Interface To Mail Filtering (PDF)

  6. Many system administrators like to set up the Maildir directories and .forward filter file in the /etc/skel directory so that when they make a new user on the system, everything is automatically copied over. I suggest that you do this as well as it makes things easier.

  7. Before going live with the mail server, we will want to test it!

    1. Generate the new configuration:

      update-exim4.conf

      If you made it this far, then your config files don't have any syntax errors. Now try:

      exim4 -bV

      If you are still without errors, then your config files are formatter properly

    2. Next, start exim by issuing:

      service exim4 restart

    3. Now, check a local address:

      exim4 -bt local_user@example.com

    4. Check sending an email:

      exim4 -v mailbox_you_can_check@dom.ain
      From: user@your.domain

      Testing exim
      .

      You should now see some messages to let you know that the email was sent or information about what went wrong.

    5. To test with full debug output using a specific config file, use something like:

      exim4 -C /etc/exim/exim_example.conf -d -bt user@example.com

    6. To test the config coming from a specified ip address, use:

      exim4 -bh 192.168.1.10

      HELO example.com
          MAIL FROM: <user@example.com>
          RCPT TO: <local_user@example.com>
          DATA
          Subject: something
          your message here
          .
          QUIT

  8. Add the following to your /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs file:

    # This tells what virus scanner to use
    av_scanner = clamd:/var/run/clamav/clamd.ctl

  9. Edit /etc/exim4/conf.d/acl/40_exim4-config_check_data to inlude the following before the "# accept otherwise" line:

    # Reject messages that have serious MIME errors.
    # This calls the demime condition again, but it
    # will return cached results.
    deny message = Serious MIME defect detected ($demime_reason)
    demime = *
    condition = ${if >{$demime_errorlevel}{2}{1}{0}}

        # Reject file extensions used by worms.
    # Note that the extension list may be incomplete.
    deny message = This domain has a policy of not accepting certain types of attachments \
        in mail as they may contain a virus. This mail has a file with a .$found_extension \
        attachment and is not accepted. If you have a legitimate need to send \
        this particular attachment, send it in a compressed archive, and it will \
        then be forwarded to the recipient.
    demime = exe:com:vbs:bat:pif:scr

    # Reject messages containing malware.
    deny message = This message contains a virus ($malware_name) and has been rejected
        malware = *

  10. Then, you need to enable ClamAV.

    1. Firstly, you will want to be sure that it is running against messages. In /etc/exim4/sa-exim.conf, search for "SAEximRunCond":

      SAEximRunCond: ${if and {{def:sender_host_address} {!eq {$sender_host_address}{127.0.0.1}} {!eq {$h_X-SA-Do-Not-Run:}{Yes}} } {1}{0}}

      That is simply skipping the scan on anything from the local machine or if the "X-SA-Do-Not-Run" header in the message is set to "Yes". If you just want exim to run ClamAV on all messages, use this:

      SAEximRunCond: 1

    2. Before restarting ClamAV, we need to be sure that all of the access rights are in place so that the scans actually happen. The best way to handle this is to add the clamav user to the Debian-exim group. Either manually edit /etc/group, or simple run:

      adduser clamav Debian-exim

    3. Be sure that /etc/clamav/clamd.conf contains a line that reads:

      AllowSupplementaryGroups

    4. Set the file permissions for the /var/run/clamav directory to allow for the correct user to use it:

      chown Debian-exim.Debian-exim /var/run/clamav
      chmod g+w /var/run/clamav

    5. A restart of ClamAV is necessary for the changes to take effect:

      /etc/init.d/clamav-daemon restart

  11. You should now be able to get your mail via IMAP with a mail client like Thunderbrd. Check your headers (View Source) and see that SpamAssassin has added its headers. SMTP-end virus scanning should also be taking place. Check your /var/log/clamav/clamav.log to monitor this.

Multiple Domain Alias Files

The steps below are used to enable support for having multiple virtual domains each with its own alias file.

  1. Exim will need to have the alias files for each domain.

    1. Create the /etc/exim4/virtual directory.

    2. For each virtual domain, create a file that contains the aliases to be used named as the domain.

      For example, if example.com was one of my domains, I'd do the following:

      1. Create the /etc/exim4/virtual/example.com file.

      2. If my system users were sys1, sys2, and sys3, and their email addresses were to be joe, john, jason, I'd put the following into the domain alias file:

        joe: sys1@localhost
        john: sys2@localhost
        jason: sys3@localhost

        If john was also to get all mail addressed to info@example.com, you would add this entry:

        info: sys2@localhost

        If you wanted all mail to user1@example.com to go to another email account outside of this domain, you would enter:

        user1: a.user@some.domain

        If you wanted all mail directed at any address other than what is defined in the alias file to go to joe, you'd enter:

        *: sys1@localhost

        In the above examples, the "@localhost" suffix to the user names forces the delivery to a system user. I found that if you do not include this in the alias files and your machine's host name is within one of the domains handled by exim, every system user would need an entry in the machine's domain in order to be delivered corectly. For instance, if your host name was mail.example1.com and example1.com was handled by this server this would be needed. This would allow delivery to all the system user names at example1.com. The reason is simple, and I will try to illustrate it for you here:

        1. exim receives a message delivered to joe.blow@example3.com
        2. The alias file for this domain has 

          joe.blow: jblow

           in it.
        3. This would translate to jblow@domain-of-the-system
        4. The process would be repeated using jblow@domain-of-the-system
        5. If there was no entry in the domain-of-the-system alias file for jblow, the message would be undeliverable (or non-routable)

        You could even have special redirects like the following:

        script: "| /path/to/some/script"
        prev: :fail: $local_part left!
        kill: :blackhole:

  2. Edit /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs by replacing the current local_domains line with:

    domainlist local_domains = @:localhost:dsearch;/etc/exim4/virtual

  3. Create /etc/exim4/conf.d/router/350_exim4-config_vdom_aliases with the following content:

    vdom_aliases:
        driver = redirect
        allow_defer
        allow_fail
        domains = dsearch;/etc/exim4/virtual
        data = ${expand:${lookup{$local_part}lsearch*@{/etc/exim4/virtual/$domain}}}
        retry_use_local_part
        pipe_transport = address_pipe
        file_transport = address_file

  4. Now, regenerate your exim4 config:

    update-exim4.conf

  5. If there were no errors, restart exim4:

    /etc/init.d/exim4 restart

Domain Dependent Maximum Message Size

The next step for my server is to give each domain a configurable message size limit. Then, when the server get's a message that is larger than the target domain's size limit, I want to send a message back to the original sender telling them why the message was not delivered. However, I also want to have that message customized for each domain. That way, the domain owners can provide detailed instructions on how to send large messages to their domain if it is necessary. Of course, there will also need to be some kind of default size limit and message for domains that do not need the customization.

  1. Create /etc/exim4/domain-size-limits to contain the list of domains and their maximum message size limits. You can also add a wildcard at the end entry if you want to set a default limit. The file may look something like the following:

    example.com: 20M
    example1.com: 5M
    *: 15M

    This provides you a quick way to edit the values. The values will also take effect as soon as the file is saved - no need to restart exim!

  2. OK, now we know what domains we want to customize the size for. Now it's time to create a message to send for those domains. Create /etc/exim4/domain-size-limit-messages with content similar to:

    exmaple.com: The largest acceptable message size for Example.com is\
        ${expand:${lookup{$domain}lsearch*@{/etc/exim4/domain-size-limits}}}.\
        Your message was $message_size. If you feel that $local_part@$domain\
        should really get your message, then visit http://www.example.com/files/\
        where you can upload any large files. If you select $local_part@$domain\
        from the "notify" list, they will receive a message with a link directly\
        to your file.
    *: The largest acceptable message size for $domain is\
        ${expand:${lookup{$domain}lsearch*@{/etc/exim4/domain-size-limits}}}.\
        Your message size was $message_size. Please revise your message so it\
        does not exceed this maximum file size and resend. If this is not\
        possible, contact the recipient in another way.

    As you see, we have one domain that has a custom message sent out, and have defined a default message for all other domains. These messages can be edited at any time and do not need an exim restart to take effect.

  3. Now for the fun part! We need a way to catch the messages that are too large for the domain! First, create /etc/exim4/conf.d/router/325_exim4-config_large_messages with the following:

    large_messages:     driver = accept
        domains = dsearch;/etc/exim4/virtual
        condition = ${if >{$message_size}{${expand:${lookup{$domain}lsearch*@{/etc/exim4/domain-size-limits}}}} {yes}{no}}
        transport = bounce_large_messages
        no_verify

    This router dynamically checks which domains are available and what their limits are set to.
  4. Now create /etc/exim4/conf.d/transport/40_exim4-config_bounce_large_messages with the following content:

    # This bounces a message to people who send files too large for that domain
    bounce_large_messages:
        driver = autoreply
        from = $local_part@$domain
        to = $sender_address
        subject = Re: ${escape:$h_subject:}
        text = ${expand:${lookup{$domain}lsearch*@{/etc/exim4/domain-size-limit-messages}}}

    This transport then sends the original sender a message using the text looked up from the domain-size-limit-messages file for that domain. The "From:" field is filled in with the intended recipient of the message - appearing to be a reply.

This was put together based on what I found in the Exim FAQ

Configuration Tips

Maybe this is something I should have said in the begining, but at the time or writing this article (the first time), I had never set up an exim4 server, and the only exim3 server I had used was a default debconf install. Therefore, if you see something on this page that could be done in a more elegant, more efficient or just plain better way, please send me a note.

I will post suggestions and updates to this page as they come in.

  • UPDATE 12/11/2012: I successfully installed and configured this setup on an Ubuntu 12.10 server without modification to the steps involved.

  • UPDATE 2/23/2009: I received a report of this installation going off on top of Lenny as well. In addition, I updated the stuff about

    /var/run/clamav

    to also add write permissions to the group.

     

  • UPDATE 9/13/2006: I have successfully installed this on an Ubuntu Dapper machine as well. The only thing that I needed to do was to enable the universe repository in /etc/apt/sources.list

  • UPDATE 5/11/2006: Updated paths for alias files and such to match the configuration of my last install (makes things easier for me next time).

  • UPDATE 1/11/2006: I just made some minor edits to explain a couple things that got left out of the article. Thanks to those that brought them to my attention.

  • UPDATE 9/13/2005: I just configured a new mail server last month using the Sarge release of Debian. I did things a little different, and have updated this article to reflect the changes. Changes include using aptitude to install the packages rather than apt-get. Aptitude handles conflicting and broken packages much better. Also, I didn't go through the trouble of creating all the transports and such for spamassassin or Rules Du Jour. Instead, I installed the sa-exim package, and got rid of Rules Du Jour completely. I also am using the freshclam package to handle all the anti-virus updates.

  • UPDATE 2/15/2005: Alex Tingle submitted some updates to the article that I have now added. He also supplied a link for a Spamassassin and Exim on Debian HOWTO that some of you may find interesting.