Large-Scale Mail with Postfix, OpenLDAP and Courier

Setting up an SMTP mail server for multiple domains on a single machine with remote access via IMAP.

The first LDAP source definition is for virtual aliases. We've named this LDAP source aliases. In our configuration, our LDAP server is running on localhost. The search base is the top of the hosting subtree we defined in our LDAP server. We're querying for items where the mail elements match the e-mail recipient as well as items that are of the courierMailAlias object class. The destination of the alias is stored in the maildrop attribute. Postfix won't bind using an account, instead it will do an anonymous lookup:

aliases_server_host = localhost
aliases_search_base =
aliases_query_filter =
aliases_result_attribute = maildrop
aliases_bind = no

When using the accounts source we're looking for entries that have an object class of courierMailAccount. We request the mailbox attribute as the result:

accounts_server_host = localhost
accounts_search_base =
accounts_query_filter =
accounts_result_attribute = mailbox
accounts_bind = no

A second source for accounts, accountsmap, also needs to be defined to help locate accounts when a catchall is used. Without this lookup, a catchall in the aliases would override virtual accounts in a domain:

accountsmap_server_host = localhost
accountsmap_search_base = o=hosting,dc=myhosting,dc=example
accountsmap_query_filter =
accountsmap_result_attribute = mail
accountsmap_bind = no
Now that the aliases and accountsmap LDAP source are defined, let Postfix know to use it by defining the virtual_maps parameter in
virtual_maps = ldap:aliases
For this example, assume there is a vmail UNIX account created that has a UID of 125, a GID of 120 and its home directory is /home/vmail:
:virtual_mailbox_base = /home/vmail/domains
virtual_mailbox_maps = ldap:accounts
virtual_minimum_uid = 125
virtual_uid_maps = static:125
virtual_gid_maps = static:120
Set the virtual_uid_maps and virtual_gid_maps to a special static map and hard code it to the UID and GID of the vmail account. All of the parameters shown here are fully documented in README_FILES/VIRTUAL_README, which comes with the Postfix source.

We also need to edit the local_recipient_maps parameter to look at the virtual_mailbox_maps so Postfix knows what accounts are valid. This is needed so Postfix can reject mail for unknown accounts:

local_recipient_maps = $alias_maps
  unix:passwd.byname $virtual_mailbox_maps

There aren't any special instructions for installing Courier, so see its documentation for full instructions. It should autodetect LDAP and build it in. You should seriously consider passing the --enable-workarounds-for-imap-client-bugs option to ./configure, otherwise Netscape mail users may have trouble interacting with your server. This bends the IMAP protocol a little bit, but it's better to have happy users than a perfect protocol with unhappy users.

Courier uses an authentication dæmon to keep authentication separate from the other parts of the system. Configure it so that a valid e-mail account is either found in either LDAP or PAM. Specify this in authdaemonrc using the authmodulelist parameter:

authmodulelist="authldap authpam"

All LDAP parameters are in authldaprc. Most parameters are self-explanatory. To use the Courier schema, you actually have a few modifications to make, though. You also need to map all virtual accounts to the vmail account. Here is a summary of the updates you need to make to authldaprc:

LDAP_GLOB_UID           vmail
LDAP_GLOB_GID           vmail
LDAP_HOMEDIR            homeDirectory
LDAP_MAILDIR            mailbox
LDAP_CRYPTPW            userPassword
Three other settings to be concerned with are LDAP_AUTHBIND, LDAP_BINDDN and LDAP_BINDPW. These relate to authenticating the user. LDAP_AUTHBIND is mutually exclusive with LDAP_BINDDN and LDAP_BINDPW. We recommend using LDAP_AUTHBIND. A comment in authldaprc mentions a memory leak in OpenLDAP when using LDAP_AUTHBIND, but it has been fixed in OpenLDAP version 2.0.19.

If you use LDAP_BINDDN and LDAP_BINDPW, you are limited to the crypt, MD5 and SHA algorithms for passwords. SMD5 and SSHA are not available. Also, you must put the root LDAP password in clear text in authldaprc when defining LDAP_BINDPW. There are security issues with putting the root LDAP password in clear text, so definitely use LDAP_AUTHBIND if you can.

The last change is to enable the IMAP server by setting the IMAPDSTART parameter to YES. You should now be able to use the courier-imap.sysvinit startup script to start and stop the IMAP dæmon.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

What about procmail??

Atrillanes's picture

Have I missed something in your article? Or you have just omitted the part where procmail gets into the picture?

dn of the user objects

Ahmed El Zein's picture

Why is the dn of the users mail=user@domain,o=domain,etc? would it not make more sense to have a dn of cn=user,o=domain,etc (like you did for the postmaster)?

The only reason I can think of if to be able to search through the whole tree by email address. wouldn't it be faster to separate user and domain and just search under the o=domain? especially if you have a huge amount of users for each domain?

Re: Large-Scale Mail with Postfix, OpenLDAP and Courier

Anonymous's picture

I don't see a reply to the previous comment. I'm having a similar problem.

Given the format of the cn=postmaster,o=domain1.example..., I suspect that objectClass: organizationalRole needs to be present.

Another question, however, is why are the "mail:" attributes empty? They are required fields and the LDAP server balks at there being no entry there.

Re: Large-Scale Mail with Postfix, OpenLDAP and Courier

Anonymous's picture

Since OpenLDAP 2.2 (I don't know if earlier too) LDAP entities must have "structural" object class. Classes top, CourierMailAccount, CourierMailAlias are "auxiliary" clasess. You should add to each entity "structural" class: domain, organization, organizationalPerson, etc.
So eg.

dn: dc=myhosting, dc=example
objectClass: top

should be

dn: dc=myhosting, dc=example
objectClass: domain
dc: myhosting

You can also change those objectClasses to STRUCTURAL

Darian's picture modifying authldap.schema and replacing "AUXILIARY" with "STRUCTURAL" on the CourierMailAccount, CourierMailAlias, and CourierDomainAlias.

Keep in mind, however, that you can only have one structural objectClass, just in case you plan on hanging e-mail information off of an inetOrgPerson or somesuch.

Re: Large-Scale Mail with Postfix, OpenLDAP and Courier

Anonymous's picture

Hi, I'm trying to follow your example, but I'm having a couple of problems here...

in the first ldif (base.ldif), I had to take out all the first spaces in the lines, and when I try to make an ldapadd I'm getting:

dapadd: update failed: dc=myhosting, dc=example
ldap_add: Object class violation (65)
additional info: no structural object class provided

any suggestions?

Re: Large-Scale Mail with Postfix, OpenLDAP and Courier

Anonymous's picture

This article looks abandoned ... but .. I am having the same problem and really don't know what to do here.

Re: Large-Scale Mail with Postfix, OpenLDAP and Courier

Anonymous's picture

Dear friend

Hi and thanks for the solution provided by you
but do you have any solution for multiple servers running under one domain. with the helpof ldap server database replication.

amit jain

Geek Guide
The DevOps Toolbox

Tools and Technologies for Scale and Reliability
by Linux Journal Editor Bill Childers

Get your free copy today

Sponsored by IBM

Upcoming Webinar
8 Signs You're Beyond Cron

Scheduling Crontabs With an Enterprise Scheduler
11am CDT, April 29th
Moderated by Linux Journal Contributor Mike Diehl

Sign up now

Sponsored by Skybot