Contents

Setup

Start with a minimal installation of Debian Jessie, and proceed to install some useful packages; e.g., htop, vim, sudo. This should be enough to get through the next few steps.

Setting up LDAP

Before You Begin

If you'd like or are in a hurry, you can skip to Installation below. This may contain some valuable information for setup or troubleshooting, however.

First, a foreword about LDAP: the Lightweight Directory Access Protocol is a protocol designed to provide a speedy, reliable way for networked machines to retrieve small records (that are often ubiquitously needed for various purposes--not the least of which identification across clients). LDAP records (or entries, as I will use interchangeably) are arranged in a hierarchical manner in a one-to-many relationship--much like filesystem hierarchies. These records are identified by a unique string, reminiscent of an absolute file path, called the distinguished name, or DN, which is used consistently to refer to the record throughout the architecture. DNs, as written out, consist of one or more components, each with a key and value separated by an equal sign '=', and joined together with a comma ',' separator. DNs can (and regularly do) have embedded spaces. The base DN for an LDAP database is usually derived from a domain name--a sufficiently unique identifier--as a series of domain components, or DCs, which will look something like dc=example,dc=com for the hypothetical "example.com" domain. Records below the base DN are arranged with more specific components on the right, such as cn=admin,dc=example,dc=com. It's worth mentioning that you will encounter CN (common name), OU (organizational unit), and other more domain-specific keys such as uid and olcDatabase.

Records each possess attributes, a multi-map of string values to a sequence of string values. While it is entirely possible to have arbitrary attributes, records in LDAP normally have properties dictated by a schema. Such a schema consists of attribute types, which define the names of attributes with the kinds of values that can be stored in an attribute (its syntax), its sorting/collation order, its comparison semantics, whether or not it needs to be unique per record, and so forth, as well as classes, which consist of a set of required attribute types (by name), and a set of optional attribute types. For example, an attribute type is something like a "userPassword", which stores an Octet String, is compared via Octet String comparison, and need not be unique to a record, while an object class is something like "posixUser" which MAY have a "userPassword" but MUST have a "uidNumber" and a "gidNumber" (both of which have syntax Integer). Classes have the additional property that they may be structural, auxiliary, or abstract. A record must have one, and only one, structural class, but can possess zero or more auxiliary classes. Classes can participate in inheritance; any may inherit arbitrarily from abstract classes, whereas auxiliary and structural classes may only inherit from their own kind (in addition to abstract). For example, consider the following Python code:

If we consider y to represent a record in the LDAP database, then Y would be its singular structural class, which inherits from the structural class X. Auxiliary classes are not well-represented in this language, except perhaps as well-known attributes defined on the object y itself. Beyond the properties that class Y defines (here absolutely none), the Mixin class can be thought of as an abstract class from which Y also inherits some properties. All of these ultimately inherit from object, whose analogue in LDAP is top, a class which is simultaneously (and confoundingly) abstract and structural--the only such class.

It would be wise to familiarize yourself with some important classes, amongst them "posixAccount", "posixGroup", "organizationalUnit", and "simpleSecurityObject", amongst others. These and many more are defined in various RFCs and included with the OpenLDAP installation (under cn=schema,cn=config, described metareferentially through LDAP access). A web-based tool for browsing local schemas will be installed much later in this tutorial.

Access to the database is controlled through a process called binding. It should be noted that LDAP makes no distinction between users and records; a connecting client can connect to any record it so chooses. Of course, the most obvious things to bind to are user accounts, which are represented as first-class objects in most major LDAP schemas. In the simple case, one can use "simpleSecurityObject" with OpenLDAP to make any object in any part of the database be bindable, but posixAccounts are more likely to be used regularly. A connection that is not yet bound is said to be anonymous.

Access control is another topic entirely. You should be well aware of OpenLDAP's access control mini-language, which reads fairly straightforward. Amongst other things to be aware of, some important points are:

The first matching "to" clause is selected, in the order it is specified in the database. In particular, a "to *" clause will cancel all further searches, because it will immediately match everything. As a rule of thumb, specify more specific "to" clauses first, followed by more generic ones, with "to *" at the end.

The "by" clauses match similarly; place "by *" at the end of the list.

Giving an access level of "none" (or, equivalently, omitting it entirely) will cause results (when trying to search a record) indistinguishable from that record not existing. You may want to be the root account to be sure you're getting the entire picture.

olcAccess lines are a little cumbersome; you are well advised to Keep It Simple, Stupid.

Finally, LDIF--the LDAP Data Interchange Format. It is standardized as RFC2849, which does a good job of being one of the more concise RFCs I've read (the examples at the end are particularly informative). Basically, the syntax is as follows:

As with most script-like languages, lines whose first non-whitespace character is a pound-sign "#" are ignored, and blank lines have no effect on parsing or performance outside of a production.

Record indications are headed by a "dn: " field, followed by the DN, on its own line. From there on, all attributes and modifications are applied to that DN. For non-empty LDIF files, this must be the first non-comment, non-blank line.

Attributes are indicated similarly, with key, ": ", and value, all strings. If the value is too long, it may wrap onto continuation lines, which begin with exactly one space. If the value contains possibly illegal characters, such as non-ASCII ordinals, control sequences that may disrupt the parser, and so forth, the key may be followed instead by two colons ":: ", and the value will be base64 encoded. Wrapping similarly applies to base64-encoded values. Multi-valued attributes are ordered in the order in which they appear, unless they have a leading integer in {angle brackets} preceding the value, in which case they are first stably sorted by that integer in ascending order. Outputs from ldapsearch will always contain this integer for multi-valued attributes.

Some attributes have special behavior when passed through certain tools; ldapmodify is the most prominent of these. This command expects the "changetype: " attribute to be one of "add", "replace", or "delete", which is then to be followed by one or more "add: ", "replace: ", or "delete: " attributes, specifying a data attribute name (which must then follow that specification for all but delete), terminated by a dash "-" on a line of its own. Multiple additions and/or replacements may be specified this way, but (AFAIK) they must consistently be the same operation.

While not technically LDIF (and there is an LDIF way of specifying this), ldapmodrdn uses a format without any colon-separated headers in which each pair of non-blank, non-comment lines is, in order:

An existent DN.

A relative DN (RDN), whose first n components will replace the first n components of the existent DN.

Installation

For this purpose, the instructions on the Debian Wiki are invaluable, though somewhat flawed. Presented here is a slightly more instruction-oriented and pedantic version.

First, become root (sudo or su). Though it is not mentioned much, all the commands in the setup assume you are root.

From a root terminal, apt-get install slapd ldap-utils. During this process, you may be prompted for an administrator password for the database. It does not, however, prompt you about certain other things, like the base DN for the main database. After the packages are installed, I recommend running dpkg-reconfigure -p low slapd and verifying the results. It may tell you that it will overwrite the database previously created--that's OK. Remember to set the password to something memorable, but you probably won't need it for a little while.

Now would be a good time to familiarize yourself with the tools in ldap-utils. They mostly take the same options, permitting you to log in in various ways:

-H <URI> sets the URI to attempt to contact the server on.

-Y <AUTH> sets the SASL authentication method.

-D <DN> sets the DN to bind to (very probably "cn=admin,<your_dc_components>" after the setup)

-W will instruct the utility to prompt for a bind password, and is best used with -D above.

The exact method you choose will probably heavily depend on the URI in -H. In particular, ldapi:/// represents a Unix domain socket, and allows one to log in using the UIDs present on the local machine. By default, root has access to both the config database (at cn=config) and the regular database (under your dc components). As root, you will generally want to use <ldapcommand> -H ldapi:// -Y EXTERNAL to perform work. **Do not use this as a non-root user, especially on cn=config**; the default privileges restrict anyone but root from even knowing about the existence of the cn=config base DN.

Eventually, for some work (or testing), you might want to log in as the administrator you set up. This is usually accomplished with <ldapcommand> -H ldap://localhost/ -D "cn=admin,<dc_components>" -W. You will be prompted for the password you entered during setup. It should be noted that OpenLDAP, by default, will add this user DN to a special config attribute on the database called "olcRootDN", which means that (1) the password is dictated by "olcRootPW" on this database, and not necessarily by the password in "userPassword", and (2) cn=admin will have unrestricted access regardless of the settings of "olcAccess" on that database. (More on that later.)

The commands that are most important in this set are:

ldapsearch, which takes -b <baseDN> and an optional -s <scope>, which searches for the baseDN and returns results in LDIF format. Scope may be one of:

sub (default if omitted): returns the subtree--the baseDN and all of its descendants.

one: returns the baseDN and all of its immediate children.

children: returns only the children of the baseDN.

base: returns only the baseDN.

ldapmodify, which takes -f <file> (or input from stdin) and alters the database according to a modification (usually addition, replacement, or deletion of attributes).

ldapadd, which takes similar to the above, but adds records to the database.

ldapmodrdn, which moves records.

ldapdelete, which deletes records.

You may want to read through the search results in entirety to get a feeling for LDIF. Note well that ldapmodify with replacement will replace all values of a multivalue attribute, and that you are responsible for ensuring the correct ordering afterward--it cannot be used for insertion or replacement of a single element. For more on the syntax, I direct you to the man pages--each of which has at least one simple, contrived example that will get you pointed in the right direction. Keep in mind that, returning valid LDIF, ldapsearch may be used to transfer or convert databases in mass as well.