User Tools

Site Tools


Sidebar

js#vista.png msort nsort

mail:hostingserver

Hosting Mail Server w/ Exim, Courier/Dovecot, SA, ClamAV & Greylistd

Originally

This howto describes one way to build a scalable, secure, full-featured mail platform. It offers virtual hosting of mailboxes in maildir format with support for quotas and server-side filtering, domain aliasing, address aliasing, address forwarding, catchall addresses. Relaying is secured with STARTTLS and SMTP-AUTH. Incoming mails are checked for viruses, spam, and checked against SPF policy and DNSBL.

In order to achieve scalabality the setup will be split accross 3 servers:

  1 MX server, where most of the security features sit (faramir.middle.earth)
  1 SMTP relay, to allow users to send mails to the outside world (ectelion.middle.earth)
  1 Mailstore server, where the mailbox sits (denetor.middle.earth)

Of course more MX can be added using DNS MX records to your domains, more relay servers can be added using DNS round-robin, and more mail stores can be added using mechanisms described in this howto. At the opposite you can merge easily the MX and relay part, or the relay and the mailstore part. Merging MX and mailstore involves some modifications.

Preliminary Note

In this howto we will assume you have a working Debian server. As well sudo is supposed to be installed on the systems and you must be a sudoer.

Configuring LDAP

The users' information will be stored in an LDAP directory. Here we will install it on the relay server.

First let's install the necessary packages:

sudo apt-get install slapd ldap-utils

For the tutorial we will use the following LDAP parameters:

ldapBase: dc=middle,dc=earth

adminDn: cn=admin,dc=middle,dc=earth

adminPwd: thirdAge

In addition we will use a specific LDAP schema. Most of the attributes and objects are standards except one or two. Caution: as there are a lot of standard attributes you will have to take care none of them is defined twice.

So let's add the schema in openldap in /etc/ldap/schema/mailMEO.schema:

attributetype ( 2.16.840.1.113730.3.1.13

  NAME 'mailLocalAddress' 
  DESC 'RFC822 email address of this recipient' 
  EQUALITY caseIgnoreIA5Match 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) 

attributetype ( 2.16.840.1.113730.3.1.16

  NAME 'mailQuota' 
  DESC 'Maiximal amount of disk space for a mailbox in kilobytes' 
  EQUALITY integerMatch 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) 

attributetype ( 2.16.840.1.113730.3.1.18

  NAME 'mailHost' 
  DESC 'FQDN of the SMTP/MTA of this recipient' 
  EQUALITY caseIgnoreIA5Match 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} 
  SINGLE-VALUE ) 

attributetype ( 2.16.840.1.113730.3.1.22

  NAME 'mailCopyAddress' 
  DESC 'RFC822 email shadow copy address' 
  EQUALITY caseIgnoreIA5Match 
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) 

attributetype ( 2.16.840.1.113730.3.1.47

  NAME 'mailRoutingAddress' 
  DESC 'RFC822 routing address of this recipient' 
  EQUALITY caseIgnoreIA5Match 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) 

attributetype ( 2.16.840.1.113730.3.1.49

  NAME 'spamassassinUserPrefs' 
  DESC 'SpamAssassin user preferences' 
  EQUALITY caseIgnoreIA5Match 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) 

objectclass ( 2.16.840.1.113730.3.2.147

  NAME 'inetLocalMailRecipient' 
  DESC 'Internet local mail recipient' 
  SUP top AUXILIARY 
  MAY    ( mailLocalAddress $ mailHost $ mailRoutingAddress $ mailCopyAddress $ mailQuota $ spamassassinUserPrefs ) ) 

objectclass ( 2.16.840.1.113730.3.2.148

  NAME 'inetMailForwarder' 
  DESC 'Internet mail Forward Address' 
  SUP top AUXILIARY 
  MAY    ( mailHost $ mailRoutingAddress ) )

Then make sure you add the needed schema in /etc/ldap/slapd.conf:

… include /etc/ldap/schema/inetorgperson.schema include /etc/ldap/schema/mailMEO.schema …

… and check the suffix (debconf should have already configured it when you installed slapd):

suffix “dc=middle,dc=earth”

Now we add few ACLs the daemons will need to bind to LDAP.

A readonly access to the userPassword attribute for dovecot:

access to attrs=userPassword,shadowLastChange

      by dn="cn=admin,dc=middle,dc=earth" write 
      by dn="uid=dovecot,dc=middle,dc=earth" read 
      by anonymous auth 
      by self write 
      by * none

A readonly access to other attributes for exim and dovecot:

access to *

      by dn="cn=admin,dc=middle,dc=earth" write 
      by dn="uid=dovecot,dc=middle,dc=earth" read 
      by dn="uid=exim,dc=middle,dc=earth" read 
      by * read
      by anonymous none

The last ACL disables anonymous reads but enables reading (search) for every authenticated user, which you may not want.

We can now restart slapd for changes to take effect:

sudo /etc/init.d/slapd restart

We have to create the users of the previous ACL. To do it we will use the following user.ldif file:

dn: uid=exim,dc=middle,dc=earth objectClass: account objectClass: simpleSecurityObject objectClass: top uid: exim userPassword:: e01ENX1hOElTeXAwV2hnVzFSVnhHd0hCNDF3PT0=

dn: uid=dovecot,dc=middle,dc=earth objectClass: account objectClass: simpleSecurityObject objectClass: top uid: dovecot userPassword:: e01ENX1yZGp2Q1lPNmtDRm1scXAyVWQwa0xBPT0=

The user/pass are:

dovecot/dovecotpopper

exim4/eximmta

To feed the directory type:

ldapadd -x -D cn=admin,dc=middle,dc=earth -W < users.ldif

Here is another ldif file that contains sample data (caution, this sample contains IP addresses that won't suit your setup, change them manually):

dn: ou=domains,dc=middle,dc=earth objectClass: organizationalUnit objectClass: top ou: domains

dn: dc=middle.earth,ou=domains,dc=middle,dc=earth dc: middle.earth objectClass: dNSDomain objectClass: top objectClass: inetLocalMailRecipient objectClass: domainRelatedObject objectClass: posixAccount mailLocalAddress: catchall@middle.earth cn: catchall gidNumber: 8 homeDirectory: /var/mail/middle.earth/c/catchall uid: catchall uidNumber: 8 userPassword:: e01ENX1EV3RteGErOFROanJKNUFXZWt1Z0tBPT0= mailQuota: 102400 mailHost: denetor.middle.earth associatedDomain: middle.earth associatedDomain: lotr.middle.earth

dn: uid=sam,dc=middle.earth,ou=domains,dc=middle,dc=earth cn: sam displayName: Sam Gamji gidNumber: 8 homeDirectory: /var/mail/middle.earth/s/sam mail: sam@middle.earth mailHost: 172.16.16.23 mailQuota: 102400 objectClass: inetLocalMailRecipient objectClass: inetOrgPerson objectClass: posixAccount objectClass: top sn: Gamji uidNumber: 8 uid: sam userPassword:: e01ENX1NeVV5M1BxaHkvWWVLaVpyMXlOaExBPT0= mailLocalAddress: sam@middle.earth mailLocalAddress: gamji@middle.earth mailLocalAddress: shire@middle.earth

dn: uid=frodo,dc=middle.earth,ou=domains,dc=middle,dc=earth cn: frodo displayName: Frodo Baggins gidNumber: 8 givenName: Frodo homeDirectory: /var/mail/middle.earth/f/frodo mail: frodo@middle.earth mailHost: 172.16.16.23 mailQuota: 102400 objectClass: inetLocalMailRecipient objectClass: inetOrgPerson objectClass: posixAccount objectClass: top sn: Baggins uidNumber: 8 uid: frodo userPassword:: e01ENX04UGlDRHVnWEdCMmNhRktnbDljTmpRPT0= mailLocalAddress: frodo@middle.earth mailLocalAddress: baggins@middle.earth mailLocalAddress: shire@middle.earth

dn: uid=gmail,dc=middle.earth,ou=domains,dc=middle,dc=earth cn: gmail mail: alxgomz@gmail.com mailHost: 172.16.16.23 mailRoutingAddress: alxgomz@gmail.com objectClass: inetMailForwarder objectClass: inetOrgPerson objectClass: top sn: alias to Gmail address uid: gmail

Configuring MTAs

Exim4 is the MTA we will use on the MX, the relay server and the mailstores.

The Relay server

First we add the volatile repository in the file /etc/apt/source.list.d/volatile.list:

deb http://volatile.debian.org/debian-volatile lenny/volatile main

… and update the apt database:

sudo apt-get update

Then we install exim4 (most people want to proceed with 'internet site' type installation, and let exim listen on 0.0.0.0). Answer YES when prompted for splitted configuration files:

sudo apt-get install exim4-daemon-heavy clamav-daemon clamav-freshclam openssl

In order to use TLS over the SMTP sessions we need to have a certificate. It can be either a certificates delivered by a certification authority, or a self-signed cert. Here we generate a self-signed cert, but be aware that such certificate will produce warnings on the client side.

RSA key:

openssl genrsa 2048

chmod 640 exim.key

Certificate request:

openssl req -new -key exim.key -out exim.csr

Certificate:

openssl x509 -req -signkey exim.key -in exim.csr -days 9999 -out exim.c

File copy:

chown Debian-exim exim.key

sudo cp exim.key exim.crt /etc/exim4

Activate TLS in /etc/exim4/update-exim4.conf.conf:

… MAIN_TLS_ENABLE='true' …

Create the main macro definition file /etc/exim4/conf.d/main/04_mailMEOmacrodefs:

ldap_default_servers = ldap.middle.earth .ifndef MAILMEO_DOMAINROOT MAILMEO_DOMAINROOT = ou=domains,dc=middle,dc=earth .endif

MAILMEO_DOMAINROOT defines the LDAP root dn where we store domains and users info.

As a modern relay server our server will authenticate users before relaying their mail: this is SMTP-AUTH.

In order to do this we will create the new config file /etc/exim4/conf.d/auth/50_mailMEO_authsmtp:

plain_server:

 driver = plaintext 
 public_name = PLAIN 
 server_condition = ${if ldapauth \ 
      {user="uid=${quote_ldap_dn:${extract{1}{@}{$2}{$value} fail}},\ 
      dc=${quote_ldap_dn:${extract{2}{@}{$2}{$value} fail}},\ 
      MAILMEO_DOMAINROOT" \ 
  pass=${quote:$3} \ 
  ldap:///}{yes}{no}} 
 server_set_id = $auth2 
 server_prompts = : 
 .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS 
 server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}} 
 .endif

login_server:

 driver = plaintext 
 public_name = LOGIN
 server_condition = ${if ldapauth \ 
      {user="uid=${quote_ldap_dn:${extract{1}{@}{$1}{$value} fail}},\ 
      dc=${quote_ldap_dn:${extract{2}{@}{$1}{$value} fail}},\ 
      MAILMEO_DOMAINROOT" \ 
  pass=${quote:$2} \ 
  ldap:///}{yes}{no}} 
 server_set_id = $auth1
 server_prompts = "Username:: : Password::"
 .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS 
 server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}} 
 .endif

As we store password in a encrypted form we have to use a clear-text password mechanism for authentication: either PLAIN or LOGIN (or both). It is then recommended to advertise AUTH only for crypted SMTP sessions. To make AUTH available even for clears sessions, define AUTH_SERVER_ALLOW_NOTLS_PASSWORDS (eg = true) in the /etc/exim4/conf.d/main/04_mailMEOmacrodefs file.

Additionally we will ask exim to bind on the submission port (587), which *should* be preferred by MUA to submit mails, and to advertise TLS. In /etc/exim4/update-exim4.conf.conf change dc_local_interfaces:

dc_local_interfaces='0.0.0.0:0.0.0.0.587'

Antivirus scanning is done by clamav, and is tightly integrated in exim4. All is needed is activating an option in /etc/exim4/conf.d/main/02_exim4-config_options:

av_scanner = clamd:/var/run/clamav/clamd.ctl

Uncomment 3 lines in /etc/exim4/conf.d/acl/40_exim4-config_check_data:

deny

 malware = *
 message = This message was detected as possible malware ($malware_name).

Add user clamav to the Debian-exim group:

sudo adduser clamav Debian-exim

We restart clamav and exim4 and we're done with the relay server.

sudo /etc/init.d/clamav-daemon restart

sudo /etc/init.d/exim4 restart

The Mailstore server (denetor)

This server hosts the mailboxes on its filesystem. Spam checking is done here too. You may find it strange to scan for spam on the mail store but, to me it's the best way to do it… Let me explain why.

Spam scanning is really a resource hug, so it make sense to do it lately after every other filter (DNSBL or so)have done their jobs. Spam scanning is prone to false positives (at least when you have a lot of mailboxes with different profiles like for an ISP) so it 's very risky to reject mails based such scanning. At last doing spam scanning at smtp time prohibit users specific setting (at least its not coherent as soon as you have multiples recipients for a mail). So deferring spam scanning on mailstore allows to store spam in a junk folder based on scanning that really suits each users because it's done at delivery time.

Enough talking, let's start with packages installation (install just like previously).

To benefit from the best features of dovecot we need to use the 1.2.x version. Unfortunately, Debian chips an old 1.0 version which lacks important features like quotawarning. So we're going to add the backports repository (which are now official Debian repositories). Add the following file: /etc/apt/sources.list.d/backports.list

deb http://backports.debian.org/debian-backports lenny-backports main

and run

sudo apt-get update

sudo apt-get install spamassassin exim4-daemon-heavy

sudo apt-get -t lenny-backports install dovecot-imapd dovecot-pop3d

Definition of exim's macros in /etc/exim4/conf.d/main/04_mailMEOmacrodefs:

ldap_default_servers = ldap.middle.earth # mailMEO macros definitions .ifndef MAILMEO_DOMAINROOT MAILMEO_DOMAINROOT = ou=domains,dc=middle,dc=earth .endif

.ifndef MAILMEO_MAINDOMAIN MAILMEO_MAINDOMAIN = ${lookup ldap {USER=userid=exim,dc=middle,dc=earth PASS=eximmta ldap:///MAILMEO_DOMAINROOT?dc?one?(associatedDomain=$domain)}} .endif domainlist mailMEO_domains = <\n ${sg{${lookup ldapm {\

  USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
  ldap:///MAILMEO_DOMAINROOT?associatedDomain?one?\ 
  (&(objectClass=inetLocalMailRecipient)(objectClass=dNSDomain)(mailHost=${loo

kup dnsdb{a=$primary_hostname}{$value}fail}))}}}{,}{\\n}}

mailMEO_domains returns the list of domains handled in LDAP. To enable management of a domain in LDAP, just create an LDAP entry using the following template (change it to suit your needs):

dn: dc=%MYDOMAIN.TLD%,ou=domains,dc=middle,dc=earth dc: middle.earth objectClass: dNSDomain objectClass: top objectClass: inetLocalMailRecipient objectClass: domainRelatedObject mailHost: %IPADDR_OF_MAILSTORE% associatedDomain: %MYDOMAIN%

We have to specify exim to accept thoose domains this is done by adding the domainlist to the rcpt acl file /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt: change

require

message = relay not permitted
domains = +local_domains : +relay_to_domains

to

require

message = relay not permitted
domains = +local_domains : +relay_to_domains : +mailMEO_domains

The MAILMEO_MAINDOMAIN introduces a feature I would call “domain aliasing”. It makes it possible for all addresses living in a domain to also exist in another domain. In the sample data lotr.middle.earth is a domain alias of middle.earth so frodo's mailbox can be reached using address frodo@middle.earth or frodo@lotr.middle.earth. middle.earth is the “main” domain, and is some what privileged. For example authentication (POP/IMAP/SMTP) is only possible using “main domain” credentials. To add a domain alias to am existing domain, just add another “associatedDomain” attribute to the domain object.

Now let's add one router for each type of address. Those routers will defines which message has to be handled by which transport.

The Aliases:

These are secondary addresses for a mailbox. They must belong to the same domain as destination address. One Alias can be added to several mailboxes, in which case all the mailboxes receive mails sent to the alias address. In the sample data, baggins@middle.earth is an alias for frodo@middle.earth and shire@middle.earth is an alias for both, frodo@middle.earth and sam@middle.earth.

To add an alias to an existing mailbox just add a mailLocalAddress attribute with the mail address of the alias.

The file /etc/exim4/conf.d/router/070_mailMEO_alias is the router for such address: mailMEO_alias:

mailMEO_alias:

driver = redirect 
debug_print = "R: locally aliased from $local_part@$domain" 
domains = +mailMEO_domains 
qualify_domain = MAILMEO_MAINDOMAIN 
check_ancestor = true 

local_parts = <\n ${sg{\ 
      ${sg{\ 
          ${lookup ldapm \ 
              {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
              ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?mailLocalAddress?one?\ 
              (&(objectClass=inetLocalMailRecipient)(objectClass=inetOrgPerson)(mailLocalAddress=$local_part@$domain))}\ 
          }}{([\\w\\-\\.]+)@([\\w\\-]+\\.)([\\w\\-]+)}{\$1}}\ 
      }{,}{\\n}} 
data = ${sg{\ 
      ${lookup ldapm \ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?uid?one?\ 
          (&(objectClass=inetLocalMailRecipient)(objectClass=inetOrgPerson)(mailLocalAddress=$local_part@$domain))}}\ 
      }{([\\w\-\.]+)}{\$1@$domain}\ 
  } 

The Forwarders:

Forwarders are addresses that forward mails to one or several addresses. They are quite similar to aliases except that they can forward mails to addresses not belonging to their domains or even remote addresses. To create mail forwarders, create an LDAP entry under the domain entry following the template:

dn: uid=gmail,dc=middle.earth,ou=domains,dc=middle,dc=earth cn: %FWD_LOCALPART% mail: %DEST_MAILADDR% mailHost: %IPADDR_OF_MAILSTORE% mailRoutingAddress: %DEST_MAILADDR% objectClass: inetMailForwarder objectClass: inetOrgPerson objectClass: top sn: Alias address uid: %FWD_LOCALPART%

The file /etc/exim4/conf.d/router/071_mailMEO_fwd this kind of address:

mailMEO_fwd_routes:

driver = redirect 
debug_print = "R: Forwarded from $local_part@$domain" 
domains = +mailMEO_domains 
qualify_domain = MAILMEO_MAINDOMAIN 
check_ancestor = true 
forbid_pipe = true 
forbid_file = true 
forbid_exim_filter = true 
local_parts = ${lookup ldap \ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?uid?one?\ 
          (&(uid=$local_part)(objectClass=inetOrgPerson)(objectClass=inetMailForwarder))}\ 
      } 
data = ${lookup ldap \ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?mailRoutingAddress?one?\ 
          (&(uid=$local_part)(objectClass=inetOrgPerson)(objectClass=inetMailForwarder))}\ 
      }

The Catchall:

Catchalls are kind of garbage mailboxes that will receive every mails sent to a domain whatever the localpart is. You can mix regular mailbox and catchall mailbox in a domain (of course only one catchall per domain is allowed). To add a catchaal address to a domain, add the posixAccount to the domain entry (and all the needed attributes), a mailLocalAddress and mailQuota attributes:

objectClass: posixAccount mailLocalAddress: %CATCHALL_ADDR% gidNumber: %gID% homeDirectory: %MAILDIR_PATH% uid: %CATCHALL_LOCALPART% uidNumber: %UID% userPassword:: %HASH_PASS_STR% mailQuota: %KB%

The file /etc/exim4/conf.d/router/079_mailMEO_catchall defines catchall routing:

mailMEO_catchall:

driver = redirect 
debug_print = "R: domain catchall for $domain <- $local_part" 
domains = <\n ${sg{${lookup ldapm {\ 
  USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
  ldap:///ou=domains,dc=middle,dc=earth?associatedDomain?one?\ 
  (&(objectClass=inetLocalMailRecipient)(objectClass=posixAccount)(objectClass=dNSDomain)(mailHost=$primary_hostname))}}}{,}{\\n}} 
qualify_domain = MAILMEO_MAINDOMAIN 
data = ${lookup ldap \ 
      {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
      ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?uid?base?}\ 
  }

The Virtual Users:

Well… It's the mailbox that users will check for mails! To create a user add an LDAP entry under the domain following this template:

dn: uid=%LOCALPART%,dc=%DOMAIN%,ou=domains,dc=middle,dc=earth cn: %SOMETHING_DESCRIPTIVE% displayName: %SOMETHING_DESCRIPTIVE% gidNumber: %GID% givenName: %SOMETHING_DESCRIPTIVE% homeDirectory: %MAILDIR_PATH% mail: %EMAIL_ADDR% mailHost: %IPADDR_OF_MAILSTORE% mailQuota: %KB% objectClass: inetLocalMailRecipient objectClass: inetOrgPerson objectClass: posixAccount objectClass: top sn: %SOMETHING_DESCRIPTIVE% uidNumber: %UID uid: %LOCALPART% userPassword:: %HASH_PASS_STR% mailLocalAddress: %EMAIL_ADDR%

Please note that the main email address *MUST* be set as a mailLocalAddress just like aliases.

Routing is done using the file /etc/exim4/conf.d/router/077_mailMEO_users:

mailMEO_virtual:

driver = accept 
debug_print = "R: mailMEO virtual for $local_part@$domain" 
domains = +mailMEO_domains 
local_parts = ${lookup ldap\ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?uid?sub?\ 
          (&(objectClass=inetLocalMailRecipient)(uid=$local_part))}\ 
      } 
transport = mailMEO_virtual_delivery

Once done with the routers we have to add several transports.

The first one obvious, it delivers mails in the mailboxes of virtual users. To do so we will use the dovecot LDA because it's reliable and natively implements cool stuffs like quota or sieve filtering (Dovecot rules!).

This is a typical example as described on dovecot wiki and is in the file /etc/exim4/conf.d/transport/50_mailMEO_dovecot:

mailMEO_virtual_delivery:

driver = pipe 
command = /usr/lib/dovecot/deliver -d $local_part@$domain  -f $sender_address 
message_prefix = 
message_suffix = 
delivery_date_add 
envelope_to_add 
return_path_add 
log_output 
user = mail 
temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78 

Spamassassin:

This router is used to check mails for spam using the spamassassin daemon.

spamcheck_router:

no_verify
condition = ${if and { {<{$message_size}{90K}} {!def:header_X-Spam-Flag:} {!eq {$received_protocol}{spam-scanned}}} {1}{0}}
driver = accept
transport = spamcheck

The spamcheck transport is used to process mails in spamassassin daemon.

SA transport is configured in /etc/exim4/conf.d/transport/50_mailMEO_spamcheck:

spamcheck:

  driver = pipe 
  command = /usr/sbin/exim4 -oMr spam-scanned -bS 
  use_bsmtp = true 
  transport_filter = /usr/bin/spamc -u $local_part@$domain 
  home_directory = "/dev/shm" 
  current_directory = "/dev/shm" 
  # must use a privileged user to set $received_protocol on the way back in! 
  user = mail 
  group = mail 
  log_output = true 
  return_fail_output = true 
  return_path_add = false 
  message_prefix = 
  message_suffix =

Let's configure SpamAssassin while we are dealing with it.

Most of the config is stored in /etc/spamassassin/local.cf:

user_scores_dsn ldap://ldap.middle.earth/ou=domains,dc=middle,dc=earth?spamassassinUserPrefs?sub?(&(mailLocalAddress=USERNAME)(objectClass=inetLocalMailRecipient)) user_scores_ldap_username uid=exim,dc=middle,dc=earth user_scores_ldap_password eximmta clear_headers add_header all Flag _YESNO_ add_header spam Result _SCORE_/_REQD_ (_TESTS_)

With this config you can have specific settings for each user, just use the spamassassinUserPrefs attribute using the form 'item value'.

We have to enable spamd in /etc/default/spamassassin:

ENABLED=1 OPTIONS=“-x –ldap-config -u nobody –max-children 5”

… and start it.

sudo /etc/init.d/spamassassin restart

We can now restart exim as well:

sudo /etc/init.d/exim4 restart

At this point, mails can't be sent to the mailstore yet (dovecot must be configured… we'll do it later), and most of the security features are not implemented.

The MX server (faramir):

It's here where we will add security features.

As the MX server will do virus scanning too it needs to have volatile repository in the file /etc/apt/sources.list.d/volatile.list:

deb http://volatile.debian.org/debian-volatile lenny/volatile main

… and the backports for a newer dovecot version, in the /etc/apt/sources.list.d/backports.list:

deb http://backports.debian.org/debian-backports lenny-backports main

Update the apt database:

sudo apt-get update

We can now install the needed packages:

sudo apt-get install clamav-daemon clamav-freshclam exim4-daemon-heavy libmail-spf-query-perl

sudo apt-get -t lenny-backports install dovecot-imapd dovecot-pop3d

Proceed with exim4 installation just like for the relay server.

The file /etc/exim4/conf.d/main/04_mailMEOmacrodefs defines the macros we will use in other config files:

ldap_default_servers = ldap.middle.earth # mailMEO macros definitions .ifndef MAILMEO_DOMAINROOT MAILMEO_DOMAINROOT = ou=domains,dc=middle,dc=earth .endif

.ifndef MAILMEO_MAINDOMAIN MAILMEO_MAINDOMAIN = ${lookup ldap {USER=userid=exim,dc=middle,dc=earth PASS=eximmta ldap:///MAILMEO_DOMAINROOT?dc?one?(associatedDomain=$domain)}} .endif domainlist mailMEO_domains = <\n ${sg{${lookup ldapm {\

  USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
  ldap:///MAILMEO_DOMAINROOT?associatedDomain?one?\ 
  (&(objectClass=inetLocalMailRecipient)(objectClass=dNSDomain))}}}{,}{\\n}}

.ifndef CHECK_RCPT_IP_DNSBLS CHECK_RCPT_IP_DNSBLS = cbl.abuseat.org:dnsbl.njabl.org:sbl.spamhaus.org .endif

.ifndef CHECK_RCPT_SPF CHECK_RCPT_SPF = true .endif

CHECK_RCPT_SPF enabled SPF checking at SMTP time, and reject mail for which spf check failed.

CHECK_RCPT_IP_DNSBL enables DNSBL lookups. The blacklists used here are trustworthy and should not list smarthosts of big MSP. As a consequence, we will choose to reject mails based on thoose DNSBL instead of just warn (which is the default in exin4). Go to the file /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt and change:

.ifdef CHECK_RCPT_IP_DNSBLS

warn
  message = X-Warning: $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text)
  log_message = $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text)
  dnslists = CHECK_RCPT_IP_DNSBLS
.endif

to

.ifdef CHECK_RCPT_IP_DNSBLS

deny
  message = Access denied: $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text)
  dnslists = CHECK_RCPT_IP_DNSBLS
.endif

We have to specify exim to accept the domains defined by mailMEO_domains in /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt: change

require

message = relay not permitted
domains = +local_domains : +relay_to_domains

to

require

message = relay not permitted
domains = +local_domains : +relay_to_domains : +mailMEO_domains

Now we enable virus scanning just like we did on the relay server in /etc/exim4/conf.d/main/02_exim4-config_options:

av_scanner = clamd:/var/run/clamav/clamd.ctl

Uncomment 3 lines in /etc/exim4/conf.d/acl/40_exim4-config_check_data:

deny

 malware = *
 message = This message was detected as possible malware ($malware_name).

Add user clamav to the Debian-exim group:

sudo adduser clamav Debian-exim

sudo /etc/init.d/clamav-daemon restart

The main purpose of the MX server is to route mails to the mailstore server where the mailbox is hosted.

In Exim, routing can be done using the manualroute driver, which will send mails to remote hosts using SMTP.

We will needed 2 drivers of this kind to handle users accounts, aliases and forwarders on one side and catchall accounts on the other side.

All is in /etc/exim4/conf.d/router/075_mailMEOroutes:

mailMEO_routes:

debug_print = "R: $local_part@$domain routed with mailMEO_routes to $0" 
driver = manualroute 
domains = +mailMEO_domains 
transport = remote_smtp 
local_parts = <\n ${sg{\ 
  ${sg{\ 
      ${lookup ldapm \ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?mailLocalAddress?one?\ 
          (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=$local_part@$domain))}\ 
      }}{([\\w\\-\\.]+)@([\\w\\-]+\\.)([\\w\\-]+)}{\$1}}\ 
      , ${lookup ldap \ 
          {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?uid?one?\ 
          (uid=$local_part)}}\ 
  }{,}{\\n}} 
route_data = ${lookup ldap \ 
  {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
  ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?mailHost?base?}} 
host_find_failed = defer 
same_domain_copy_routing = yes

mailMEO_catchall_routes:

debug_print = "R: $local_part@$domain routed with mailMEO_catchall_route to $0" 
driver = manualroute 
domains = <\n ${sg{\ 
      ${lookup ldapm {\ 
          USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
          ldap:///MAILMEO_DOMAINROOT?associatedDomain?one?\ 
          (&(objectClass=inetLocalMailRecipient)(objectClass=posixAccount)(objectClass=dNSDomain))}}\ 
  }{,}{\\n}} 
transport = remote_smtp 
route_data = ${lookup ldap \ 
  {USER=userid=exim,dc=middle,dc=earth PASS=eximmta \ 
  ldap:///dc=MAILMEO_MAINDOMAIN,MAILMEO_DOMAINROOT?mailHost?base?}} 
host_find_failed = defer 
same_domain_copy_routing = yes

A quick explanation: this router first validate the domain is stored in ldap, then it checks that the address exists and at last requests the hostname of the mail server where the mailbox sits.

We don't need to do anything else for the MX concerning the MTA part. So we restart exim and will come back later for the dovecot part.

sudo /etc/init.d/exim4 restart

Configuring Dovecot

Dovecot is used by users to retrieve mails via POP3 or IMAP.

Of course Dovecot has to be installed on the mailstore, but if if we have several mailstores (or if we plan to add more). We will need a POP/IMAP proxy that will “route” connexion to the mailstore hosting the mailbox we want to retrieve mails from. Dovecot being a really great POP/IMAP server, it is also possible to make it act as proxy on the mx server (but it can be placed somewhere else).

Dovecot on the mailstore:

We will now configure dovecot on the mailstore. There are 2 configuration files to edit, the global /etc/dovecot/dovecot.conf:

protocols = imap imaps pop3 pop3s managesieve disable_plaintext_auth = no log_timestamp = “%Y-%m-%d %H:%M:%S ” mail_location = maildir:%h/MailDir mail_privileged_group = mail #mail_debug = yes first_valid_uid = 8 last_valid_uid = 8 first_valid_gid = 8 last_valid_gid = 8 protocol imap {

mail_plugins = quota imap_quota 

} protocol pop3 {

pop3_uidl_format = %08Xu%08Xv 
mail_plugins = quota 

} protocol managesieve {

login_executable = /usr/lib/dovecot/managesieve-login 
mail_executable = /usr/lib/dovecot/managesieve 

} protocol lda {

postmaster_address = postmaster@denetor.middle.earth 
hostname = denetor@middle.earth 
mail_plugins = quota sieve 
auth_socket_path = /var/run/dovecot/auth-master 
sieve_global_path = /var/sieve/global 
sieve=~/.dovecot.sieve 

} auth default {

mechanisms = plain login 
passdb ldap { 
  args = /etc/dovecot/dovecot-ldap.conf 
} 
userdb ldap { 
  args = /etc/dovecot/dovecot-ldap.conf 
} 
userdb prefetch { 
} 
user = root 
socket listen { 
  master { 
    path = /var/run/dovecot/auth-master 
    mode = 0660 
    group = mail 
  } 
  client { 
    path = /var/run/dovecot/auth-client 
    mode = 0660 
    group = mail 
  } 
} 

} dict { } plugin {

quota = maildir:User quota 
quota_warning = storage=90%% /usr/local/bin/quota-warning.sh 90 
sieve_before = /var/sieve/global 

}

… and the ldap part configuration file /etc/dovecot/dovecot-ldap.conf:

uris = ldap://ldap.middle.earth dn = uid=dovecot,dc=middle,dc=earth dnpass = dovecotpopper ldap_version = 3 base = dc=%d,ou=domains,dc=middle,dc=earth scope = subtree user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid,mailQuota=quota_rule=*:storage=%$ user_filter = (&(objectClass=inetLocalMailRecipient)(objectClass=posixAccount)(uid=%n)) pass_attrs = mailRoutingAddress=user,userPassword=password,homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid,mailQuota=userdb_quota_rule=*:storage=%$ pass_filter = (&(objectClass=inetLocalMailRecipient)(objectClass=posixAccount)(uid=%n)) default_pass_scheme = LDAP-MD5

As part of the sieve filtering we have defined a global filter that will be used to store in the 'Junk' mailfolder mails classified as spam by spamassassin.

sudo mkdir /var/sieve

This filter is /var/sieve/global:

require “fileinto”; if header :contains [“X-Spam-Flag”] [“Yes”] {

 fileinto "Junk";
 stop;

}

sudo chown mail -R /var/sieve

As part of the quota plugin we need to create a short script that will warn users in case they reach the threshold limit. It will sit in /usr/local/bin/quota-warning.sh:

#!/bin/sh

PERCENT=$1 FROM=“postmaster@denetor.middle.earth” qwf=“/tmp/quota.warning.$$”

echo “From: $FROM To: $USER To: postmaster@domain.org Subject: Your email quota is $PERCENT% full Content-Type: text/plain; charset='UTF-8'

This message is automatically created by mail delivery software.

The size of your mailbox has exceeded a warning threshold that is set by the system administrator. You *must* delete mails or empty some folders or you may loose emails in the future.”» $qwf

cat $qwf | /usr/sbin/sendmail -f $FROM “$USER” rm -f $qwf

exit 0

sudo chmod +x /usr/local/bin/quota-warning.sh

Dovecot on the MX:

On this host we configure Dovecot to act like a proxy. Proxying to the right mailstore in controlled by the LDAP attribute mailHost. Even if each user has its own mailHost attribute, the configuration presented in this howto involves a domain must be hosted on a single mailstore.

The dovecot config is as follows, /etc/dovecot/dovecot.conf:

protocols = imap imaps pop3 pop3s managesieve disable_plaintext_auth = no log_timestamp = “%Y-%m-%d %H:%M:%S ” login_process_per_connection = no login_processes_count = 8 mail_uid = 8 mail_gid = 8 mail_privileged_group = mail first_valid_uid = 8 last_valid_uid = 8 first_valid_gid = 8 last_valid_gid = 8 protocol imap { } protocol pop3 {

pop3_uidl_format = %08Xu%08Xv 

} protocol managesieve { } auth default {

mechanisms = plain login 
passdb ldap { 
  args = /etc/dovecot/dovecot-ldap.conf 
} 
userdb passwd { 
} 
userdb static { 
} 
user = nobody 

} dict { } plugin { }

… and /etc/dovecot/dovecot-ldap.conf:

uris = ldap://ldap.middle.earth dn = uid=dovecot,dc=middle,dc=earth dnpass = dovecotpopper base = dc=%d,ou=domains,dc=middle,dc=earth pass_attrs==nopassword=1,=password=,=proxy=y,mailHost=host,=destuser=%u pass_filter = (&(objectClass=inetLocalMailRecipient)(objectClass=posixAccount)(uid=%n))

Extra Notes:

  If you want to use several mailstores and benefit of bayesian filtering site wide, you can use an sql database to store bayesian data and share it on all mailstores (unfortunately, bayesian storage in ldap in not possible now).
  To manage users, domains and all kind of addresses you can use any ldap client, as long as you follow the examples in sample data. PHPLDAPAdmin can be a good alternative if you don't feel confortable with ldif format.
  There dozens of webmail around the web which  you can use in this setup. I personnaly have a preferrence for roundcube webmail which offers features that integrates very well here, mostly because it has plugins for sieve filtering, LDAP directory and more.
  As users informations are stored in LDAP it is quite easy to setup a centralized address book that can be queried by many mail clients.
  If you use such directory, most clients will use the "mail" LDAP Attribute, so addresses not defined as mail won't show up during search.

Security Notes:

  In this howto, all operations are run by the "mail" user. This may not be wise to do it in production (read "real internet") environment. You can create a specific user that would replace the "mail" user, tweaking the config will then be necessary.
  Dovecot is shipped with default certicates that should be replaced in production environment (the procedure for exim relay applies).
mail/hostingserver.txt · Last modified: 2020/02/24 11:16 (external edit)