Adding the Initial Directory Entries
A directory without data isn’t of much use. There are two ways to add information to your directory; which method to use depends on the directory’s state. First, slapadd and the other slap* commands were presented in Chapter 3 as database maintenance tools. They allow an administrator to import entries directly into the database files and export the entire directory as an LDIF file. They work directly with the database, and don’t interact with slapd at all. Second, the OpenLDAP distribution includes a number of tools, such as ldapmodify , that can update a live directory using the LDAPv3 network operations. These tools access the directory through the server.
What are the advantages and disadvantages of these approaches? The offline tools can be much faster; furthermore, there are circumstances when you can’t start the server without first adding data (for example, when restoring the directory’s contents from a backup). The disadvantage of the offline tools, of course, is that they must be run locally on the server.
In contrast to the offline tools, the LDAP client utilities are more flexible and allow a directory administrator greater control by forcing user authentication and by using access control lists on directory entries. A good rule of thumb is that the slap* tools are used for getting your LDAP server online, and the ldap* tools are for day-to-day administration of the directory.
Warning
OpenLDAP 2.1 removed the restriction that slapd must not be running before any of the slap* tools can be used. However, OpenLDAP 2.0 caches data in memory, so using the slap* tools while the directory is running can present an inconsistent view of the directory at best, and corrupt data at worst.
The tools for offline manipulation of directory information are slapadd, slapcat, slapindex, and slappasswd (covered in Chapter 3). The slapadd tool determines which files and indexes to update based on the slapd.conf file. Because it is possible for a given configuration file to define more than one database partition, slapadd provides options for specifying a database partition by either the directory suffix (-b suffix) or the numbered occurrence (-n integer) in slapd.conf. Referring to a particular database using a numbered instance is confusing and error-prone. It is far more intuitive to refer to the same database by using the directory suffix. Note that the -b and -n options are mutually exclusive. A summary of the various slapadd command-line options is provided in Table 4-2.
Option |
Description |
-c |
Continues processing input in the event of errors. |
-b suffix-n integer |
Specify which database in the configuration file to use by the
directory’s suffix ( |
-d integer |
Specifies which debugging information to log. See the
|
-f filename |
Specifies which configuration file to read. |
-l filename |
Specifies the LDIF file to use for input. In the absence of this option, slapadd reads data from standard input. |
-v |
Enables verbose mode. In this mode, slapd prints some additional messages on standard output. |
The slapcat
utility dumps the contents of an
entire directory (including persistent operational attributes such as
modifyTimeStamp
) in LDIF format. The command-line
options for slapcat are identical to the options
for slapadd (Table 4-2),
except that the -l switch specifies an output
filename instead of an input filename. In the absence of this switch,
slapcat writes all entries to standard output.
slapcat can provide a useful means of backing up
the directory. Unlike the actual DBM datafiles, which are machine-
and version-dependent, LDIF is very portable and allows easier
editing in case of corrupted data. I don’t mean to
discourage you from backing up the DBM files, but you could do worse
than backing up the directory in both forms.
The slapindex
command can be used to regenerate the
indexes for a bdb
backend. This might be necessary
if a new index was added to slapd.conf after the
directory was populated with entries. The
slapindex tool shares the same command-line
options as slapadd, with the exception of
-l. The -l option
isn’t used for slapindex
because neither an input nor an output file is needed.
To start populating your directory, create a file containing the LDIF
entries of the top-level nodes. These LDIF entries build the root
node and the people
organizational unit.
## Build the root node. dn: dc=plainjoe,dc=org dc: plainjoe objectClass: dcObject objectClass: organizationalUnit ou: PlainJoe Dot Org ## Build the people ou. dn: ou=people,dc=plainjoe,dc=org ou: people objectClass: organizationalUnit
Assuming that these entries are stored in a file named /tmp/top.ldif, you can add them to the directory by executing:
root# slapadd -v -l /tmp/top.ldif added: "dc=plainjoe,dc=org" (00000001) added: "ou=people,dc=plainjoe,dc=org" (00000002)
The output indicates that the entries were added successfully.
Verifying the Directory’s Contents
Next, you will bring the directory online so that you can use it in conjunction with ldapsearch, ldapmodify, and the other tools for working on a live directory:
root# /usr/local/libexec/slapd
After the directory server has started, you can use ldapsearch to query the server. ldapsearch allows you to dig through your directory, test for the existence of data, and test whether access control has been set up correctly.
OpenLDAP’s ldapsearch began life as a simple wrapper for the LDAP search operation. The list of search possibilities is lengthy; I won’t cover it until Chapter 5. For now, I will focus on very simple searches that assure you the directory is up and running correctly. In its simplest form, a query requires the following information:
The LDAP server’s hostname or IP address
The credentials (i.e., user DN and password) to use to bind to the host
The search base in the form of a DN
The scope of the directory to search
A search filter
A list of attributes to return
We’ll start with a “Show me
everything” search. Here, you ask the directory to
return all entries that have a value for the
objectClass
attribute (which is all entries).
$ ldapsearch -x -b "dc=plainjoe,dc=org" "(objectclass=*)" version: 2 # # filter: (objectclass=*) # requesting: ALL # # plainjoe,dc=org dn: dc=plainjoe,dc=org dc: plainjoe.org objectClass: dcObject objectClass: organizationalUnit ou: PlainJoe Dot Org # people,dc=plainjoe,dc=org dn: ou=people,dc=plainjoe,dc=org ou: people objectClass: organizationalUnit # Search result search: 2 result: 0 Success # numResponses: 3 # numEntries: 2
The ldapsearch
options
used here are:
-
-x
Instructs ldapsearch to perform a simple bind (i.e., do not use SASL for authentication).
-
-b dc=plainjoe,dc=org
Defines the DN
dc=plainjoe,dc=org
as the search’s base suffix. This DN specifies the point at which the search begins. Therefore, it must be a DN that is held by the LDAP server. All entries located higher in the tree will be ignored.-
(objectclass=*)
The search filter. If you are familiar with filename globbing, or just general wildcard patterns, this filter should be familiar. RFC 2254 defines ways to represent an LDAP search filter as a string. The syntax of filters is covered in Chapter 5. For now, it’s sufficient to know that this filter matches any value of the
objectClass
attribute.
The surprising thing about this command is that it doesn’t explicitly contain most of the items that I said were necessary for any search. In fact, the only two items that we can clearly see are the search base and the search filter. What’s going on? Let’s look at the missing items one at a time:
- The LDAP server’s hostname (or IP address)
ldapsearch queries the local host if the server isn’t specified explicitly. The
-h
hostname
option specifies the hostname or IP address. In this case, though, you’re running the server locally, so you don’t need it.- Credentials used to bind to the directory
The ACL defined in slapd.conf gave read permission to all users. Therefore, you don’t need to authenticate to perform this search. When authentication is required, the
-D
DN
and-w
password
options specify the login DN and password to be used.- The search scope
By default, ldapsearch queries the server for all entries contained in the subtree of the root node defined by the -b option. Other possibilities include searching only the immediate children of the base suffix entry or searching this entry alone. The search scope (-s) option can be used to specify either
sub
,base
, orone
.- A list of attributes to return
By default, slapd returns all nonoperational attributes. On a complex directory, you might get an extremely long list of attributes for every entry in the directory. To limit the result to a few specific attributes, list the attributes you want on the command line, separated by commas. Operational attributes such as
modifyTimestamp
andmodifiersName
are not returned unless specifically asked for by name or by using the plus character (+) as the attribute list in the search.
The default values for many LDAP client parameters can be controlled via the system-wide ldap.conf configuration file (located in the same directory as slapd.conf) or the user-specific version in $HOME/.ldaprc. For more details, refer to the ldap.conf(5) manpage. Our examples explicitly list all parameters required by the command-line tools unless the compile-time defaults can be used, which was the case in the previous ldapsearch listing.
Table 4-3 and Table 4-4 list all the options and arguments for ldapsearch. Don’t worry about understanding them all now.
Option |
Description |
-d integer |
Specifies what debugging information to log. See the
|
-D binddn |
Specifies the DN to use for binding to the LDAP server. |
-e [!]ctrl[=ctrlparam] |
Defines an LDAP control to be used on the current operation. See also the -M option for the manageDSAit control. |
-f filename |
Specifies the file containing the LDIF entries to be used in the operations. |
-H URI |
Defines the LDAP URI to be used in the connection request. |
-I |
Enables the SASL “interactive” mode. By default, the client prompts for information only when necessary. |
-k |
Enables Kerberos 4 authentication. |
-K |
Enables only the first step of the Kerberos 4 bind for authentication. |
-M-MM |
Enable the Manager DSA IT control. This option is necessary when modifying an entry that is a referral or an alias. -MM requires that the Manager DSA IT control be supported by the server. |
-n |
Does not perform the search; just displays what would be done. |
-O security_properties |
Defines the SASL security properties for authentication. See previous
information on the |
-P [2|3] |
Defines which protocol version to use in the connection (Version 2 or 3). The default is LDAPv3. |
-Q |
Suppresses SASL-related messages such as the authentication mechanism use, username, and realm. |
-R sasl_realm |
Defines the realm to be used by the SASL authentication mechanism. |
-U username |
Defines the username to be used by the SASL authentication mechanism. |
-v |
Enables verbose mode. |
-w password |
Specifies the password to be used for authentication. |
-W |
Instructs the client to prompt for the password. |
-x |
Enables simple authentication. The default is to use SASL authentication. |
-X id |
Defines the SASL authorization identity. The identity has the form
|
-y passwdfile |
Instructs the ldap tool to read the password for a simple bind from the given filename. |
-Y sasl_mechanism |
Instructs the client as to which SASL mechanism should be used. The bind request will fail if the server does not support the chosen mechanism. |
-Z-ZZ |
Issue a StartTLS request. Use of -ZZ makes the support of this request mandatory for a successful connection. |
Option |
Description |
-a [never|always|search|find] |
Specifies how to handle aliases when located during a search.
Possible values include |
-A |
For any entries found, returns the attribute names but not their values. |
-b basedn |
Defines the base DN for the directory search. |
-F prefix |
Defines the URL prefix for filenames. The default is to use the value
stored in |
-l limit |
Defines a time limit (in seconds) for the server in the search. |
-L-LL-LLL |
Print the resulting output in LDIFv1 format. -LL causes the result to be printed in LDIF format without comments. -LLL prints the resulting output in LDIF format without comments or version information. |
-s [sub|base|one] |
Defines the scope of the search to be |
-S attribute |
Causes the ldapsearch client to sort the results
by the value of
attribute
|
-t-tt |
Write binary values to files in a temporary directory defined by the -T option. -tt specifies that all values should be written to files in a temporary directory defined by the -T option. |
-T directory |
Defines the directory used to store the resulting output files. The
default is the directory specified by
|
-u |
Includes user-friendly entry names in the output. |
-z limit |
Updating What Is Already There
Eventually, the information stored in a directory will need to be updated or deleted. While a directory isn’t designed to be updated as frequently as a database, there are very few applications in which the data never changes. This section covers how to update the data in the directory using ldapmodify. The name ldapmodify is a little misleading; this utility can add new entries and delete or update existing entries using some of the advanced features of LDIF for its input language.
The following LDIF listing defines two entries that we will add to our directory:
## filename: /tmp/users.ldif ## LDIF entry for "Gerald W. Carter" dn: cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org cn: Gerald W. Carter sn: Carter mail: jerry@plainjoe.org mail: gcarter@valinux.com labeledURI: http://www.plainjoe.org/ roomNumber: 1234 Dudley Hall departmentNumber: Engineering telephoneNumber: 222-555-2345 pager: 222-555-6789 mobile: 222-555-1011 objectclass: inetOrgPerson ## LDIF entry for "Jerry Carter" dn: cn=Jerry Carter,ou=people,dc=plainjoe,dc=org cn: Jerry Carter sn: Carter mail: carter@nowhere.net telephoneNumber: 555-123-1234 objectclass: inetOrgPerson
The following command shows how to
add these entries to the
directory while it is running. Because write privileges are required
to add new entries, ldapmodify binds to the
directory using the credentials from the rootdn
and rootpw
slapd.conf
parameters.
$ ldapmodify -D "cn=Manager,dc=plainjoe,dc=org" -w secret \ > -x -a -f /tmp/users.ldif adding new entry "cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org" adding new entry "cn=Jerry Carter,ou=people,dc=plainjoe,dc=org"
The output indicates that both entries were added successfully. The -D, -w, and -x options to ldapmodify should be familiar; they specify the DN to use for the modification, specify the password for the modification, and request simple authentication, respectively. This leaves only two new options to discuss:
- -a
Entries are to be added to the directory. The default for ldapmodify is to update existing information.
- -f filename
Reads the new entries from the given filename. By default, ldapmodify reads from standard input.
If ldapmodify returns an error message such as the following, try enabling verbose messages via the -v command-line switch:
ldap_add: Invalid syntax additional info: value contains invalid data
There are two common causes of this error message. You may have forgotten to include all the necessary schema files in slapd.conf, or you may have extra whitespace at the end of line in the LDIF file. The set list command in vi can help you track down extra whitespace.
Refer again to Table 4-3 for a list of the common options for all of these ldap client tools. Table 4-5 lists those options specific to ldapmodify and ldapadd. Note that ldapadd and ldapmodify are the same executable; ldapadd is only a hard link to ldapmodify. The commands differ only in their default behavior, which depends on the name by which the program was invoked.
Now let’s see how a modification works. Suppose you
want to add a URL to the entry for cn=Jerry
Carter,ou=people,dc=plainjoe,dc=org
. To add a URL, use the
labeledURI
attribute:
labeledURI: http://www.plainjoe.org/~jerry/
In addition, you should delete the gcarter@valinux.com email address for “Gerald W. Carter” because it has become invalid. You can place both changes in a single LDIF file:
## /tmp/update.ldif ## Add a web page location to Jerry Carter. dn: cn=Jerry Carter,ou=people,dc=plainjoe,dc=org changetype: modify add: labeledURI labeledURI: http://www.plainjoe.org/~jerry/ ## Remove an email address from Gerald W. Carter. dn: cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org changetype: modify delete: mail mail: gcarter@valinux.com
The changetype
keyword in the LDIF file is the key to modifying existing entries.
This keyword can accept the following values:
-
add
Adds the entry to the directory.
-
delete
Deletes the entry from the directory.
-
modify
Modifies the attributes of an entry. With this keyword, you can both add and delete attribute values.
-
modrdn
Changes the RDN of an entry.
-
moddn
Changes the DN of an entry.
This LDIF file tells ldapmodify what changes to make. We’ll invoke ldapmodify with the verbose (-v) option so you can follow the update operations more closely. The -a option isn’t needed because you’re not adding new entries.
$ ldapmodify -D "cn=Manager,dc=plainjoe,dc=org" -w secret > -x -v -f /tmp/update.ldif ldap_initialize( <DEFAULT> ) add labeledURI: http://www.plainjoe.org/~jerry/ modifying entry "cn=Jerry Carter,ou=people,dc=plainjoe,dc=org" modify complete delete mail: gcarter@valinux.com modifying entry "cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org" modify complete
Notice that the LDIF file is parsed sequentially from the top.
Therefore, later LDIF entries can modify entries created previously
in the file. You can also create an LDIF file with entries having
different changetype
values. For example, the
following LDIF file adds an entry for user Peabody Soup, adds a new
telephoneNumber
to Jerry Carter’s
entry, and finally deletes the previously created entry for Peabody
Soup.
## /tmp/changetypes.ldif ## Add entry for Peabody Soup. dn: cn=Peabody Soup,ou=people,dc=plainjoe,dc=org changetype: add cn: Peabody Soup sn: Soup objectclass: inetOrgPerson ## Add new telephoneNumber for Jerry Carter. dn: cn=Jerry Carter,ou=people,dc=plainjoe,dc=org changetype: modify delete: telephoneNumber telephoneNumber: 555-123-1234 - add: telephoneNumber telephoneNumber: 234-555-6789 ## Remove the entry for Peabody Soup. dn: cn=Peabody Soup,ou=people,dc=plainjoe,dc=org changetype: delete
A couple of facts about this LDIF file are worth mentioning:
Entries are separated by a blank line, as noted earlier.
Multiple changes to a single entry using the
modify
changetype are separated by a single dash (-) on a line by itself. These should be handled as a single change by the server. Either all the changes for this DN take effect or none are applied.
The modify
changetype supports
add
and delete
keywords
for adding and deleting attribute values. In order to delete the
value of an attribute, the delete
: must
immediately be followed by an
attributetype
:value
pair. It’s necessary to specify the value
you’re deleting because some attributes can hold
multiple values. Specifying which value to remove eliminates any
ambiguity about what you want to do. When the last value of an
attribute is removed from an entry, that attribute is no longer
present in the entry.
Here’s how to apply this second set of changes to the directory. Again, we’ve specified the -v option to see how ldapmodify processes the LDIF file.
$ ldapmodify -D "cn=Manager,dc=plainjoe,dc=org" -w secret -x -v -f /tmp/changetype.ldif ldap_initialize( <DEFAULT> ) add cn: Peabody Soup add sn: Soup add objectclass: inetOrgPerson adding new entry "cn=Peabody Soup,ou=people,dc=plainjoe,dc=org" modify complete delete telephoneNumber: 555-123-1234 add telephoneNumber: 234-555-6789 modifying entry "cn=Jerry Carter,ou=people,dc=plainjoe,dc=org" modify complete deleting entry "cn=Peabody Soup,ou=people,dc=plainjoe,dc=org" delete complete
Modifying the RDN of an entry takes a little more thought than adding an entry or changing an attribute of an entry. If the entry is not a leaf node, changing its RDN orphans the children in the directory because the DN of their parent has changed. You should make sure that you don’t leave orphaned nodes in the directory—you should move the nodes with their parent or give them a new parent. With that in mind, let’s think about how to change the RDN of the entry:
dn: cn=Jerry Carter,ou=people,dc=plainjoe,dc=org
from cn
: Jerry
Carter
to cn
:
Gerry
Carter
.
Here’s the LDIF file that makes the changes:
## /tmp/modrdn.ldif ## Change the RDN from "Jerry Carter" to "Gerry Carter." dn: cn=Jerry Carter,ou=people,dc=plainjoe,dc=org changetype: modrdn newrdn: cn=Gerry Carter deleteoldrdn: 1
You can also use the ldapmodrdn command to perform the same task:
$ ldapmodrdn \ > "cn=Jerry Carter,ou=people,dc=plainjoe,dc=org" \ > "cn=Gerry Carter"
Not counting the DN of the entry to be changed and the new RDN value, the ldapmodrdn tool has three command-line options besides those common to the other OpenLDAP client tools (Table 4-3). These additional options are listed in Table 4-6.
Option |
Description |
-c |
Instructs ldapmodrdn to continue if errors occur. By default, it terminates if there is an error. |
-r |
Removes the old RDN value. The default behavior is to add another RDN value and leave the old value intact. The default behavior makes it easier to modify a directory without leaving orphaned entries. |
-s new_superior_node |
Defines the new superior, or parent, entry under which the renamed entry should be located. |
If an entire subtree of the directory needs to be moved, a better solution may be to export the subtree to an LDIF file, modify all occurrences of the changed attribute in all the DNs, and finally re-add the subtree to the new location. Once the information has been entered correctly into a location, you can then use a recursive ldapdelete to remove the old subtree.
ldapdelete
possesses all of the command-line
options common to ldapsearch and
ldapmodify. The only new option is
-r (recursive), which deletes all entries below
the one specified on the command line, in addition to the named
entry. Note that this deletion is not atomic; entries are deleted
individually. The following command deletes the entire
ou=people
subtree:
$ ldapdelete -D "cn=Manager,dc=plainjoe,dc=org" -w secret -x \ > -r -v "ou=people,dc=plainjoe,dc=org" ldap_initialize( <DEFAULT> ) deleting entry "ou=people,dc=plainjoe,dc=org" deleting children of: ou=people,dc=plainjoe,dc=org deleting children of: cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org removing cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org cn=Gerald W. Carter,ou=people,dc=plainjoe,dc=org removed deleting children of: cn=Gerry Carter,ou=people,dc=plainjoe,dc=org removing cn=Gerry Carter,ou=people,dc=plainjoe,dc=org cn=Gerry Carter,ou=people,dc=plainjoe,dc=org removed Delete Result: Success (0)
Now that you have a working directory, a good exercise would be to experiment with various ACLs to restrict access to certain attributes. This exercise will also help you become more comfortable with the tools presented in this chapter. Use the slapcat tool to dump the directory to an LDIF file and start over from scratch until you are comfortable with adding, deleting, and modifying entries. The next chapter explores creating a distributed directory, replicating information to multiple servers, more searching techniques, and some advanced ACL configurations.
Get LDAP System Administration now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.