Penguin

Be careful about editing the configuration

Since Exim (version 3) rereads its configuration on every delivery attempt, it is unadvisable to edit a live configuration file. If you're using Debian and 4.x, the config it uses isn't the one you edit (it gets generated on init-script-reload), so you're OK.

Removing all frozen messages in an Exim queue

Lots of bounce messages to fake Email addresses from spam?

mailq | awk '/frozen/ { print $3 }' | xargs exim -Mrm

Setting up Exim as a mail filter for another machine that does mail delivery (eg MicrosoftExchange)

See EximMailFilter.

Setting up Exim to use LDAP aliasing and to deliver via Cyrus IMAP

If you're running Exim 3, make sure to read the notes at the bottom of this entry.

First, in the router section of exim.conf, add a router for Cyrus (at the top) as follows:

local_user_cyrus:
  driver = accept
  check_local_user
  local_part_suffix = +*
  transport = local_delivery_cyrus

Note that the local_part_suffix part allows you to specify user+mailfolder@domain... and the command in the local_delivery_cyrus section takes care of delivering to the correct subfolder of inbox.

Next, in the transport section, add a transport to allow delivery via Cyrus:

local_delivery_cyrus:
  driver = pipe
  command = /usr/libexec/cyrus/deliver -m ${substr_1:$local_part_suffix} -- $local_part
  user = cyrus
  group = mail
  return_output
  log_output
  message_prefix =
  message_suffix =

Finally, you will need to modify your alias resolution to use LDAP. This is based on my LDAP configuration, which consists of an Organisational Unit (OU) of Aliases, each container having an attribute rfc822MailMember, containing the real mailbox. An example:

dn: cn=greg,ou=Aliases,dc=compel,dc=co,dc=nz
rfc822MailMember: greig
objectClass: nisMailAlias
objectClass: top
cn: greg

This fixes the fact that no one can deal with the fact that GreigMcGill has an "i" in his name! ;)

So... back to exim.conf... the alias router looks like the following:

system_aliases:
   driver = redirect
   allow_fail
   allow_defer
   data = ${lookup ldap {ldap://10.0.0.1/cn=$local_part,\
     ou=Aliases,dc=compel,dc=co,dc=nz?rfc822MailMember?base}}
   file_transport = address_file
   pipe_transport = address_pipe

Now, this assumes that your system is like mine, where each user is a real account, and getpwnam() and the like will return usernames out of LDAP via PAM. If this is the case, the above configuration should be all you need. If you are running a "black box" MailServer, then some other kind soul may like to add the required configuration below.

If you have a different LDAP Schema to the one mentioned above, either because your system installed a different set of Schema for the equivalent entries, or you are running a custom Schema that handles multiple virtual domain hosting without unix accounts, then just modify your LDAP lookups appropriately. As a hint:

data = ${lookup ldap {ldap://127.0.0.1/cn=$local_part,\
  ou=Aliases,o=$domain,dc=wlug,dc=org,dc=nz?mailAlias?base}}

If my system recieves mail to say daniel@wlug.org.nz, it will for the dn given as:

cn=daniel,ou=Aliases,o=wlug.org.nz,dc=wlug,dc=org,dc=nz

and return the mailAlias attribute.

Between Exim, SendMail, Postfix and Courier, Exim has been the easiest to set up with a custom LDAP Schema by far. SendMail supposedly supports it, but in practice doesn't. Postfix is SendMail with a pretty configuration file, and has worse support for custom LDAP Schemas IMO. Exim > *.

Exim 3

The above system will work with previous version of Exim, however the format might be slightly different (not confirmed this, I just base this off what I've done with Exim 3.3.5)

The main difference is in the query line in the Aliases director example above:

data = ${lookup ldap {ldap://10.0.0.1/cn=$local_part,\
  ou=Aliases,dc=compel,dc=co,dc=nz?rfc822MailMember?base}}

I always used something that looked like:

 query = "ldap://127.0.0.1/ou=Aliases,o=$domain,ou=Domains,BASEDN?mailAlias?sub?cn=$local_part"

but there are probably other ways of doing this too :)

Delivering to Cyrus21 from Exim 3.3 (Woody)

The mighty Cuchulain's config:

Transport:

NOTE: this must be placed somewhere sensible in the transports list. ie, before any transports for procmail(1), userforward, or local_user delivery.)

local_delivery_cyrus:
   driver = pipe
   command = "/usr/sbin/cyrdeliver -m ${substr_1:${local_part_suffix}} -- ${local_part}"
   user = cyrus
   group = mail
   envelope_to_add = true
   return_output
   log_output
   prefix =
   suffix =

Director:

local_user_cyrus:
   driver = localuser
   transport = local_delivery_cyrus

Delivering to Cyrus from Exim 4.x

Exim 4.x has native LMTP support, so you don't need to pipe the mail to cyrus's deliver program. Instead, you can do the following:

Note that the same rules about placing local_user_cyrus in a sensible place apply here as well!

Router:

local_user_cyrus:
  debug_print = "R: local_user_cyrus for $local_part@$domain"
  driver = accept
  local_part_suffix = +*
  local_part_suffix_optional
  transport = cyrus_ltcp

Transport:

cyrus_ltcp:
  debug_print = "T: cyrus_ltcp for $local_part@$domain"
  driver = smtp
  protocol = lmtp
  hosts = localhost
  allow_localhost

LMTP callbacks

Another thing you can do with Exim and Cyrus-IMAP integration is described at Cyrus Imap page of Exim Wiki. It allows Cyrus to check the existence of mailboxes before replying to "RCPT TO:" or "MAIL FROM:". Exim does not have to send back bounces in reply to "dictionary recipient" spam.

It is best suited for integration with Cyrus-IMAP virtual domains.

See also LMTPNotes.

Using MailDir format instead of MBox

This is for version 3.35 (the version with Debian Woody).

The default is to deliver local mail to /var/spool/mail/$USERNAME in MBox format. In the local_delivery section of exim.conf, remove the line that says

file = /var/spool/mail/${local_part}

and add lines that say

create_directory = true
  directory = /home/${local_part}/Maildir/
  directory_mode = 770
  maildir_format

Easy peasy. :) But see the comment below about possible gotchas, as this is just the basic bit.

Exim can deliver to Inbox but can't deliver to forwarded mailboxes (subfolders etc)

I have Exim filtering set up in a .forward file to drop all my Email into the right place.

However, I was getting lots of messages like this in my logs:

2003-04-21 19:38:04 196iGy-00005j-01 ==
      /home/user/Maildir/.Mailing Lists.WLUG/ <user@localhost> D=userforward
  defer (-31): directory_transport unset in userforward driver

The problem? exim.conf was set up for MailDir support, but for some reason the config for the directory delivery agent wasn't linked to the forwarding section.

Ensure address_directory has Maildir uncommented, and then find the userforward section. Under file_transport = address_file, set

directory_transport = address_directory

and rerun the queue, forcing redelivery if necessary, with exim -qff.

Exim 3 generates some message about unable to deliver due to a child process failure

This when used in conjunction with Cyrus IMAP can be caused by trying to deliver to a mailbox which has reached it's maximum quota limit. The exim_mainlog entry looks similar to the following:

2004-01-08 11:08:12 1AeLqW-0007k0-00 == gerwin@north.pub.tla T=local_delivery_cyrus defer (0): Child
process of local_delivery_cyrus transport returned 75 (could mean temporary error) from
command: /usr/lib/cyrus-imapd/deliver

Configuring Exim4 with a virtual domain table/users in text files

(If you have multiple domains on the same machine, and users account names don't correspond to their Email addresses.)

In the top section of your exim.conf

domainlist local_domains = @ : \
    @[] : \
    localhost : \
    partial-lsearch;/etc/exim/virtual.domains

and underneath the part where it says something like

real_local:
  driver = accept
  check_local_user
  local_part_prefix = real-
  transport = local_delivery

add a section

virtual:
  driver = redirect
  allow_defer
  allow_fail
  data = ${lookup{$local_part@$domain}lsearch*@{/etc/exim/virtual.users}}
  domains = partial-lsearch;/etc/exim/virtual.domains
  retry_use_local_part

now you will need file called virtual.domains that is simply a list of all the domains for which you accept mail, and a virtual.users file with a table of users in the format:

virtual.domains:
example.com
example.net
virtual.users:
#example.com
regularuser@example.com     : localuser
forwardinguser@example.com  : someuser@example.org
*@example.com               : catchalluser

#example.net
regularuser@example.net     : localuser2
forwardinguser@example.net  : someuser2@example.org
*@example.net               : catchall2

--BlairHarrison

Connections to Exim take a long time to work (Exim is slow to show the SMTP banner)

Check that your DNS and hosts(5) settings are correct, and that you can correctly resolve hosts on the server.

If this all works as desired but connections are still slow, it could be an IDENT timeout. Try either setting:

rfc1413_hosts = *
rfc1413_query_timeout = 0s

or installing an IDENT server on the Exim machine.

Testing a new transport on a live system

prefix = test- is your friend

Implementing SPF in Exim 4 using exiscan-acl

Section 8 of the exiscan-acl patch documentation explains how to do it if you've compiled SPF into Exim. Otherwise, you can do so so via the Mail::SPF::Query Perl module. Appending A.7 of Spam Filtering for Mail Exchangers shows both approaches.

http://www.meini.org/spf/ contains Debian Packages for the Mail::SPF::Query and Net::CIDR::Lite modules and for libspf. The packaged libspf version is pretty old though, you might consider installing from SourceCode. You'll also need Net::DNS, for which a Debian package can be found at http://www.proesdorf.de/debian/.

Alternatively you could use Evan's deb packages but the newer code of libspf2 and build some new libspf2 packages.

Useful Exim4 Mail Proxy tweaks.

If you have Exim4 configured as a mail proxy in front of, say, MS Exchange, or another Active Directory based email server, you may find the following useful to either limit spam, or transition from a "catchall" domain setup. Note that these two configurations are mutually exclusive.

This first config fragment provides the ability to reject nonexistent email addresses at SMTP time.

This first stanza belongs in the main configuration, and simply defines the LDAP lookup macro. You will need to create the "MTA Auth" user in AD. Copy Guest and enable it. Don't forget to set the IP address of the AD server correctly.

Note that port 3268 is used. AD geeks will recognise this as the Global Catalog port, and means you are asking the AD Forest, rather than just a particular server. If you don't want this, or your server isn't a GC server, just use port 389.


ITP_LDAP_AD_MAIL_RCPT = \
        user="CN=MTA\ Auth,CN=Users,DC=site,DC=example,DC=co,DC=nz" \
        pass=gand4lf \
        ldap://10.7.31.10:3268/DC=site,DC=example,DC=co,DC=nz\
        ?mail?sub?\
        (&\
         (objectClass=*)\
         (proxyAddresses=SMTP:${quote_ldap:${local_part}@${domain}})\
        )

This belongs as the first router in the routers section. Change the domains to match ALL domains we accept mail for.


adsi_itp_check:
        driver = redirect
        domains = *example.co.nz
        allow_fail
        allow_defer
        forbid_file
        forbid_pipe
        redirect_router = spam_redirect
        data = ${lookup ldap {ITP_LDAP_AD_MAIL_RCPT}\
        {${local_part}@${domain}}{:fail: User unknown}}

The second fragment provides a "catchall" function for a domain. If an email address is not defined within a site, mail will be accepted and routed to the provided address (catchall@example.co.nz in this case). Please do not use this except as a transitory measure, as catchalls are inherently bad IMHO.

First, the lookup macro, as before.


ITP_LDAP_AD_MAIL_RCPT = \
        user="CN=MTA\ Auth,CN=Users,DC=site,DC=example,DC=co,DC=nz" \
        pass=gand4lf \
        ldap://10.7.31.10:3268/DC=site,DC=example,DC=co,DC=nz\
        ?mail?sub?\
        (&\
         (objectClass=*)\
         (proxyAddresses=SMTP:${quote_ldap:${local_part}@${domain}})\
        )

Now the catchall router:


adsi_itp_catchall:
        driver = redirect
        domains = *example.co.nz
        redirect_router = spam_redirect
        condition = ${if eq{${lookup ldap {ITP_LDAP_AD_MAIL_RCPT}{${local_part}@${domain}}}}{} {yes}{no}}
        data = catchall@example.co.nz

As nice as this solution is, it doesn't (in the above example) cater for Public Folders which are mail enabled. Greig informs me that you can modify the LDAP bind to do this, however. One way to do this is below:


ldap_default_servers = <; server.test.lan:3268
LDAP_AD_MAIL_RCPT = \
        user="CN=ldap_user,OU=Users,DC=test,DC=lan" \
        pass=ilovegreig \
        ldap:///DC=test,DC=lan\
        ?mail?sub?\
        (&\
                (|\
                        (objectClass=user)\
                        (objectClass=publicFolder)\
                        (objectClass=group)\
                )\
        (proxyAddresses=SMTP:${quote_ldap:${local_part}@${domain}})\
)

One issue I found was that I wasn't able to query the whole forest using port 389. I needed to use port 3268 (GC). If you're having problems, there's a high chance you haven't got the DN correct. Use adsiedit to check (found in the Windows Support Tools). More information can be found at http://www.exim.org/eximwiki/MsExchangeAddressVerification.

/dev/null or other file alias doesn't work (Exim 4.5)

root@firewall.tla:/etc # exim4 -bv -v junk@firewall.tla
R: spam_redirect for firewall.tla
R: system_aliases for junk@firewall.tla
junk@firewall.tla -> /dev/null
*** Error in setting up pipe, file, or autoreply:
file_transport unset in system_aliases router

There is a macro defined for the file_transport router, but its unset by default. To allow this behaivour, add this macro to either your /etc/exim4/conf.d/main/02_exim4-config_options.rul, or a local macros filw, such as /etc/exim4/conf.d/main/000_local_macros:

# allow writing to files in system aliases (potentially bad!)
SYSTEM_ALIASES_FILE_TRANSPORT = address_file

A better answer is changing the destination using the redirect router alias :blackhole:.

Allow connections to the submission port (587) and the smtps port (465)

SMTPS is easy, since 4.43:

# wlug: listen on 25 (smtp), 465 (ssmtp for Outlook)
daemon_smtp_port = 25:465
tls_on_connect_ports = 465

in your main section. Between 4.03 and 4.43, you can use the -tls-on-connect command line parameter.

587 is harder, because the rules are "either SMTP AUTH or SSL". An Exim SSL/TLS recipe was posted to the list, which suggests (updated, and with client certs removed - you really want to only use SMTP AUTH):

# wlug: listen on 25 (smtp), 465 (ssmtp for Outlook) and 587 (submission)
daemon_smtp_port = 25:465:587

# uncomment to only allow auth if you're on by SSL
# auth_advertise_hosts  = ${if eq{$tls_cipher}{}{localhost}{*}}

tls_advertise_hosts     = *
tls_certificate         = /etc/exim4/server-cert.pem
tls_privatekey          = /etc/exim4/server-key.pem

..and in your RCPT ACL (near the top):

  accept  authenticated = *
          encrypted = *

  accept  condition = ${if eq{$interface_port}{587}{1}{0}}
          endpass
          message = SMTP AUTH required for port 587
          authenticated = *

Redirecting all mail to a single mailbox

I wanted to set up an SMTP server for testing that would accept all mail and send it to a single mailbox so that we didn't accidentally send mail to anyone.

This example uses Debian's split config.
Add a new router in /etc/exim4/conf.d/router. Because these files are processed in alphabetical order, add it at the top. 09_exim4-config-redirect_all is a good choice.

redirect_all:
        debug_print = "R: redirecting all mail"
        driver = redirect
        data = "user_to_send_to"

Test using the method listed below.

Testing a new config file before putting it live.

JohnMcPherson pointed out the following useful tips:

If you're using Debian's split configuration setup, you can generate a new configuration file to a temporary location with:

 $ update-exim4.conf -o ~/exim4.test.config

Then you can test the routing of that config without touching your running server using

 $ exim -C ~/exim4.test.config -bt user@host.com

If it's not working how you intended, then ramp up the debugging and get all the details

 $ exim -d+all -C ~/exim4.test.config  -bt user@host.com < /dev/null

See also


Part of CategoryMailNotes