Mailserver with ldap tutorial – part 1: openldap

In this post I’m going to describe how to setup openldap as a user database for my mailserver where also the maildomains, addresses and -boxes are stored.

This is a post from my old blog http://tech.cbjck.de. It has been moved here and slightly edited for better readability. It's also been adjusted to the new layout.
The content however is old and might be outdated.


This is step 1 of the 8 step tutorial for setting up a mailserver with openldap, postfix and dovecot using virtual users. You can find the overview here.

With debian 6.0 “squeeze” the configuration of openldap has moved from sldapd.conf to a directory based configuration in cn=config. This allows changes in the config without restarting slapd but makes handling a bit more complex.

Install slpad as ldap server

aptitude install slapd ldap-utils

Preconfigure slapd

Create an initial database by executing:

dpkg-reconfigure slapd

You should:

  • set the base dn, eg: dc=cbjck,dc=de
  • set the organisation (ie the o= field of the base record
  • passwort for admin (dn cn=admin,dc=cbjck,dc=de)
  • set backend to hdb (default)
  • not allow LDAPv2

Gain access to cn=config

By default your admin user is not allowed to access the ldap configuration stored in cn=config. To allow access here and edit the configuration create a file config.ldif in your working directory:

dn: olcDatabase={-1}frontend,cn=config
changetype: modify
delete: olcAccess

dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=admin,cn=config

dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {SSHA}8S1oP5BG2cYEpxQYcc2YbQxwUBZwxR7v

dn: olcDatabase={0}config,cn=config
changetype: modify
delete: olcAccess

Of course you will replace the password hash by something really strong. Zhe hash used here is for test.pw. You can use the command slappasswd to create it.
Now add alle the settings made in config.ldif into the ldap:

ldapadd -Y EXTERNAL -H ldapi:/// -f config.ldif

You can now use the binddn cn=admin,cn=config to edit cn=config.

A note on editors

There are several possibilities to add entries to ldap or edit them. A very comfortable way is using ldapvi (thanks to Michael Hammer for the hint). Use

ldapvi -h ldap://localhost -D cn=admin,cn=config -b cn=config

for editing the config and

ldapvi -d -h ldap://localhost -D cn=admin,dc=cbjck,dc=de

for editing your ldap-database.
To add and edit entries in the database I prefer Apache Directory Studio which is an ldap browser you are running on a client rather than on the server.
And of course you can create .ldif-files for any of your changes and use ldapmodify to apply them.
I will only describe the changes that need to be made. How you do it is up to you.

Add a ldap schema for postfix

As we want to save maildomains, -addresses and -boxes in the ldap database we need a schema to store these information. There is no official postfix schema but you will find a lot of similar approaches around in the web.
This is the schema I use:

# extended LDIF
#
# LDAPv3
# base <cn={4}postfix,cn=schema,cn=config> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# {4}postfix, schema, config
dn: cn={4}postfix,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: {4}postfix
olcAttributeTypes: {0}( 1.3.6.1.4.1.15347.2.102 NAME 'transport' SUP name)
olcAttributeTypes: {1}( 1.3.6.1.4.1.15347.2.101 NAME 'mailRoutingAddress' SUP 
 mail )
olcAttributeTypes: {2}( 1.3.6.1.4.1.15347.2.110 NAME 'maildest' DESC 'Restrict
 ed to send only to local network' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreS
 ubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
olcAttributeTypes: {3}( 1.3.6.1.4.1.15347.2.111 NAME 'mailaccess' DESC 'Can be
  mailed to restricted groups' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubst
 ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
olcAttributeTypes: {4}( 1.3.6.1.4.1.15347.2.100 NAME ( 'maildrop' ) DESC 'RFC1
 274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Substrin
 gsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
olcAttributeTypes: {5}( 1.3.6.1.4.1.10018.1.1.1 NAME 'mailbox' DESC 'The absol
 ute path to the mailbox for a mail account in a non-default location' EQUALIT
 Y caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.15347.2.1 NAME 'mailUser' DESC 'E-Mail User
 ' SUP top AUXILIARY MUST ( uid $ mail $ maildrop ) MAY ( cn $ mailbox $ maild
 est $ mailaccess ) )
olcObjectClasses: {1}( 1.3.6.1.4.1.15347.2.2 NAME 'mailGroup' DESC 'E-Mail Gro
 up' SUP top STRUCTURAL MUST ( cn $ mail ) MAY ( mailRoutingAddress $ member $
  description ) )
olcObjectClasses: {2}( 1.3.6.1.4.1.15347.2.3 NAME 'transportTable' DESC 'MTA T
 ransport Table' SUP top STRUCTURAL MUST ( cn $ transport ) )

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Add the schema with

ldapadd -D cn=admin,cn=config -W -f postfix.schema.ldif

Create an index

In order to speed up lookups an index should be build. Indexes are setup in cn=config as a parameter of the database. Within olcDatabase={1}hdb,cn=config add

olcDbIndex: cn,sn,uid,displayName pres,sub,eq
olcDbIndex: default sub
olcDbIndex: mail,givenName eq,subinitial
olcDbIndex: objectClass eq
olcDbIndex: uidNumber,gidNumber,memberUid,associatedDomain,maildrop,dc eq

I actually prefer doing this task especially with ldapvi. With ldapvi this is just like editing a config file.

Create database structure

Of course the structure of the ldap tree depends on your conceptual ideas. A quite common (and useful) setup is to store users below ou=people and groups below ou=groups. To specify access to different services you could create ou=services and the put aliases for the users who should be able to access the service below.
For the beginning the following structure will be fine:

ou=groups,dc=cbjck,dc=de
ou: groups
objectClass: top
objectClass: organizationalUnit

ou=people,dc=cbjck,dc=de
ou: people
objectClass: top
objectClass: organizationalUnit

uid=dummy,ou=people,dc=cbjck,dc=de
objectClass: domainRelatedObject
objectClass: inetOrgPerson
objectClass: mailUser
objectClass: top
givenName: Test
sn: Dummy
cn: Test Dummy
uid: dummy
maildrop: dummy@beispiel.de
mailbox: maildir:/var/vmail/cbjck.de/dummy
associatedDomain: beispiel.de
mail: mail@beispiel.de
mail: postmaster@beispiel.de
userPassword: {SSHA}pd/vp7QvOuYizRJu1CuxzoiP2aOXDbon

I’ve replaced the mailadresses by examples and of course a user dummy is not existing on this server 😉

Give dovecot acces to user passwords

As we will use dovecot as sasl-provider, the programm needs read access to the password hashes.
First we create a user cn=dovecot,dc=cbjck,dc=de:

cn=dovecot,dc=cbjck,dc=de
objectClass: simpleSecurityObject
objectClass: organizationalRole
description: dovecot authentication user
cn: dovecot
userPassword: {SSHA}fEwCi8Swv/Atq0F8iE8erRryoDPkc22y

Then we alter the acl to give that user access. Edit olcAccess: {0},olcDatabase={1}hdb,cn=config to

olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=dovecot,dc=cbjck,dc=de" read by self write by anonymous auth by * none

Disable unencrypted acces from outside

In order to make your ldap server more secure you might want to disable unecrypted access from other than localhost. So edit /etc/default/slapd.conf and change the appropriate line to

SLAPD_SERVICES="ldap://127.0.0.1:389/ ldapi:///"

Now the ldap is ready for step 2: dovecot


Edited on Jan 7th 2015 for better readability.
Edited on Aug 18th 2017 for better readability, included postfix_schema.ldif into the text.

1 Comment

  1. This is a awesome tutorial, thanks! I was facing a mistake at the beginning of the LDAP configuration. At the line where you use “ldapadd” as for configuration. On Debian 7 when using the dpkg for reconfiguration a initial config setting will be there after this step. Hence replace the “ldapadd” by ldapmodify this ends in the the following command:
    ldapmodify -Y EXTERNAL -H ldapi:/// -f config.ldif

    Cheers,
    Andreas

Leave a Reply

Your email address will not be published. Required fields are marked *