I believe that knowing how to administer a Lightweight Directory Access Protocol (LDAP) directory server has become an essential skill for a network administrator. An LDAP directory is your key to network simplicity. It is your universal directory across all platforms and applications, supporting simplified network authentication and a centralized company data store. The LDAP protocol is cross-platform, network-aware, and standards-based. There are a large number of LDAP implementations; in this chapter, we'll use the excellent free-of-cost, free-software OpenLDAP.
LDAP is widely supported by applications; for example, most email clients come with LDAP clients. Additionally, various databases, Content Management Systems (CMS), groupware and messaging servers, authentication servers, customer management applications, and application servers can all speak to an LDAP server.
Some folks like to argue about whether LDAP is a database. Strictly speaking, it is a protocol, not a database. It accesses a special kind of database that is optimized for very fast reads. Use it for relatively static information, such as company directories, user data, customer data, passwords, asset tracking, and security keys. OpenLDAP uses the Sleepycat Berkeley DB.
Why not use an ordinary relational database like PostgreSQL, Oracle, or MySQL? You can if you like, but then you'll lose the advantages of LDAP, which are:
Very fast reads
Flexible data types
Nearly universal application support
Fine-grained control over access to data
Distributed storage and replication
No need for elite database guru admins
No need for custom APIs
You don't want to use OpenLDAP for for a retail or web site backend, for example, or any application that needs fast, frequent changes. That's where you want an RDBMS.
The structure of the Sleepycat BDB is different from a relational database. Rather than storing information in columns and rows, and having a rigid set of indexes and fields, data are stored in attribute-type/attribute-value pairs. This structure offers great flexibility in designing records. A particular user record, for example, can have new types of data added without having to redesign the entire database. You can store any kind of text or binary data. Because it is simple like a large flat file, adding new entries is easy—just tack them on. OpenLDAP supports a distributed architecture, replication, and encryption.
Let's take a run through the basic concepts and structure of an LDAP directory. This is more important than having an encyclopedic knowledge of configuration options, because if you don't have a clear idea of what you need and how everything fits together, LDAP will remain a mysterious mess. But it's not really all that mysterious; once you grasp the basics, you'll be in fine shape. As coaches always say, first master the fundamentals. An LDAP directory can be pictured as a standard upside-down tree structure, with the root portrayed as being the top, and the branches flowing downward. Figure 12-1 is a hierarchical namespace; it is also called the directory information tree (DIT).
The root of this example directory is the country entry. The next stop is the state entry, then the organizational unit (OU) entry, which is the company's name. This branches off into different company entries, which are also called organizational units. The lefthand branch terminates at a user ID (UID). The Quality Assurance (QA) OU could hold many more users than just the one in the example.
Now comes the important bit: Terry Jones has a
distinguished name (DN), which consists of
Terry's Relative Distinguished Name (RDN), which in this example
is the UID, plus tacking on all the ancestor entries: uid=terryjones, ou=qa, ou=alrac's cookies, ou=or,
c=us
. Any attribute can be the RDN; it must be unique within
the level that the entry belongs to. The UID is usually unique because
it is a common practice to make it the user's login, but you could use
any other attribute. Obviously, a little common sense goes a long way here;
for example, there are many duplicate surnames, so using the SN
attribute would cause problems. The most common RDN for people is a
UID or common name (CN).
The basic unit of your directory is an entry. An entry is also called a record or directory object. Terry Jones' entry contains a number of attributes, such as name, phone number, email address, and so forth. You can't just invent attributes out of thin air; these must be already defined in OpenLDAP. An easy way to view them is with the GQ LDAP client (http://sourceforge.net/projects/gqclient/). You may also see them in the files in /etc/ldap/schema (on Fedora, /etc/openldap/schema) in the objectClass definitions.
You may create your own custom objectClass definitions and attribute types. I don't recommend this unless you absolutely need something that's not included. The default schema are extensive, and a lot of effort has gone into making them universal; there's no need to reinvent the wheel. On the other hand (there is always another hand, isn't there), this makes OpenLDAP flexible and extensible, and it's easy to share custom schema.
Each attribute is made up of an attribute type and an attribute value. Attributes can have multiple values. For example, Terry Jones' entry could look like this:
uid=terryjones cn=Terry Jones gn=Terry sn=Jones telephoneNumber=123-456-7890 telephoneNumber=123-456-7891 mail=tjones@alrac.com
This shows a couple of duplicate attributes. You may use as many as you like. A common use for duplicate attributes is for people's names, like this:
cn=Terry Jones cn=T. Jones cn=Terry "codefiend" Jones cn=Codefiend
The result of this is a search on any of these attribute values will succeed, so Terry Jones has nowhere to hide.
The suffix or naming
context is the top of your LDAP hierarchy. In our simple example, the suffix is
c=us
. A common approach these days
is to use your company's domain name, like dc=alrac, dc=net
. DC stands for
domain component.
When you create an entry in a DIT, its data are contained in attributes. These belong to objectClasses. Schemas can be thought of as big bags of organized objectClasses. So, when you hear someone talking about OpenLDAP schemas, you know they are referring to the files that define the organization and types of data that go into an OpenLDAP directory. In OpenLDAP, some schema are hardcoded into slapd itself.
An objectClass is part of an objectClass hierarchy. It inherits
all the properties of its parents. For example, the inetOrgPerson
objectClass is one you'll use
a lot. If you look inside /etc/ldap/schema/inetorgperson.
schema, you'll find this definition:
objectclass ( 2.16.840.1.113730.3.2.2
NAME
'inetOrgPerson'
DESC 'RFC2798: Internet Organizational Person'
SUP organizationalPerson
STRUCTURAL
This snippet shows that the long objectClass number is an official Object ID (OID) number. All of the LDAP OIDs are globally unique; you can't just make them up. This only matters when you create a custom schema and need some new OIDs. Then, find a registrar to assign some to you, such as Internet Assigned Numbers Authority (IANA).
The SUP
(superior) organizationalPerson
line tells you that its
parent objectClass is organizationalPerson
, which is a child of
person
, which is a top-level
objectClass. The objectClass defines the required and optional
attributes of all of its children, which you can read in any LDAP
browser.
STRUCTURAL
means this
objectClass can be used to create entries in your DIT. You'll also see
AUXILARY
objectClasses; these
cannot stand alone, but must be used along-side a STRUCTURAL
objectClass.
An objectClass is also an attribute.
Don't worry if this doesn't make a lot of sense right now. After you create a simple directory, you'll see how it all fits together.
One more thing you should know about: the rootDSE. This is one of those clever self-referential geek names: DSE stands for DSA Specific Entry, and DSA means Directory System Agent. This is the invisible topmost entry in your LDAP hierarchy; the built-in attributes of your LDAP server. To see these, run these two commands on your LDAP server:
$ ldapsearch -x -s base -b "" +
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: +
#
#
dn:
structuralObjectClass: OpenLDAProotDSE
configContext: cn=config
namingContexts: dc=alrac,dc=net
supportedControl: 2.16.840.1.113730.3.4.18
supportedControl: 2.16.840.1.113730.3.4.2
[...]
supportedFeatures: 1.3.6.1.4.1.4203.1.5.4
supportedFeatures: 1.3.6.1.4.1.4203.1.5.5
supportedLDAPVersion: 3
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: CRAM-MD5
supportedSASLMechanisms: NTLM
entryDN:
subschemaSubentry: cn=Subschema
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
All those long numbers are official Object Identifiers (OIDs). To learn more about these, visithttp://www.alvestrand.no/objectid/. This includes a searchable database, so you can see what a particular OID means.
This shows the same output, plus a bale of subschema:
$ ldapsearch -x -s base -b "cn=subschema" objectclasses
[...]
# Subschema
dn: cn=Subschema
objectClasses: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRAC
T MUST objectClass )
objectClasses: ( 1.3.6.1.4.1.1466.101.120.111 NAME 'extensibleObject' DESC 'RF
C2252:extensible object' SUP top AUXILIARY )
objectClasses: ( 2.5.6.1 NAME 'alias' DESC 'RFC2256: an alias' SUP top STRUCTU
RAL MUST aliasedObjectName )
objectClasses: ( 2.16.840.1.113730.3.2.6 NAME 'referral' DESC 'namedref: named
subordinate referral' SUP top STRUCTURAL MUST ref )
objectClasses: ( 1.3.6.1.4.1.4203.1.4.1 NAME ( 'OpenLDAProotDSE' 'LDAProotDSE'
) DESC 'OpenLDAP Root DSE object' SUP top STRUCTURAL MAY cn )
objectClasses: ( 2.5.17.0 NAME 'subentry' SUP top STRUCTURAL MUST ( cn $ subtr
eeSpecification ) )
[...]
That's all the same information you have
in/etc/ldap/schema. You don't need to do anything
with the rootDSE; this is just to show it's there, and what it looks
like. The rootDSE is sometimes confused with the root
DN, but they are not the same thing. The rootDSE is your
bare OpenLDAP server; i.e., the schema and
supported protocols. You'll see root DN in a lot
of documentation as your suffix name, or the base name of your data
hierarchy. I avoid using the term root DN; it's
too confusing. Oh, and then there is the rootdn
. That's your directory superuser; the rootdn
and rootpw
directives go in
slapd.conf. Yes, it is a bit confusing. The
rootdn
is all-powerful; many admins
prefer to not have a rootdn
at all,
but instead create some sort of admin user that is defined inside the
directory itself.
You're trying to plan for the future, and you want to design your DIT so smartly that it will seamlessly expand as your organization grows. It's a noble goal, for sure! So, you're wondering if you should structure it widely and shallowly, or more narrowly and deeply. This is the problem that all LDAP administrators face, and as always, the definitive answer is "it depends." My own preference is toward a shallower directory structure because it's easier to maintain, and because LDAP is optimized for searches along a level, rather than up and down the hierarchy.
Figure 12-2 shows a DIT with three OUs.
This seems all nice and organized, doesn't it? Three separate
departments each with their own OU, which feels all satisfying, like a
tidy filing cabinet. But think about it—what if Jenn from Upstairs
gets moved to Downstairs? You'll have remove her entry from Upstairs
and create a new one in Downstairs
, which is several steps, no
matter how efficient you are.
Now, take a look at Figure 12-3.
All users are lumped into the People
OU. How do we know what departments
they belongto? By giving them an extra OU attribute, like this:
dn: cn=Jenn Dancer,ou=people,dc=foo,dc=com objectClass: inetOrgPerson cn: Jenn Dancer ou=Upstairs [...]
Jenn wants to move Downstairs? Piece of cake. All I do is run
ldapmodify or a graphical LDAP browser to change
ou=Upstairs
to ou=Downstairs
, and anything else that
changes (e.g., phone number and title). This is less than half the
work of moving her entry to a new OU, which requires these
steps:
Export the existing entry to an LDIF file with ldapsearch.
Delete the record with ldapdelete.
Edit the LDIF file.
Add it to the new OU with ldapadd.
You might also think in terms of delegating responsibility to junior admins, or figuring out how to protect sensitive data. This might mean storing some data in different subtrees or separate databases, which makes administration a bit more complex, but gives you more control over who has read and write access.
This is never an easy subject, and if you ask five other LDAP admins for their advice, you'll get eight different opinions. LDAP System Administration, by Gerald Carter (O'Reilly), is especially helpful with figuring out your directory topology.