Tools OpenLDAP Lead image: Lead Image © tiero, 123RF.com
Lead Image © tiero, 123RF.com
 

OpenLDAP server workshop

Central Register

Centralized user management with LDAP or Active Directory is the standard today, although many prefer to manage user data manually rather than build this kind of infrastructure. In this article, we look at a better approach with OpenLDAP. By Ulrich Habel

Today's OpenLDAP server was created by Kurt Zeilenga in 1998 as a clone of source code for the LDAP (Lightweight Directory Access Protocol) server at the University of Michigan. Interestingly, the OpenLDAP project has never been dormant but has evolved constantly and is therefore still as progressive and groundbreaking as ever. However, strict enforcement of the numerous changes also puts off some users.

Usually, changes of elemental components in a version are marked as deprecated with an appropriate warning, indicating which functionality will no longer be available in the next version. Although this means ensuring progress for the project, for the administrator, it means always having to keep on your toes.

The OpenLDAP server has a long history in the Unix world. The beginnings of the project date back to 1998 when the issue of central user administration was only taken seriously in the enterprise environment. Small isolated solutions were then the basis for central user administration; directory servers were only available from major IT vendors. Older readers will perhaps smile when they think back to the beginnings of domain management on Windows NT, Novell Netware, or NIS.

A mature service was also available in the form of X.500, but it was not very widespread in practice. LDAP was originally designed as a protocol for X.500 services. This mutated into the LDAP directory servers that are seen in a variety of uses today.

What Is a Directory Server?

A directory server provides a container for information that can be queried via the LDAP protocol and matching clients. Some people compare this with a phone book, but the comparison is tenuous. Although an LDAP server can contain contact information for the company, it can also be enriched with additional information. Ultimately, however, the type of information is not specified, so the directory could accommodate a product catalog or an inventory list.

A directory server is always useful whenever information is to be stored in a tree-like structure with corresponding sub-branches. The tree structure is referred to as the DIT (Directory Information Tree). Each item of information stored within the tree can contain a set of attributes, some of which are mandatory and others optional. The schema determines which attributes are available. The OpenLDAP server provides its own configuration in a DIT, for example.

In this article, you will learn how to install and commission OpenLDAP server version 2.4.23 on CentOS 6.5. As an example, I will authenticate users of a web server, although the configuration can also be extended for operating systems or other services. At the end of the article, you will have a fully functional LDAP server for the enterprise that is easily extensible and reflects the current state of CentOS 6.5 and OpenLDAP 2.4, without having to piece together the configuration.

Installing the OpenLDAP Server

Installing OpenLDAP is easy. All required packages are found in the CentOS repositories and are thus available in any CentOS installation without further change. Using the Yum package manager, the install takes just a single command line:

$ sudo yum install openldap-serversopenldap-clients httpd ldapvi

The first two packages are self-explanatory and are required to install and manage the OpenLDAP server. The web server, httpd, is used in the course of the tutorial to demonstrate authentication and authorization of a web server location against the LDAP server. The ldapvi tool is a universal, command-line LDAP client ideal for smaller administrative tasks.

Configuration via OLC

OpenLDAP version 2.4, which is the one in the CentOS repository, changed to the dynamic configuration model known as OLC (On-Line Configuration). In the various bits of documentation for OpenLDAP, you will repeatedly see references to the cn = config method, which means the same thing.

The cn=config model stores the configuration data on the LDAP server which is processed by the LDAP client tools. In the old model, the OpenLDAP server was still managed via a central configuration file. The reasons for the change, which at first sight make everything more complicated, become apparent upon second inspection.

Changes can be made on the fly, without requiring a server restart. Especially for larger installations, however, restarting is relatively time-consuming and can take several minutes. Additionally, the LDAP server loses its cache, which resides in main memory, on a restart. Requests then take longer until the cache is repopulated.

The dynamic configuration model removes the need for reboots and ensures availability of the LDAP server. Once you understand the concept behind the new configuration model, it also feels more coherent. In the old model, the configuration files were stored in a directory tree below /etc/openldap/slapd.d; it took priority over an /etc/openldap/slapd.conf configuration file that you might happen to have. In this article, I focus exclusively on the new model.

After the installation, some steps are required before you can start the LDAP server daemon, slapd. First, you must decide on a data back end. OpenLDAP supports a variety of back ends, starting with Berkeley DB (BDB; the default), MySQL, Memory databases, or even Perl data structures. In the sample configuration here, the LDAP server uses BDB.

The following steps must be run with root privileges. Instead of working directly as the root user, all commands run with sudo. The OpenLDAP server includes a configuration template to help you create an initial database. Copy it to the data directory on the OpenLDAP server:

$ sudo cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG

The template contains information about cache size and database logfiles. However, all these values can be modified later on, thanks to the dynamic configuration model. After copying the file, check to see that the OpenLDAP server is configured properly and then start it with:

$ sudo slaptest -u
$ sudo chkconfig slapd on
$ service slapd start

Voilà, the OpenLDAP server is ready for an initial configuration. Currently, even though the server is running, no one can connect.

Authentication

First, create the LDAP RootDN password. RootDN is the top node in an LDAP directory and can basically change all the nodes below it – it's practically the root user of the LDAP system.

The password can be generated using the slappasswd command. The following command sets the password to secret and returns the SHA hash of the password at the command line:

$ sudo slappasswd -s secret
{SSHA}f0pv70XFFox5UqKc6A4Uy39NcxkqcJbc

The complete line returned will be needed later for the configuration file, so copy it to a temporary editor window.

At the present time, the slapd daemon still has a very rudimentary configuration and cannot fulfill any meaningful tasks. Nevertheless, looking at the directory information tree in its current form can be educational. Once the LDAP server is running, you can formulate a search query using the ldapsearch command and send it to the server:

$ sudo ldapsearch -b cn=config -Y EXTERNAL -H ldapi:// '(objectClass=olcDatabaseConfig)' olcDatabase

The ldapsearch command uses a socket (ldapi) to connect to the LDAP server and start a search. The first part of the server's response looks like this:

SASL/EXTERNAL authentication started
SASL username:
gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth

The authentication method is specified by the -Y EXTERNAL option. It tells the LDAP server not to authenticate against the data on the LDAP server, but to make the decision on the basis of the user ID or other criteria. A standard for this form of authentication is already pre-configured in OpenLDAP, and that standard is used here.

The user with the user ID 0 and the GroupID 0 can log on (i.e., the root user). This is why the command needs a sudo prefix. Right now, this is the only way to log on to the LDAP server.

The other lines of output provide information about the search and the type of output:

# extended LDIF
# LDAPv3
# base <cn=config> with scope subtree
# filter: (objectClass=olcDatabaseConfig)
# requesting: olcDatabase

The extended LDIF format is the default in LDAP protocol version 3. The base line indicates where the search started. In this case, the node with the common name (cn) config was searched.

The scope (i.e., the search area) provides information about the depth of the search. The subtree keyword indicates a search for everything below the base. The search filter was set to an objectClass – in this case, olcDatabaseConfig. An object class in LDAP is a data container that is filled with attributes.

Additionally, you can see the two most interesting databases in the output:

# {0}config, config
dn: olcDatabase={0}config,cn=config
olcDatabase: {0}config
# {2}bdb, config
dn: olcDatabase={2}bdb,cn=config
olcDatabase: {2}bdb

The first element is the configuration database, then comes the ({2}bdb) database, which will later take the user data. The response to the request is rounded off by some statistics on the search and the results:

# search result
search: 2
result: 0 Success
# numResponses: 5
# numEntries: 4

Basic Configuration

Now that the LDAP server is running, it's time for the actual configuration to make it really usable. The configuration in this example is divided into two parts: the configuration server and the configuration for the later LDAP data. There is a strict demarcation between the different management tasks for the LDAP server. The administrator of the Linux system can manage the configuration of the LDAP server; the administrator of the LDAP directory (the "Manager") only has rights for these data.

A demarcation of this kind is often useful in production operation, because a clear distinction is made between the different tasks. Thus, it is possible to delegate application support for the LDAP server to another department without sacrificing the integrity of the server in terms of security.

The first step is to configure the LDAP services. The configuration file is a text file in LDIF format; the ldapmodify command parses it. The structure of the file, which is shown in Listing 1, references the entry to be changed within the LDAP branch, specifies the type of change, and concludes with the new value.

Listing 1: initial.ldif

01 dn: olcDatabase={0}config,cn=config
02 changetype: modify
03 replace: olcAccess
04 olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" \
  write by dn.base="cn=manager,dc=acme-services,dc=org" read by * none
05
06 # replace: olcRootDN
07 dn: olcDatabase={2}bdb,cn=config
08 changetype: modify
09 replace: olcRootPW
10 olcRootPW: {SSHA}f0pv70XFFox5UqKc6A4Uy39NcxkqcJbc
11 -
12 replace: olcAccess
13 olcAccess: {0}to attrs=userPassword by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" \
  write by dn.base="cn=manager,dc=acme-services,dc=org" \
  write by anonymous auth by self \
  write by dn="uid=syncrepl,ou=systems,dc=acme-services,dc=org" \
  read by * none
14 olcAccess: {1}to * by self write by users read by * none
15 -
16 replace: olcSuffix
17 olcSuffix: dc=acme-services,dc=org
18 -
19 replace: olcRootDN
20 olcRootDN: cn=manager,dc=acme-services,dc=org
21
22 dn: olcDatabase={1}monitor,cn=config
23 changetype: modify
24 replace: olcAccess
25 olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=manager,dc=acme-services,dc=org" read by * @KE:

The initial configuration file references the entry points for the changes with the DN, the distinguished name. It always corresponds to the full path of the OpenLDAP server within the DIT and must therefore be unique. An overview of the different abbreviations and their meanings can be found in Table 1.

Tabelle 1: LDAP Keywords

Abbreviation

Full Name

Meaning

DIT

Directory information tree

Information tree on LDAP server

CN

Common name

Attribute of an object in the LDAP tree

DN

Distinguished name

Complete path of an object within the LDAP tree

DC

Domain component

Component of a top-level node in the LDAP tree

The initial configuration performs a variety of settings. For example, it allows the application administrator (manager) to access the information tree on the OpenLDAP server, which means the configuration can be read but not modified by this user. This approach has proven useful for troubleshooting, because you do not need to jump back and forth between different rights. The second block defines the password for the manager user, which was generated earlier with slappasswd. In this configuration example, the password is secret; for production use, you would want to replace this with something more meaningful.

Access Rules

The third block contains the rules for access. They allow write access for the root user via system authorization and for the manager user. Additionally, each user is allowed to authenticate and then change their own data and view all data – except for the user password attribute.

The fourth block sets the suffix for the LDAP server. It is usually made up of the server or company domain components. Dots act as separator characters between the parts of the domain component (DC). The suffix from the example, dc=acme-services,dc=org, therefore corresponds to the acme-services.org domain. However, it has only an organizing function and must be a unique identifier. It is not necessary to use the LDAP server's real domain.

The fourth block contains the internal OpenLDAP service monitor. It can be used later to retrieve status information for the server. After all the parts of the configuration have been modified, you can parse the configuration file:

$ sudo ldapmodify -Y EXTERNAL -H ldapi:// -f initial.ldif

Again, you need to be root for this.

Hardening the Server

The server is now ready for use but is still completely insecure because it accepts unencrypted requests on port 389. When the manager connects with the server from a workstation, the password is transmitted in plain text. To protect the server against unauthorized access and prevent the password being sniffed off the network, encryption should be configured as soon as possible. You have basically two approaches. The first solution uses a self-signed certificate that is generated using the scripts supplied with the OpenLDAP package. The clients then later trust the server certificate. If you already operate a certification authority, you can take a different approach, but more on that later.

The easy option is to use the supplied scripts, which you must run as the root user. The first script builds a certificate database in the /etc/openldap/certs directory:

$ sudo /usr/libexec/openldap/create-certdb.sh
Creating certificate database in '/etc/openldap/certs'.

The second script generates the required certificate and imports it into the certificate store you just created:

$ sudo /usr/libexec/openldap/generate-server-cert.sh -d /etc/openldap/certs -h ldap.acme-services.org

The OpenLDAP server is set up in the default configuration to use this certificate. The certificate is stored as OpenLDAP Server in the certificate database. NSS-based certificates distinguish between the common name, which usually corresponds to the hostname of the server, and the readable name. The names can differ. Therefore, in the above command we specified the hostname of the server with the -h ldap.acme-services.org command-line option. This is where a special feature of the LDAP configuration enters the game: If you want to address the OpenLDAP server with both the StartTLS method, and with traditional SSL, the common name (-h) must match the hostname of the server.

After completing this part of the configuration, you only need to add the ldaps option to the /etc/sysconfig/ldap file. Change the SLAPD_LDAPS line, which is set to no in the basic configuration, to yes. After a reboot, the OpenLDAP server accepts SSL connections (SSL and StartTLS):

$ sudo service slapd restart

Now you can use openssl to check the connection. The command does not trust this certificate yet, and identifies it as self-signed; therefore, you need to export the certificate from the certificate database to the system-wide certificate directory and link with the required certificate hash:

$ sudo certutil -L -d /etc/openldap/certs -n "OpenLDAP Server" -a > /etc/pki/tls/certs/ldap.acme-services.org.crt
$ sudo ln -sf /etc/pki/tls/certs/ldap.acme-services.org.crt $(openssl x509 -in ldap.acme-services.org.crt -noout -hash).0

After these two steps, you can check the server certificate with the openssl command, which provides the s_client command for this purpose:

$ openssl s_client -connect ldap.acme-services.org:636

The certificates are output on the console. The decisive thing here is that the last line should read 0 (ok), given a correct configuration. The command now waits for input. However, since you only want it to test the certificate, you can cancel by pressing Ctrl+C.

This second approach describes the use of your own certificates that will not reside in the OpenLDAP certificate database. It often makes sense not to store certificates there (e.g., if other services need to use them).

CentOS stores certificates in the /etc/pki/tls/certs directory and the keys in /etc/pki/tls/private. It is important to ensure that the group that has access to private keys includes the OpenLDAP server. The OpenLDAP server runs as the ldap user and is a member of the ldap group. It is therefore advisable, to assigned the private key file to the owner root and the ldap group. The permissions need to be set to 640.

Unfortunately, the OpenLDAP server is not aware of this key in the default configuration, so you need to configure this separately. Listing 2 shows a typical configuration. Using the ldapmodify command, you pass the file to the LDAP server, as you do when parsing the initial configuration. Then, for the first approach, set the SLAPD_LDAPS variable in the /etc/sysconfig/ldap file to yes and restart the server. Make sure the private key is not protected with a password.

Listing 2: ssl.ldif

01 dn: cn=config
02 changetype: modify
03 replace: olcTLSCACertificatePath
04 olcTLSCACertificatePath: /etc/pki/tls/certs
05 -
06 replace: olcTLSCertificateFile
07 olcTLSCertificateFile: /etc/pki/tls/certs/localhost.crt
08 -
09 replace: olcTLSCertificateKeyFile
10 olcTLSCertificateKeyFile: /etc/pki/tls/private/localhost.key

For the server to use the new SSL connections, it is helpful to modify the configuration file for the LDAP client /etc/openldap/ldap.conf (Listing 3). When modifying the configuration file, note how to set up SSL encryption. In particular, you may need to adapt the TLS_CACERTDIR parameter to point to the system-wide certificate database.

Listing 3: /etc/openldap/ldap.conf

§nonumber
BASE dc=acme-services,dc=org
URI  ldaps://ldap.acme-services.org
TLS_CACERTDIR /etc/openldap/certs
TLS_REQCERT allow

The server configuration is now complete, and the user database setup can begin.

Directory Structure

In this section, I will be creating the tree for the user data. The top level of this tree has already been established and consists of two domain components (DCs): dc=acme-services and dc=org. All new structures are inserted below this point. The tree should look like Figure 1.

The complete LDAP directory tree.
Figure 1: The complete LDAP directory tree.

Now, you need to generate new LDAP objects and assign them the required attributes. Objects are unique nodes that possess specific object classes as attributes. Object classes can be, for example, organizationalUnit, domain, inetOrgPerson, or posixAccount. The available object classes depend on the schemas that the LDAP server has configured. Figure 2 illustrates this relationship.

Structure of LDAP schemas and object classes.
Figure 2: Structure of LDAP schemas and object classes.

Built-in Schemas

An OpenLDAP server can manage a wide variety of schemas; 10 are installed by default. Examples of other data models include Samba, YubiKey, DHCP, and so on. In this article, I will be working exclusively with the built-in schemas. You create the user tree with an LDIF file. Listing 4 shows the LDIF file that creates the required data in the directory tree.

Listing 4: base.ldif

01 dn: dc=acme-services,dc=org
02 dc: acme-services
03 objectClass: top
04 objectClass: domain
05
06 dn: ou=people,dc=acme-services,dc=org
07 ou: people
08 objectClass:
09
10 dn: ou=groups,dc=acme-services,dc=org
11 ou: groups
12 objectClass: organizationalUnit
13
14 dn: ou=systems,dc=acme-services,dc=org
15 ou: systems
16 objectClass: organizationalUnit
17
18 dn: uid=uhabel,ou=people,dc=acme-services,dc=org
19 objectClass: person
20 objectClass: organizationalPerson
21 objectClass: inetOrgPerson
22 objectClass: posixAccount
23 cn: Ulrich Habel
24 gidNumber: 100
25 homeDirectory: /home/uhabel
26 sn: Habel
27 uid: uhabel
28 uidNumber: 1000
29 userPassword: {SSHA}f0pv70XFFox5UqKc6A4Uy39NcxkqcJbc
30
31 dn: cn=vcsldap,ou=groups,dc=acme-services,dc=org
32 objectClass: groupOfUniqueNames
33 objectClass: top
34 cn: vcsldap
35 uniqueMember: uid=uhabel,ou=people,dc=acme-services,dc=org
36
37 dn: cn=httpd,ou=systems,dc=acme-services,dc=org
38 objectClass: inetOrgPerson
39 objectClass: organizationalPerson
40 objectClass: person
41 objectClass: top
42 cn: httpd
43 sn: httpd Webserver
44 userPassword: {SSHA}f0pv70XFFox5UqKc6A4Uy39NcxkqcJbc

Unlike before, I will now use the ldapadd command and the newly created manager user for the first time. The following command parses the LDIF file:

$ ldapadd -x -W -D cn=manager,dc=acme-services,dc=org -f base.ldif

Notice that now this command no longer needs the root user for the system; instead, any user can run it. To register, you need a password; in this example, it is secret. The user is determined by the command-line option -D, followed by the distinguished name (DN) of the user. Because each DN is unique in the system, the user is also unique.

The -x option in this case indicates that no SASL method is used for authentication; the -w option requires a password entry. After importing the LDIF file, the information tree shown in Figure 1 is completely initialized.

Test and Try

Now you should gain some initial experience with the existing data in LDAP – first, with simple command-line tools, then with more convenient tools. An LDAP search using the command-line tools is tedious. In this example, the user uhabel logs on and looks for objects that have any object class. The result will be all objects of the LDAP server, because each object must at least have an object class:

$ ldapsearch -D uid=uhabel,ou=people,dc=acme-services,dc=org -W -x'(objectClass=*)'

After entering the password, all entries are listed. The user password attribute is only displayed for user uhabel; it remains hidden for the other users. In principle, it is possible to output each node in the tree with a search and then to modify the results with a matching LDIF file. However, this procedure is very time consuming; therefore, different user tools have become established.

Elegant Command Line

The ldapvi [1] command-line tool connects the LDAP search with the vi editor, thus supporting simple changes. When you save and quit the editor, an LDIF file is created and then applied.

The ldapvi tool's command-line options are similar to those of openldap-client, which I already looked at:

$ ldapvi -D cn=Manager,dc=acme-services,dc=org -b dc=acme-services,dc=org -h ldapi://

The preceding command queries the directory tree and presents the results in the editor (Figure 3). This approach allows you to edit entries elegantly and quickly on a remote SSH console.

ldapvi, an elegant command-line tool.
Figure 3: ldapvi, an elegant command-line tool.

Apache Directory Studio

Apache Directory Studio [2] is a graphical client built on the Eclipse framework (Figure 4). Because of the Eclipse platform, the client computer definitely needs a few megabytes of memory. Although stability is questionable, Directory Studio has carved its own niche.

Graphical interface for LDAP: Apache Directory Studio.
Figure 4: Graphical interface for LDAP: Apache Directory Studio.

Administrators who want to develop their own schemas have no way around this tool. The main advantage, in addition to the graphical interface, is support for all imaginable situations in the life of an LDAP administrator.

Directory Hardening Cookbook

The following recipe demonstrates how to protect individual directories on the web server with usernames and passwords stored in LDAP. Instead of painstakingly maintaining individual htpasswd files, user accounts can be centrally managed on the LDAP server.

All of the necessary ingredients for this recipe are on the table and ready for use: All you need to complete the recipe is a user account for the HTTPD server, a group with a group member, and a user who is a member of that group.

The directory I want to protected in this example is /repositories in the root directory of the web server. Therefore, the path is – without making any further changes to the SELinux context – /var/www/html/repositories.

Apache version 2.2 fundamentally changed the way LDAP is used. The installation of additional modules is no longer required; the web server already comes with everything you need to use LDAP in the basic installation. The basis of this recipe is the httpd user account on the LDAP server, which I created earlier.

LDAP Modules Included

Authentication and authorization on the web server is handled by two modules: ldap_module provides the LDAP data structures on the web server, and authnz_ldap_module handles authentication. In the default web server configuration, both modules are already installed and enabled. After creating the directory, you need to configure the /repositories path on the server and enable directory protection. To test this, I'll do:

$ sudo mkdir /var/www/html/repositories
$ echo "Hello world" > index.html
$ sudo cp index.html /var/www/html/repositories/
$ sudo chown -R apache /var/www/html/repositories

Listing 5: /etc/httpd/conf.d/repositories.conf

01 <Location /repositories>
02     AuthType               Basic
03     AuthName               "Repositories"
04     AuthBasicProvider      ldap
05     AuthLDAPBindDN         cn=httpd,ou=systems,dc=acme-services,dc=org
06     AuthLDAPBindPassword   geheim
07     AuthzLDAPAuthoritative Off
08     AuthLDAPURL            ldap://localhost/dc=acme-services,dc=org?uid
09     Require                ldap-group cn=vcsldap,ou=groups,dc=acme-services,dc=org
10 </Location>

The complete configuration shown in Listing 5 was saved in /etc/httpd/conf.d/repositories.conf, and then I restarted the web server. I'll use Curl for the test:

$ curl -sL -w "%{http_code} %{url_effective}\n" http://localhost/repositories/index.html -o /dev/null
401 http://localhost/repositories/index.html

The output correctly shows a 401 error: The directory is protected. But does logging in as a user with the associated password also work? This can be checked easily with the following command:

$ curl -sL -w "%{http_code} %{url_effective}\n" -u uhabel:secret http://localhost/repositories/index.html -o /dev/null
200 http://localhost/repositories/index.html

Voilà, the directory is protected, and everything works as expected.

The web server configuration file has a few special features that are worth investigating. For simplicity's sake I only used ldap in this example, because access was on the localhost and thus routed via the loopback interface.

For production use of web servers that access the OpenLDAP server on the network, use SSL instead (ldaps). Directory protection is implemented here via the vcsldap group and the group members. This means you can set up slightly different groups to protect different areas of the web server.

Future

In this article, I provided a little insight into the world of the OpenLDAP server with the current configuration model (cn=config), and I installed a working OpenLDAP server. Still missing are more users and more applications, but that's reserved for future articles.

The OpenLDAP Server documentation [3] is improving all the time, but the official documentation still often describes the legacy configuration model with the /etc/slapd.conf configuration file. In this case, your only hope is to check the mailing lists, which may shed some light on the issue.