OpenLDAP Server

The Lightweight Directory Access Protocol, or LDAP, is a protocol for querying and modifying a X.500-based directory service running over TCP/IP. The current LDAP version is LDAPv3, as defined in RFC4510, and the implementation in Ubuntu is OpenLDAP."

So the LDAP protocol accesses LDAP directories. Here are some key concepts and terms:

  • A LDAP directory is a tree of data entries that is hierarchical in nature and is called the Directory Information Tree (DIT).

  • An entry consists of a set of attributes.

  • An attribute has a type (a name/description) and one or more values.

  • Every attribute must be defined in at least one objectClass.

  • Attributes and objectclasses are defined in schemas (an objectclass is actually considered as a special kind of attribute).

  • Each entry has a unique identifier: its Distinguished Name (DN or dn). This, in turn, consists of a Relative Distinguished Name (RDN) followed by the parent entry’s DN.

  • The entry’s DN is not an attribute. It is not considered part of the entry itself.


The terms object, container, and node have certain connotations but they all essentially mean the same thing as entry, the technically correct term.

For example, below we have a single entry consisting of 11 attributes where the following is true:

  • DN is “cn=John Doe,dc=example,dc=com”

  • RDN is “cn=John Doe”

  • parent DN is “dc=example,dc=com”

 dn: cn=John Doe,dc=example,dc=com
 cn: John Doe
 givenName: John
 sn: Doe
 telephoneNumber: +1 888 555 6789
 telephoneNumber: +1 888 555 1232
 manager: cn=Larry Smith,dc=example,dc=com
 objectClass: inetOrgPerson
 objectClass: organizationalPerson
 objectClass: person
 objectClass: top

The above entry is in LDIF format (LDAP Data Interchange Format). Any information that you feed into your DIT must also be in such a format. It is defined in RFC2849.

Although this guide will describe how to use it for central authentication, LDAP is good for anything that involves a large number of access requests to a mostly-read, attribute-based (name:value) backend. Examples include an address book, a list of email addresses, and a mail server’s configuration.


Install the OpenLDAP server daemon and the traditional LDAP management utilities. These are found in packages slapd and ldap-utils respectively.

The installation of slapd will create a working configuration. In particular, it will create a database instance that you can use to store your data. However, the suffix (or base DN) of this instance will be determined from the domain name of the host. If you want something different, you can change it right after the installation when you still don’t have any useful data.


This guide will use a database suffix of dc=example,dc=com.

Proceed with the install:

sudo apt install slapd ldap-utils

If you want to change your DIT suffix, now would be a good time, because changing it discards your existing one. To change the suffix, run the following command:

sudo dpkg-reconfigure slapd

To switch your DIT suffix to dc=example,dc=com, for example, so you can follow this guide more closely, answer when asked about the DNS domain name.

Since Ubuntu 8.10 slapd is designed to be configured within slapd itself by dedicating a separate DIT for that purpose. This allows one to dynamically configure slapd without the need to restart the service. This configuration database consists of a collection of text-based LDIF files located under /etc/ldap/slapd.d. This way of working is known by several names: the slapd-config method, the RTC method (Real Time Configuration), or the cn=config method. You can still use the traditional flat-file method (slapd.conf) but it’s not recommended; the functionality will be eventually phased out.


Ubuntu now uses the slapd-config method for slapd configuration and this guide reflects that.

During the install you were prompted to define administrative credentials. These are LDAP-based credentials for the rootDN of your database instance. By default, this user’s DN is cn=admin,dc=example,dc=com. Also by default, there is no administrative account created for the slapd-config database and you will therefore need to authenticate externally to LDAP in order to access it. We will see how to do this later on.

Some classical schemas (cosine, nis, inetorgperson) come built-in with slapd nowadays. There is also an included “core” schema, a pre-requisite for any schema to work.

Post-install Inspection

The installation process set up 2 DITs. One for slapd-config and one for your own data (dc=example,dc=com). Let’s take a look.

  • This is what the slapd-config database/DIT looks like. Recall that this database is LDIF-based and lives under /etc/ldap/slapd.d:



    Do not edit the slapd-config database directly. Make changes via the LDAP protocol (utilities).

  • This is what the slapd-config DIT looks like via the LDAP protocol:

    sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config dn
    dn: cn=config
    dn: cn=module{0},cn=config
    dn: cn=schema,cn=config
    dn: cn={0}core,cn=schema,cn=config
    dn: cn={1}cosine,cn=schema,cn=config
    dn: cn={2}nis,cn=schema,cn=config
    dn: cn={3}inetorgperson,cn=schema,cn=config
    dn: olcBackend={0}mdb,cn=config
    dn: olcDatabase={-1}frontend,cn=config
    dn: olcDatabase={0}config,cn=config
    dn: olcDatabase={1}mdb,cn=config

    Explanation of entries:

    • cn=config: global settings

    • cn=module{0},cn=config: a dynamically loaded module

    • cn=schema,cn=config: contains hard-coded system-level schema

    • cn={0}core,cn=schema,cn=config: the hard-coded core schema

    • cn={1}cosine,cn=schema,cn=config: the cosine schema

    • cn={2}nis,cn=schema,cn=config: the nis schema

    • cn={3}inetorgperson,cn=schema,cn=config: the inetorgperson schema

    • olcBackend={0}mdb,cn=config: the ‘mdb’ backend storage type

    • olcDatabase={-1}frontend,cn=config: frontend database, default settings for other databases

    • olcDatabase={0}config,cn=config: slapd configuration database (cn=config)

    • olcDatabase={1}mdb,cn=config: your database instance (dc=example,dc=com)

  • This is what the dc=example,dc=com DIT looks like:

    ldapsearch -x -LLL -H ldap:/// -b dc=example,dc=com dn
    dn: dc=example,dc=com
    dn: cn=admin,dc=example,dc=com

    Explanation of entries:

    • dc=example,dc=com: base of the DIT

    • cn=admin,dc=example,dc=com: administrator (rootDN) for this DIT (set up during package install)

Modifying/Populating your Database

Let’s introduce some content to our database. We will add the following:

  • a node called People (to store users)

  • a node called Groups (to store groups)

  • a group called miners

  • a user called john

Create the following LDIF file and call it add_content.ldif:

dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups

dn: cn=miners,ou=Groups,dc=example,dc=com
objectClass: posixGroup
cn: miners
gidNumber: 5000

dn: uid=john,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: john
sn: Doe
givenName: John
cn: John Doe
displayName: John Doe
uidNumber: 10000
gidNumber: 5000
userPassword: johnldap
gecos: John Doe
loginShell: /bin/bash
homeDirectory: /home/john


It’s important that uid and gid values in your directory do not collide with local values. Use high number ranges, such as starting at 5000. By setting the uid and gid values in ldap high, you also allow for easier control of what can be done with a local user vs a ldap one. More on that later.

Add the content:

ldapadd -x -D cn=admin,dc=example,dc=com -W -f add_content.ldif

Enter LDAP Password: ********
adding new entry "ou=People,dc=example,dc=com"

adding new entry "ou=Groups,dc=example,dc=com"

adding new entry "cn=miners,ou=Groups,dc=example,dc=com"

adding new entry "uid=john,ou=People,dc=example,dc=com"

We can check that the information has been correctly added with the ldapsearch utility:

ldapsearch -x -LLL -b dc=example,dc=com 'uid=john' cn gidNumber

dn: uid=john,ou=People,dc=example,dc=com
cn: John Doe
gidNumber: 5000

Explanation of switches:

  • -x: “simple” binding; will not use the default SASL method

  • -LLL: disable printing extraneous information

  • uid=john: a “filter” to find the john user

  • cn gidNumber: requests certain attributes to be displayed (the default is to show all attributes)

Modifying the slapd Configuration Database

The slapd-config DIT can also be queried and modified. Here are a few examples.

  • Use ldapmodify to add an “Index” (DbIndex attribute) to your {1}mdb,cn=config database (dc=example,dc=com). Create a file, call it uid_index.ldif, with the following contents:

    dn: olcDatabase={1}mdb,cn=config
    add: olcDbIndex
    olcDbIndex: mail eq,sub

    Then issue the command:

    sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f uid_index.ldif
    modifying entry "olcDatabase={1}mdb,cn=config"

    You can confirm the change in this way:

    sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
    cn=config '(olcDatabase={1}mdb)' olcDbIndex
    dn: olcDatabase={1}mdb,cn=config
    olcDbIndex: objectClass eq
    olcDbIndex: cn,uid eq
    olcDbIndex: uidNumber,gidNumber eq
    olcDbIndex: member,memberUid eq
    olcDbIndex: mail eq,sub
  • Let’s add a schema. It will first need to be converted to LDIF format. You can find unconverted schemas in addition to converted ones in the /etc/ldap/schema directory.


    • It is not trivial to remove a schema from the slapd-config database. Practice adding schemas on a test system.

    • Before adding any schema, you should check which schemas are already installed (shown is a default, out-of-the-box output):

      sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
      cn=schema,cn=config dn
      dn: cn=schema,cn=config
      dn: cn={0}core,cn=schema,cn=config
      dn: cn={1}cosine,cn=schema,cn=config
      dn: cn={2}nis,cn=schema,cn=config
      dn: cn={3}inetorgperson,cn=schema,cn=config

    In the following example we’ll add the CORBA schema.

    Create the conversion configuration file schema_convert.conf containing the following lines:

    include /etc/ldap/schema/core.schema
    include /etc/ldap/schema/collective.schema
    include /etc/ldap/schema/corba.schema
    include /etc/ldap/schema/cosine.schema
    include /etc/ldap/schema/duaconf.schema
    include /etc/ldap/schema/dyngroup.schema
    include /etc/ldap/schema/inetorgperson.schema
    include /etc/ldap/schema/java.schema
    include /etc/ldap/schema/misc.schema
    include /etc/ldap/schema/nis.schema
    include /etc/ldap/schema/openldap.schema
    include /etc/ldap/schema/ppolicy.schema
    include /etc/ldap/schema/ldapns.schema
    include /etc/ldap/schema/pmi.schema

    Create the output directory ldif_output.

    Determine the index of the schema:

    slapcat -f schema_convert.conf -F ldif_output -n 0 | grep corba,cn=schema


    When slapd ingests objects with the same parent DN it will create an index for that object. An index is contained within braces: {X}.

    Use slapcat to perform the conversion:

    slapcat -f schema_convert.conf -F ldif_output -n0 -H \
    ldap:///cn={2}corba,cn=schema,cn=config -l cn=corba.ldif

    The converted schema is now in cn=corba.ldif

    Edit cn=corba.ldif to arrive at the following attributes:

    dn: cn=corba,cn=schema,cn=config
    cn: corba

    Also remove the following lines from the bottom:

    structuralObjectClass: olcSchemaConfig
    entryUUID: 52109a02-66ab-1030-8be2-bbf166230478
    creatorsName: cn=config
    createTimestamp: 20110829165435Z
    entryCSN: 20110829165435.935248Z#000000#000#000000
    modifiersName: cn=config
    modifyTimestamp: 20110829165435Z

    Your attribute values will vary.

    Finally, use ldapadd to add the new schema to the slapd-config DIT:

    sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f cn\=corba.ldif
    adding new entry "cn=corba,cn=schema,cn=config"

    Confirm currently loaded schemas:

    sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn
    dn: cn=schema,cn=config
    dn: cn={0}core,cn=schema,cn=config
    dn: cn={1}cosine,cn=schema,cn=config
    dn: cn={2}nis,cn=schema,cn=config
    dn: cn={3}inetorgperson,cn=schema,cn=config
    dn: cn={4}corba,cn=schema,cn=config


For external applications and clients to authenticate using LDAP they will each need to be specifically configured to do so. Refer to the appropriate client-side documentation for details.


Activity logging for slapd is indispensible when implementing an OpenLDAP-based solution yet it must be manually enabled after software installation. Otherwise, only rudimentary messages will appear in the logs. Logging, like any other slapd configuration, is enabled via the slapd-config database.

OpenLDAP comes with multiple logging subsystems (levels) with each one containing the lower one (additive). A good level to try is stats. The slapd-config man page has more to say on the different subsystems.

Create the file logging.ldif with the following contents:

dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: stats

Implement the change:

sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f logging.ldif

This will produce a significant amount of logging and you will want to throttle back to a less verbose level once your system is in production. While in this verbose mode your host’s syslog engine (rsyslog) may have a hard time keeping up and may drop messages:

rsyslogd-2177: imuxsock lost 228 messages from pid 2547 due to rate-limiting

You may consider a change to rsyslog’s configuration. In /etc/rsyslog.conf, put:

# Disable rate limiting
# (default is 200 messages in 5 seconds; below we make the 5 become 0)
$SystemLogRateLimitInterval 0

And then restart the rsyslog daemon:

sudo systemctl restart syslog.service


The LDAP service becomes increasingly important as more networked systems begin to depend on it. In such an environment, it is standard practice to build redundancy (high availability) into LDAP to prevent havoc should the LDAP server become unresponsive. This is done through LDAP replication.

Replication is achieved via the Syncrepl engine. This allows changes to be synchronized using a Consumer - Provider model. The specific kind of replication we will implement in this guide is a combination of the following modes: refreshAndPersist and delta-syncrepl. This has the Provider push changed entries to the Consumer as soon as they’re made but, in addition, only actual changes will be sent, not entire entries.

Provider Configuration

Begin by configuring the Provider.

Create an LDIF file with the following contents and name it provider_sync.ldif:

# Add indexes to the frontend db.
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryCSN eq
add: olcDbIndex
olcDbIndex: entryUUID eq

#Load the syncprov and accesslog modules.
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov
add: olcModuleLoad
olcModuleLoad: accesslog

# Accesslog database definitions
dn: olcDatabase={2}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {2}mdb
olcDbDirectory: /var/lib/ldap/accesslog
olcSuffix: cn=accesslog
olcRootDN: cn=admin,dc=example,dc=com
olcDbIndex: default eq
olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart

# Accesslog db syncprov.
dn: olcOverlay=syncprov,olcDatabase={2}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE
olcSpReloadHint: TRUE

# syncrepl Provider for primary db
dn: olcOverlay=syncprov,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE

# accesslog overlay definitions for primary db
dn: olcOverlay=accesslog,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=accesslog
olcAccessLogOps: writes
olcAccessLogSuccess: TRUE
# scan the accesslog DB every day, and purge entries older than 7 days
olcAccessLogPurge: 07+00:00 01+00:00

Change the rootDN in the LDIF file to match the one you have for your directory.

Create a directory:

sudo -u openldap mkdir /var/lib/ldap/accesslog

Add the new content:

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f provider_sync.ldif

The Provider is now configured.

Consumer Configuration

And now configure the Consumer.

Install the software by going through Installation. Make sure the slapd-config database is identical to the Provider’s. In particular, make sure schemas and the databse suffix are the same.

Create an LDIF file with the following contents and name it consumer_sync.ldif:

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryUUID eq
add: olcSyncRepl
olcSyncRepl: rid=0 provider=ldap:// bindmethod=simple binddn="cn=admin,dc=example,dc=com"
  credentials=secret searchbase="dc=example,dc=com" logbase="cn=accesslog"
  logfilter="(&(objectClass=auditWriteObject)(reqResult=0))" schemachecking=on
  type=refreshAndPersist retry="60 +" syncdata=accesslog
add: olcUpdateRef
olcUpdateRef: ldap://

Ensure the following attributes have the correct values:

  • provider (Provider server’s hostname – in this example – or IP address)

  • binddn (the admin DN you’re using)

  • credentials (the admin DN password you’re using)

  • searchbase (the database suffix you’re using)

  • olcUpdateRef (Provider server’s hostname or IP address)

  • rid (Replica ID, an unique 3-digit that identifies the replica. Each consumer should have at least one rid)

Add the new content:

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f consumer_sync.ldif

You’re done. The two databases (suffix: dc=example,dc=com) should now be synchronizing.


Once replication starts, you can monitor it by running

ldapsearch -z1 -LLLQY EXTERNAL -H ldapi:/// -s base -b dc=example,dc=com contextCSN

dn: dc=example,dc=com
contextCSN: 20120201193408.178454Z#000000#000#000000

on both the provider and the consumer. Once the output (20120201193408.178454Z#000000#000#000000 in the above example) for both machines match, you have replication. Every time a change is done in the provider, this value will change and so should the one in the consumer(s).

If your connection is slow and/or your ldap database large, it might take a while for the consumer’s contextCSN match the provider’s. But, you will know it is progressing since the consumer’s contextCSN will be steadly increasing.

If the consumer’s contextCSN is missing or does not match the provider, you should stop and figure out the issue before continuing. Try checking the slapd (syslog) and the auth log files in the provider to see if the consumer’s authentication requests were successful or its requests to retrieve data (they look like a lot of ldapsearch statements) return no errors.

To test if it worked simply query, on the Consumer, the DNs in the database:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b dc=example,dc=com dn

You should see the user ‘john’ and the group ‘miners’ as well as the nodes ‘People’ and ‘Groups’.

Access Control

The management of what type of access (read, write, etc) users should be granted to resources is known as access control. The configuration directives involved are called access control lists or ACL.

When we installed the slapd package various ACL were set up automatically. We will look at a few important consequences of those defaults and, in so doing, we’ll get an idea of how ACLs work and how they’re configured.

To get the effective ACL for an LDAP query we need to look at the ACL entries of the database being queried as well as those of the special frontend database instance. The ACLs belonging to the latter act as defaults in case those of the former do not match. The frontend database is the second to be consulted and the ACL to be applied is the first to match (“first match wins”) among these 2 ACL sources. The following commands will give, respectively, the ACLs of the mdb database (“dc=example,dc=com”) and those of the frontend database:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
cn=config '(olcDatabase={1}mdb)' olcAccess

dn: olcDatabase={1}mdb,cn=config
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=shadowLastChange by self write by * read
olcAccess: {2}to * by * read


The rootDN always has full rights to its database and does not need to be included in any ACL.

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
cn=config '(olcDatabase={-1}frontend)' olcAccess

dn: olcDatabase={-1}frontend,cn=config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by * break
olcAccess: {1}to dn.exact="" by * read
olcAccess: {2}to dn.base="cn=Subschema" by * read

The very first two ACLs are crucial:

olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=shadowLastChange by self write by * read

This can be represented differently for easier digestion:

to attrs=userPassword
    by self write
    by anonymous auth
    by * none

to attrs=shadowLastChange
    by self write
    by * read

These ACLs enforce the following:

  • Anonymous ‘auth’ access is provided to the userPassword attribute so that users can authenticate, or bind. Perhaps counter-intuitively, ‘by anonymous auth’ is needed even when anonymous access to the DIT is unwanted, otherwise this would be a chicken and egg problem: before authentication, all users are anonymous.

  • The by self write ACL grants write access to the userPassword attribute to users who authenticated as the dn where the attribute lives. In other words, users can update the userPassword attribute of their own entries.

  • The userPassword attribute is otherwise unaccessible by all other users, with the exception of the rootDN, who always has access and doesn’t need to be mentioned explicitly.

  • In order for users to change their own password, using passwd or other utilities, the user’s own shadowLastChange attribute needs to be writable. All other directory users get to read this attribute’s contents.

This DIT can be searched anonymously because of ‘to * by * read’ in this ACL, which grants read access to everything else, by anyone (including anonymous):

to *
    by * read

If this is unwanted then you need to change the ACLs. To force authentication during a bind request you can alternatively (or in combination with the modified ACL) use the ‘olcRequire: authc’ directive.

As previously mentioned, there is no administrative account (“rootDN”) created for the slapd-config database. There is, however, a SASL identity that is granted full access to it. It represents the localhost’s superuser (root/sudo). Here it is:


The following command will display the ACLs of the slapd-config database:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
cn=config '(olcDatabase={0}config)' olcAccess

dn: olcDatabase={0}config,cn=config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,
              cn=external,cn=auth manage by * break

Since this is a SASL identity we need to use a SASL mechanism when invoking the LDAP utility in question and we have seen it plenty of times in this guide. It is the EXTERNAL mechanism. See the previous command for an example. Note that:

You must use sudo to become the root identity in order for the ACL to match.

The EXTERNAL mechanism works via IPC (UNIX domain sockets). This means you must use the ldapi URI format.

A succinct way to get all the ACLs is like this:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b \
cn=config '(olcAccess=*)' olcAccess olcSuffix

There is much to say on the topic of access control. See the man page for slapd.access.

Last updated a day ago. Help improve this document in the forum.