Open LDAP Server

mirored from http://www.metaconsultancy.com/whitepapers/ldap.htm


It is often convenient to share system information among workstations. For example, users like to be able to log in to multiple machines with the same password; this requires that the machines share the data conventionally stored in /etc/passwd and /etc/shadow. Using NFS usually isn't an option, since that would share all of /etc/, and we may well want some things configured differently on each machine. NIS used to be a common answer to this dilemma, but LDAP (Local Directory Access Protocol) databases, being more flexible and scalable, have now become the preferred solution.

LDAP databases are object-oriented (as opposed to relational, for those of you familiar with such beasts). An LDAP database is filled with objects, each of which has associated attributes. Here, for example is a typical object representing a user account.

dn: uid=john,ou=people,dc=example,dc=com
cn: John Doe
uid: john
uidNumber: 1001
gidNumber: 100
homeDirectory: /home/john
loginShell: /bin/bash
objectClass: top
objectClass: posixAccount

Each object has a DN (distinguished name) attribute that identifies it uniquely. Each object is a member of one or more object classes, which determine which attributes it may and must have, according to a well-defined schema. Objects in an LDAP database are organized into a tree hierarchy, based on their DN. By tradition, the organization example.com uses dc=example,dc=com for its root object (but nothing enforces this tradition). Below the root are objects representing different organizational units, such as people or hosts. The children of these objects are the people and hosts themselves.

We use OpenLDAP, because it's free and works for us. The rest of this whitepaper describes how to set up an OpenLDAP server to hold your organization's user data.

Installation

slapd is the name of the OpenLDAP LDAP server daemon.

apt-get install slapd ldap-utils

Basic Configuration

The basic configuration file for slapd is /etc/ldap/slapd.conf. The file can be loosely divided into two parts: an operational part, describing how the slapd daemon does its job, and a database part, describing the LDAP datastore (or datastores; a single slapd can serve multiple LDAP databases).

Consider first the operational part. A simple example follows.

//slapd.conf//
# schema
include      /etc/ldap/schema/core.schema
include      /etc/ldap/schema/nis.schema
schemacheck  on

# run files
pidfile      /var/run/slapd.pid
argsfile     /var/run/slapd.args

# timeout (in seconds) for dead connections
timeout      60

The schema part loads files which describe which sorts of objects may and must have which properties. Schema files are traditionally stored in the /etc/ldap/schema/ directory. Our example includes only the two most common schema files, but you may well want to load all the schema that ship with OpenLDAP, and even add some more of your own.

By declaring schemacheck on, we tell slapd to require that objects conform to schema files before it accepts them. You should always maintain this requirement, as it helps protect you against accidental errors when updating your database.

The database-specific part of the configuration file tells slapd the database root DN, and how and where to store the data. Here is a simple example:

# ldap database
database     ldbm
suffix       "dc=example,dc=com"
directory    "/var/lib/ldap"

Our example stores data using an embedded database which stores files in /var/lib/ldap. The embedded database (ldbm) option is by far the most common backend, and is the only one we will discuss here. OpenLDAP allows other options, but they are much more difficult to configure.

Securing OpenLDAP

OpenLDAP offers several mechanisms to protect the security of the data it stores. Here we discuss password hashing, access control lists, and connection encryption.

The userPassword attribute is special. Clients use it to authenticate themselves to slapd, and slapd stores it in hashed form. (A hash is a transformation which is simple to do, but difficult to undo.) When a user types in a password, it is hased and compared against the stored hash, rather than being compared directly against a stored cleartext password. Since the cleartext password is not stored anywhere, this trick (which is also used by /etc/shadow) keeps your passwords safe even from someone who has broken in to your LDAP server. You can specify the hash algorithm you want slapd to employ in slapd.conf.

//slapd.conf//

# password hash algorithm
password-hash {MD5}

We like MD5, since the mechanism is both secure and widely employed. SHA and SSHA are also secure, but less widely employed. CRYPT is the most widely employed hashing mechanism in the Unix universe, but is known to be insecure and should be avoided.

It is important to define which users are allowed to read and write which object attributes. These “rights” are defined by ACLs (access control lists) in slapd.conf. Here is a simple example for account information.

//slapd.conf//

# lock down passwords
access to attribute=userPassword
      by dn="cn=admin,dc=example,dc=com" write
      by self write
      by anonymous auth
      by * none

# allow users to write some stuff
access to attribute=loginShell,shadowLastChange
      by dn="cn=admin,dc=example,dc=com" write
      by self write
      by * read

# everything else is read-only
access to *
      by dn="cn=admin,dc=example,dc=com" write
      by * read

It is easy to make security mistakes when setting ACLs. For example, if you give users write-access to uidNumber, they can set it equal to zero to turn themselves into root. Consider carefully the security implications of each access privilege you grant.

By default, LDAP data, including passwords being transmitted for authentication, move across the network in the clear. To protect yourself against eavesdroppers, you should take the simple step of configuring slapd to offer encrypted sessions, and configuring your clients to use them. To enable encrypted sessions, use the openssl utilities to generate a key and corresponding public certificate, and tell slapd to use them for encrypted sessions. slapd.conf

TLSCertificateFile /etc/ldap/ldap.example.crt
TLSCertificateKeyFile /etc/ldap/ldap.example.key
TLSCipherSuite HIGH:+MEDIUM:!LOW

It is important that the CN (common name) on the certificate be exactly the FQDN (fully qualified domain name) which clients use to contact your LDAP server. For example, if your server is named ldap.example.com, that should be the certificate CN, and clients should connect using that name, not the corresponding IP address. Such a restriction is only necessary for encrypted sessions.

The certificate key file should be readable only by the user as which slapd runs (for the Debian package, this is root).

# chmod /etc/ldap/ldap.example.key 0700

OpenLDAP Utilities

There are two classes of utility programs which allow you to modify your OpenLDAP database. The slap* programs (slapadd, slapcat, and slapindex) read and modify an LDAP database by operating directly on the files in /var/lib/ldap. To avoid database corruption, they should be employed only when slapd is not running. The ldap* programs (ldapsearch, ldapadd, ldapmodify, ldapdelete, and ldappasswd) read and modify an LDAP database by interacting with the slapd server. They are essentially very simple LDAP clients. They can only be employed when slapd is running.

offline (slap*) utilities

The slapd* utilities accept ldif files as input. ldif files are just flat textfile lists of objects, with attributes indicated in a key: value format, one per line. Here is an example of a simple ldif file that creates the skeletal structure of a simple LDAP database.

/tmp/example.ldif

dn: dc=example,dc=com
dc: example
o: example.com
objectClass: top
objectClass: dcObject
objectClass: organization

dn: ou=people,dc=example,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit

dn: cn=admin,dc=example,dc=com
cn: admin
userPassword: secret
objectClass: top
objectClass: organizationalRole
objectClass: simpleSecurityObject

Getting these objects into the LDAP database is easy.

# /etc/init.d/slapd stop
# slapadd -v < /tmp/example.ldif
# slapindex -v
# /etc/init.d/slapd start

Note that we shut down the database before running slapadd. Note also that we ran slapindex after entering the data to update the index files. While indices are automatically maintained when changes are made to a running database (e.g. using ldap* commands), they must be updated “by hand” when changes are made directly to the database files; this is accomplished by running slapindex.

The slapcat command is the “opposite” of slapadd. It extracts the contents of an LDAP database into a ldif file. To make a backup of your LDAP database,

# /etc/init.d/slapd stop
# slapcat > /tmp/example.ldif
# /etc/init.d/slapd start

Be sure to store the extracted ldif file in a safe place, since anyone who can read it can effectively read all the data in your LDAP database. Since making this backup requires you to shut down your slapd server, backups of large, mission-critical LDAP databases should be made from a replicating database (see slaves section below) designed specifically for this purpose.

It is often necessary to use slapadd to create the skeletal structure of a new database, since when a database is first created, it does not contain any administrator object that has permission new objects. Once you have used slapadd to overcome this chicken-and-egg problem, though, you will probably want to use ldap* commands or even a GUI LDAP client for further database modifications.

online (ldap*) utilities

The ldap* utilities are easily installed on a Debian computer.

# apt-get install ldap-utils

Keep in mind that these utilities need not be run on the LDAP server.

The file /etc/ldap/ldap.conf contains the basic configuration information necessary for the ldap* utilities to know with which LDAP database you want them to interact. A simple example follows.

/etc/ldap/ldap.conf

URI  ldap://localhost
BASE dc=example,dc=com

The URI directive specifies the LDAP server to use; in our example, we are working on the LDAP server itself. The BASE directive specifies the DN of the root node of the database.

The most common way to interact with an LDAP server is to read data stored in it. The ldapsearch command allows you to do this from the command line. For example, to obtain the attributes of objects for which cn=admin,

# ldapsearch -x cn=admin
dn: cn=admin,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
objectClass: top
cn: admin

You can use * as a wildcard in your search. To get every object in the database, use

# ldapsearch -x objectClass=*

You can use the ldapadd command to add objects to your ldap database. Like slapadd, ldapadd uses LDIF input syntax. Unlike slapadd, ldapadd interacts with slapd via LDAP to make its modifications. Therefore it can be used across the network, can (in fact must) be used while slapd is running, and allows background tasks such as schema checking, indexing, modification time stamps, and replication to be handled transparently by slapd instead of by hand. For example, to add a user account, we prepare an LDIF file

/tmp/user.ldif
dn: uid=jdoe,ou=people,dc=example,dc=com
uid: jdoe
cn: Jane Doe
uidNumber: 1001
gidNumber: 100
homeDirectory: /home/jdoe
userPassword: password
loginShell: /bin/bash
objectClass: posixAccount

and then upload it to the running ldap database

$ ldapadd -x -v -D cn=admin,dc=example,dc=com -W < /tmp/user.ldif

Note that we bind as the administrator in order to gain write privleges.

The ldapmodify utility allows you to modify the attributes to existing ldap objects, and the ldapdelete utility allows you to delete an ldap object completely.

The ldappasswd utility, the last of the ldap* suite, is used to change userPassword attributes. Don't use ldapmodify to do this, since that will write a value to the attribute directly, bypassing the hashing specified in slapd.conf. For the administrator to change Jane's password,

$ ldappasswd -x -v -S -W -D cn=admin,dc=example,dc=com uid=jdoe,ou=people,dc=example,dc=com

Migration Utilities

PADL software makes available a set of migration tools which ease the transition from a system using flat files (/etc/passwd, /etc/group, /etc/hosts, /etc/networks, /etc/services, /etc/protocols) to a central directory server. You can download these tools from the PADL site, or install the Debian package

# apt-get install migrationtools

which places them in /usr/share/migrationtools/.

To use these tools,

# cd /usr/share/migrationtools
# ./migrate_passwd.pl /etc/passwd /tmp/passwd.ldif

Performance Tuning

LDAP databases take a beating. Every email, every login attempt, every directory listing (ls) on any machine at your site can result in a lookup. Fortunately, there are a number of things you can (and should!) do to increase OpenLDAP's performance.

Begin by generating indices, which are database tables that can be used to track down a record much faster than by looking though the entire database.

/etc/ldap/slapd.conf

index objectClass eq,pres
index uid,uidNumber,gidNumber eq,pres
index mailLocalAddress eq,pres

If monitoring (e.g. using top) indicates that slapd is often consuming a significant fraction of your CPU time, you are probably missing a useful index. LDAP lookup should be I/O-bound, not CPU-bound.

Try to give slapd enough memory to hold all or most of its data in memory. The cachesize directive specifies how many attribute values are cached in memory. The dbcachesize directive specifies the size, in bytes, of the space allocated to store database files in memory. The defaults for these numbers are almost never large enough. For our small organization, I use

slapd.conf

cachesize     10000
dbcachesize   1000000

Ideally, cachesize is as large as the total number of entries in your database, and dbcachesize is as large as all the files in the /var/lib/ldap directory. At larger sites, you may find that your LDAP server needs quite a bit of memory in order to attain these objectives.

Make sure you are not writing more to your log files than you need to. If you can get away without an audit trail, set loglevel to 0. If you really need an audit trail, set loglevel to 256, and be sure to use a - in /etc/syslogd.conf to allow log entries to be written asynchronously.

Minimize lookups by running nscd on your client machines. Consider upping slapd's threads variable to 64 or 128.

At very large sites, you may find that your lookup throughput is still insufficient, even after you have taken all these steps. At this point, you may have to invest in more or better hardware. If your throughput is disk I/O bound, consider dedicating a fast, SCSI disk just to /var/lib/ldap, and mount that disk with the noatime option in order to cut down down on access overhead. If your throughput is network I/O bound, make sure you have a fast ethernet card. Check that the packet collision rate suffered by your LDAP server is still low (say 5% or less); if not, you need to upgrade your network. Finally, consider setting up a number of dedicated LDAP slave machines, and assign the number of clients to each slave that produces a load it can handle. Instructions for setting up LDAP slaves are presented in the following section.

LDAP Slaves

An LDAP slave is an LDAP server whose contents mirror the contents of its LDAP master. Slaves respond to read requests themselves, but when faced with a write request, a slave refers the client to its master. Any change to a master database is propagated to its slaves immediately and automatically; this process is called replication.

LDAP slaves are useful for load-balancing; distributing client requests among a number of slaves can keep the master from getting hammered. LDAP slaves are also useful for automatic failover. Many LDAP clients can be configured to fall back to a second LDAP server if the first does not respond; by setting a client's fall-back server to be an LDAP slave, you can keep your system running even if your LDAP master crashes.

Setting up a master-slave relationship between two LDAP servers is not difficult.

slapd.conf
replogfile        /var/lib/ldap/replog
replica host=slave.example.com:389
      binddn="cn=admin,dc=example,dc=org"
      bindmethod=simple credentials=secret

slapd.conf

updatedn          "cn=admin,dc=example,dc=org"
updateref         ldap://master.example.org
 
wiki/ldap.txt · Last modified: 28.02.2012 16:43 by rgareus