I'm using LDAP (RFC 4510) to maintain a centralized address book at home. Here are my setup notes, mostly following Gentoo's LDAP howto.
Install OpenLDAP with the ldap
USE flag enabled:
# emerge -av openldap
If you get complaints about a cyrus-sasl
↔ openldap
dependency
cycle, you should temporarily (or permanently) disable the ldap
USE
flag for cyrus-sasl
:
# echo 'dev-libs/cyrus-sasl -ldap' > /etc/portage/package.use/ldap
# -ldap" emerge -av1 cyrus-sasl
# emerge -av openldap
Generate an administrative password:
$ slappasswd
New password:
Re-enter new password:
{SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4
Configure the slapd
LDAP server. Here is a very minimal
configuration, read the OpenLDAP Admin Guide for details:
# emacs /etc/openldap/slapd.conf
# cat /etc/openldap/slapd.conf
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
pidfile /var/run/openldap/slapd.pid
argsfile /var/run/openldap/slapd.args
database hdb
suffix "dc=example,dc=com"
checkpoint 32 30
rootdn "cn=Manager,dc=example,dc=com"
rootpw {SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4
directory /var/lib/openldap-data
index objectClass eq
Note that inetorgperson is huge, but it's standardized. I think it's better to pick a big standard right off, than to outgrow something smaller and need to migrate.
Gentoo creates the default database directory for you, so you can ignore warnings about needing to create it yourself.
Configure LDAP client access. Again, read the docs for details on adapting this to your particular situation:
# emacs /etc/openldap/ldap.conf
$ cat /etc/openldap/ldap.conf
BASE dc=example,dc=com
URI ldap://ldapserver.example.com
You can edit '/etc/conf.d/slapd' if you want command line options
passed to slapd
when the service starts, but the defaults looked
fine to me.
Start slapd
:
# /etc/init.d/slapd start
Add it to your default runlevel:
# eselect rc add /etc/init.d/slapd default
Test the server with
$ ldapsearch -x -b '' -s base '(objectclass=*)'
Build a hierarchy in your database (this will depend on your organizational structure):
$ emacs /tmp/people.ldif
$ cat /tmp/people.ldif
version: 1
dn: dc=example, dc=com
objectClass: dcObject
objectClass: organization
o: Example, Inc.
dc: example
dn: ou=people, dc=example,dc=com
objectClass: organizationalUnit
ou: people
description: All people in organisation
dn: cn=Manager, dc=example,dc=com
objectClass: organizationalRole
cn: Manager
description: Directory Manager
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/people.ldif
$ rm /tmp/people.ldif
abook
If you currently keep your addresses in abook, you can export them to LDIF with:
$ abook --convert --infile ~/.abook/addressbook --outformat ldif \
| abook-ldif-cleanup.py --basedn 'ou=people,dc=example,dc=com' > dump.ldif
where abook-ldif-cleanup.py does some compatibility processing using the python-ldap module.
Add the people to your LDAP database:
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f dump.ldif
To check if that worked, you can list all the entries in your database:
$ ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'
Then remove the temporary files:
$ rm -rf dump.ldif
Aliases
Ok, we've put lots of people into the people
OU, but what if we want
to assign them to another department? We can use aliases (RFC
4512), the symlinks of the LDAP world. To see how this
works, lets create a test OU to play with:
$ emacs /tmp/test.ldif
$ cat /tmp/test.ldif
version: 1
dn: ou=test, dc=example,dc=com
objectClass: organizationalUnit
ou: testing
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/test.ldif
$ rm /tmp/test.ldif
Now assign one of your people to that group:
$ emacs /tmp/alias.ldif
$ cat /tmp/alias.ldif
version: 1
dn: cn=Jane Doe, ou=test,dc=example,dc=com
objectClass: alias
aliasedObjectName: cn=Jane Doe, ou=people,dc=example,dc=com
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
$ rm /tmp/alias.ldif
The extensibleObject
class allows us to add the DN field, without it
you get:
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
Enter LDAP Password:
adding new entry "cn=Jane Doe, ou=test,dc=example,dc=com"
ldap_add: Object class violation (65)
additional info: attribute 'cn' not allowed
You can search for all entries (including aliases) with
$ ldapsearch -x -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
...
dn: cn=Jane Doe,ou=test,dc=example,dc=com
objectClass: alias
objectClass: extensibleObject
aliasedObjectName:: Y249TWljaGVsIFZhbGxpw6hyZXMsb3U9cGVvcGxlLGRjPXRyZW1pbHksZGM9dXM=
...
You can control dereferencing with the -a
option:
$ ldapsearch -x -a always -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
...
dn: cn=Jane Doe,ou=people,dc=example,dc=com
cn: Jane Doe
sn: Doe
...
Once you've played around, you can remove the test
OU and its
descendants:
$ ldapdelete -D "cn=Manager,dc=example,dc=com" -xW -r ou=test,dc=example,dc=com
shelldap
There are a number of tools to make it easier to manage LDAP databases. Command line junkies will probably like shelldap:
$ shelldap --server ldapserver.example.com
~ > ls
cn=Manager
ou=people
~ > cat cn=Manager
dn: cn=Manager,dc=example,dc=com
objectClass: organizationalRole
cn: Manager
~ > cd ou=people
ou=people,~ > ls
Shelldap's edit
command spawns your EDITOR
on a temporary file
populated by the entry you're editing. You can either alter the entry
as you see fit, or try something fancier in LDIF.
Mutt
If you use the Mutt email client (or just want a simple way to
query email addresses from the command line) there are a number of
scripts available. Pick whichever sounds most appealing to
you. I wrote up mutt-ldap.py, which lets you configuration the
connection details via a config file (~/.mutt-ldap.rc
) rather than
editing the script itself. Usage details are available in the
docstring.
Apple Address Book
You can configure Apple's Address Book to search an LDAP directory. See Humanizing OS X for details.
SSL/TLS
It took me a bit of work to get SSL/TLS working with my
GnuTLS-linked OpenLDAP. First, you'll probably need to generate
new SSL/TLS keys (/etc/openldap/ssl/*
) with certtool (see
X.509 certificates). Then add the following lines to
/etc/openldap/slapd.conf
:
TLSCipherSuite NORMAL
TLSCACertificateFile /etc/openldap/ssl/ca.crt
TLSCertificateFile /etc/openldap/ssl/ldap.crt
TLSCertificateKeyFile /etc/openldap/ssl/ldap.key
TLSVerifyClient never
Where ca.crt
, ldap.crt
, and ldap.key
are your new CA,
certificate, and private key. If you want to disable unencrypted
connections completely, remove the ldap://
entry from your slapd
command line by editing (on Gentoo) /etc/conf.d/slapd
so it has
OPTS="-h 'ldaps:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'"
Now you should be able to restart slapd
so it will use the new
configuration.
Have clients running on your server use the local socket by editing
/etc/openldap/ldap.conf
to set:
URI ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock
Test your server setup by running (on the server)
$ ldapsearch -x -b '' -s base '(objectclass=*)'
Copy your CA over to any client machines (I put it in
/etc/openldap/ssl/ldapserver.crt
), and set them up with the
following two lines in /etc/openldap/ldap.conf
:
URI ldaps://ldapserver.example.com
TLS_CACERT /etc/openldap/ssl/ldapserver.crt
Test your client setup by running (on the client)
$ ldapsearch -x -b '' -s base '(objectclass=*)'
You can configure shelldap
with the following lines in
~/.shelldap.rc
:
server: ldaps://ldapserver.example.com
tls: yes
tls_cacert: /etc/openldap/ssl/ldapserver.crt
You can configure mutt-ldap.py
with the following lines in
~/.mutt-ldap.rc
:
port = 636
ssl = yes
Debian-based systems
I wanted to mirror my home LDAP info on my public Ubuntu server. Here's a quick rundown of the Ubuntu setup. Install OpenLDAP:
$ sudo apt-get install slapd ldap-utils
Don't serve in the clear:
$ cat /etc/default/slapd
...
SLAPD_SERVICES="ldaps:/// ldapi:///"
...
Avoid Unrecognized database type (hdb)
by loading the hdb
backend
module before declaring hdb
databases:
$ sudo cat /etc/ldap/slapd.conf
...
moduleload back_hdb
database hdb
...
Convert the old school slapd.conf
to the new slapd.d:
$ sudo mv slapd.d{,.bak}
$ sudo mkdir slapd.d
$ sudo slaptest -f slapd.conf -F slapd.d
...
hdb_db_open: database "dc=example,dc=com": db_open(/var/lib/slapd/id2entry.bdb) failed: No such file or directory (2).
...
slap_startup failed (test would succeed using the -u switch)
...
$ sudo chown -R openldap.openldap slapd.d
Don't worry about that db_open
error, the conversion to slapd.d
will have completed successfully.
Set permissions on the database directory (note that the databases
should be under /var/lib/ldap
to match Ubuntu's default apparmor
config. Otherwise you'll see invalid path: Permission denied
errors
when slapd
tries to initialize the databaes).
$ sudo chown openldap.openldap /var/lib/ldap/
$ sudo chmod 750 /var/lib/ldap/
Configure your clients
$ cat /etc/ldap/ldap.conf
BASE dc=example,dc=com
URI ldaps://example.com
TLS_CACERT /etc/ldap/ssl/ldapserver.crt
Start slapd
and add it to your default runlevel:
$ sudo /etc/init.d/slapd start
$ sudo update-rc.d slapd defaults
Finally, import your directory data. Dump the data on your master server:
master$ sudo slapcat -b 'dc=example,dc=com' > database.ldif
Load the data on your slave:
$ sudo /etc/init.d/slapd stop
$ sudo slapadd -l database.ldif
$ sudo /etc/init.d/slapd start
References
There's a good overview of schema and objectclasses by Brian Jones on O'Reilly. If you want to use inetOrgPerson but also include the countryName attribute, ...