Oh The Huge Manatee

A blog about technology, open source, and the web... from someone who works with all three.

Setting Up Secure LDAP on Ubuntu 10.04 - the Idiot's Guide to SSL, TLS, and SLAPD

LDAP is one of the Elder Gods of the UNIX world.  It had it’s beginnings in DAP, the first Directory Access Protocol, in 1988.  Within a decade DAP was more or less discarded in favor of the Lightweight Directory Access Protocol.  And it has stayed in basically the same format ever since.

LDAP is, as you might have guessed, a directory protocol.  It was developed in response to X.500, a spec for telephone directories… but personally, I have a hard time thinking about LDAP structure in terms of a phone book.  The structure is really a lot more like directories on a drive.  The idea is to store and retrieve information in a hierarchical structure.  You can technically put anything in there, but most often it is used for things like phone books, personnel directories, and the like.  For SysAdmins, it’s typically used as a Single Sign On method - providing a unified set of usernames, passwords, and basic account information that can be shared across a number of services.  I’ve got it running my user accounts on 4 separate servers, plus about 30 different websites in development and 3 web applications that we use internally.  Just thinking about all those “useradd” commands I’m saving makes me misty-eyed.

Making sense of LDAP

Because LDAP is old, it’s not exactly… user friendly.  There are some terms you need to learn.  Chiefly, the terms and acronyms you will see in the “directory structure.”  Anything in LDAP has a “Distinguished Name”.  This is just like saying “full path from root” in a file directory.  The DN tells you what the object is called, and where it sits in the directory hierarchy.  If my user account is [email protected] , this is how LDAP would think of it:
CN stands for “Common Name” - that’s just the name of the object.  Like a filename, it doesn’t have to be unique, but it does have to be unique inside it’s directory.  You can’t have two files called /etc/passwd, after all!  DC just stands for Domain Component - that means it’s part of the directory where the CN “lives”.

Let’s take another example - you have a file in your home directory called “swearing”.  In the filesystem, we’d refer to this as /home/you/swearing.txt .  If it were in LDAP, it would be:
You also have the option of using Organizational Units (OU), which are just like groups.

The last thing you need to know is that this system typically mirrors the Domain Name System, at least at the top level.  So when we set up your LDAP server (it’s coming, I promise!), one of the first things you’ll do is define the “baseDN”, or the “root” of your directory.  If I were hosting LDAP here, it would be dc=swearingatcomputers,dc=blogspot,dc=com .

Make sense?  You are going to organize all your users this way, so it’d better make sense!


The trouble with LDAP is, it seems to be the only thing that is a bitch to set up on Ubuntu.  That’s right, Ubuntu, the distro for everyone.  Learning how to set up a secure LDAP installation is cryptic, poorly documented, and just darn finicky.  So here’s my “dummies” guide:

1) Get your materials
apt-get install slapd ldap-utils
Slapd is the ldap daemon, and ldap-utils are the utilities you use to work with LDAP.  Duh.  That was the easy part.

2) Set up your schema(s)

LDAP is EXTREMELY configurable.  You can set up whatever fields you like - it’s a lot like setting up your own database, in that respect.  Except that this protocol was designed by the people who make phone books, so they went ahead and wrote RFCs for the structures you’re likely to use.  There’s nothing stopping you from making your own schema, but I don’t recommend you bother.  The RFCs are very thoroughly thought out, and dammit the schemas come with the package.

We’re going to load schemas for a company directory which also serves as a Single Sign On system.  Each schema is written out like a script, and stored in it’s own little .ldif file in /etc/ldap/schema .  You load them with ldapadd.
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/cosine.ldif
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/nis.ldif
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/inetorgperson.ldif
The first schema is COSINE (RFC4524).  This defines the basic fields, like name.  It’s also got a bunch of other ones in there, but trust me - just leave it as it is.  Some of those fields are used by other schemas, and if you remember RPM hell, just imagine what it’s like to be in LDAP field hell.

The second is NIS (RFC 2307).  You guessed it - those are fields common for Network Information Services.  That’s what you’re making, so you gotta include this one.

The last is Inetorgperson (RFC 2798).  This schema is for organizations that use the Internet.  Since you’re reading this, I’m gonna guess that’s you. Seriously, if you want to store email addresses, this is you.

3) Configure the backend

SLAPD has a pretty sweet feature which lets you store configuration information in the LDAP directory itself.  Not only do you get to feel cool for configuring in something other than a flat text file, but it means you can modify the daemon’s configuration without restarting it. So here’s an .ldif file that you can tweak and load to set this service up for yourself.

# Load dynamic backend module
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulepath: /usr/lib/ldap
olcModuleload: back_hdb

# Database settings
dn: olcDatabase=hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {1}hdb
olcSuffix: dc=swearing,dc=com
olcDbDirectory: /var/lib/ldap
olcRootDN: cn=admin,dc=swearing,dc=com
olcRootPW: Sw3ar1ngatC0mputerz
olcDbConfig: set_cachesize 0 2097152 0
olcDbConfig: set_lk_max_objects 1500
olcDbConfig: set_lk_max_locks 1500
olcDbConfig: set_lk_max_lockers 1500
olcDbIndex: objectClass eq
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcAccess: to attrs=userPassword by dn="cn=admin,dc=swearing,dc=com" write by anonymous auth by self write by * none
olcAccess: to attrs=shadowLastChange by self write by * read
olcAccess: to dn.base="" by * read
olcAccess: to * by dn="cn=admin,dc=swearing,dc=com" write by * read

Go through this file before loading it.  Change the “swearing”s to match your domain, and change that “olcRootPW” to a root password that you’ll remember.  Don’t worry, it will be stored encrypted.  This is just to set it up.  Really all we’re doing setting up slapd to read it’s configuration from LDAP, and giving it login information for an admin account that will have access to the config.

Just paste this into a file, name it backend.ldif , and then run:

sudo ldapadd -Y EXTERNAL -H ldapi:/// -f backend.ldif 
4) Store stuff

Technically, that’s it!  Break out the champagne, you have LDAP!  
In the real world though, you’ll want to set up your directory structure, and secure the whole thing.  The easiest way to do mass changes is still by loading .ldif files, so go ahead and dump this baby into structure.ldif.

# Create your root -level object - this should mirror your domain name
dn: dc=swearing,dc=comobjectClass: topobjectClass: dcObjectobjectclass: organizationo: Swearing at Computersdc: swearingdescription: Swearing at Computers sample structure 
# Create your Admin user - this should match what you told the back-end!dn: cn=admin,dc=swearing,dc=comobjectClass: simpleSecurityObjectobjectClass: organizationalRolecn: admindescription: Swearing at Computers admin accountuserPassword: Sw3ar1ngatC0mputerz
# Create an OU to store a certain kind of contact indn: ou=humans,dc=swearing,dc=comobjectClass: organizationalUnitou: humans
# Create an OU to store another kind of contact in.  OK, you probably don't need this onedn: ou=cats,dc=swearing,dc=comobjectClass: organizationalUnitou: cats
# Create a couple of groupsdn: ou=friends,dc=humans,dc=swearing,dc=comobjectClass: organizationalUnitou: friends
dn: ou=enemies,dc=humans,dc=swearing,dc=comobjectClass: organizationalUnitou: enemies
# Create a POSIX group - you know, the kind that handle permissions on your server
dn: cn=administrators,ou=humans,dc=swearing,dc=comobjectClass: posixGroupcn: administratorsgidNumber: 90000

# Create a person, who has a login account in the administrators group
dn: uid=Peter,ou=friends,dc=swearing,dc=comobjectClass: inetOrgPersonobjectClass: posixAccountobjectClass: shadowAccountuid: pgriffinsn: GriffingivenName: Petercn: Peter GriffindisplayName: Peter GriffinuidNumber: 9000gidNumber: 90000userPassword: 12345?anidiotssuitcasegecos: Peter GriffinloginShell: /bin/bashhomeDirectory: /home/pgriffinshadowExpire: -1shadowFlag: 0shadowWarning: 7shadowMin: 8shadowMax: 999999shadowLastChange: 10877mail: [email protected]postalCode: 31000l: Quahogo: Toy Companymobile:  +1 (555) 555-1212homePhone: +1 (555) 555-1212title: Factory workerpostalAddress: initials: PG

Obviously, change those values to suit what you’re trying to do - but we just created a few organizational groups, and a POSIX group, and a user to go with them. If you read through this carefully, you can get a sense of what all of these values do. Basically, when you create that person you can define what kind of object it is. Are they just an inetorgperson, with an email addy and contact info? Are they a POSIX user? Fields are created for you depending on what objectClasses you choose. uidNumber and gidNumber are useless for someone who isn’t a POSIX user.

5) Have a beer

Seriously, you earned it. That’s a lot of learning right there. And at this very moment you have a working LDAP server! This is fantastic. Time to ask your boss for a raise! Look at all the productivity you’ve got!

But wait, there’s more! Right now, everything you’ve got is being sent in the clear. Let’s fix that.

6) Set up your certificates

There are two ways to encrypt data transmission in LDAP. You can tunnel the whole connection through SSL, which is called LDAPS. It works much the same way that HTTPS does compared to HTTP. That’s the old way to do it, and technically it’s discouraged, but it’s perfectly good if your application supports it. The other, recommended way, is TLS. Basically TLS is encrypting the data transmitted within the LDAP stream.

Either way, it uses the same old SSL certificates that you use for HTTPS. So if you’ve got those set up already, skip this step. If not, never fear! 99% of secure LDAP deployments don’t need to worry about authenticating the server with an expensive external Certificate Authority. All you should care about is the encryption, and that you can do with self signed certs. So that’s what I’m covering here.

6a) SLAPD is compiled with gnutls, which means you get the best behavior if you make your certs with the same library.
apt-get install gnutls-bin
6b) The first step is to set up your faux Certificate Authority. So make a private key for this CA:
sudo sh -c "certtool --generate-privkey > /etc/ssl/private/faux-ca.key"
6c) Now make a details file for your faux CA. Create /etc/ssl/faux-ca.info with the following content:
cn = Faux Certificate Authority

6d) Make the self-signed CA certificate
sudo certtool --generate-self-signed --load-privkey /etc/ssl/private/faux-ca.key --template /etc/ssl/faux-ca.info --outfile /etc/ssl/certs/faux-ca.cert
Now you’re a certificate authority! Time to make your server’s key and sign it.

6e) Make the server’s private key:
sudo sh -c "certtool --generate-privkey > /etc/ssl/private/ldap.key
6f) Make an .info file so your CA can sign the private cert. I put mine at /etc/ssl/ldap.info :
organization = Swearing at Computers

cn = ldap.swearing.com 
6g) Sign the server’s certificate with your faux CA:
sudo certtool --generate-certificate --load-privkey /etc/ssl/private/ldap.key --load-ca-certificate /etc/ssl/certs/faux-ca.cert --load-ca-privkey /etc/ssl/private/faux-ca.key --template /etc/ssl/ldap.info --outfile /etc/ssl/certs/ldap.cert
7) Add the key to SLAPD
Edit /etc/ldap/slapd , and uncomment the line that starts with SLAPD_SERVICES. It should look like this when you’re done.
SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"
Now we are going to use that LDAP configuration interface you set up to add the keys:
sudo ldapmodify -Y EXTERNAL -H ldapi:///
It will ask you for your LDAP password, which you set in the .ldif files way back in the day. Then you just get an empty prompt. Type these lines so see the magic happen:
dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/faux-ca.cert
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/ldap.cert
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/ldap.key
Press enter a couple of times, and it will tell you that it’s modifying cn=config. Press Ctrl+D to exit ldapmodify.

Make sure that SLAPD’s user (openldap on ubuntu) has read access to the certificate files you named there. For self-signed people, you can just set openldap as the owner of the files. But if you share certs with another application (like http), you have to get tricky. I just add both www-data and openldap to a new group called “ssl”, and give the new group read access.

Sadly, you have to restart SLAPD to make it read that /etc/ldap/slapd change you made. But after that, you’re up and running! That’s it!

Guaranteed you’re going to run into trouble at some stage here. You’re going to get a cryptic error message that you won’t be able to interpret. Don’t panic. The problem is probably that SLAPD doesn’t have the right access to the certificate files. Seriously. I swore at the computer for 2 days trying to figure that one out.

Have fun!