Scott M. Mcdermott

UNIX Systems & Network Administrator
available for contract or salaried positions

OpenLDAP instance

This section details the installation of an RFC2307-compliant LDAP DIT using the stock OpenLDAP server that comes with the Ubuntu Linux 8.04 VPS instance used for this project.

After the server is configured, we set up the system's authentication layer and name service switch to use LDAP for user accounts and groups, in addition to the traditional file-based UNIX account database.

Packages

First the appropriate packages need to be installed to configure and bootstrap the LDAP server:

$ sudo apt-get install slapd ldap-utils

Next we create the basic LDAP server configuration file. The file name used on Ubuntu is:

$ grep SLAPD_CONF /etc/default/slapd
SLAPD_CONF=

Override there if necessary. We use the default for our installation. We go over sections of the file in the following sections.

Schema

The following configuration directives will provide the core schema object classes and attributes we want access to for our users and groups. We will use the following standard object classes for our users:

  • person
  • organizationalPerson
  • inetOrgPerson
  • posixAccount
  • shadowAccount

which will give us standard POSIX user capability as specified in RFC2307 as well as additional fields useful for typical company directories. This configuration is enabled with the following schema:

include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/misc.schema

For POSIX group support, we choose to implement RFC2307bis instead, which is very slightly different: it changes the schema to allow posixGroup objects to also be members of the groupOfNames class. This means that system groups embedded in the filesystem ACLs can also be used for access control, mail distribution list membership, or any other group-based usage outside of the strict domain of POSIX filesystem access. The file should be placed alongside the other schema and included in the LDAP configuration:

$ cd /etc/ldap/schema
$ sudo wget http://www.grotan.com/ldap/rfc2307bis.schema
$ grep 2307bis /etc/ldap/slapd.conf
include /etc/ldap/schema/rfc2307bis.schema

Note that this file attempts to redefine the 1.3.6.1.1.1.1.0 and 1.3.6.1.1.1.1.1 OIDs (uidNumber and gidNumber, which are built into OpenLDAP on my version of Ubuntu). They must be commented out in the file for the server to start properly.

They are the first two entries in the schema file. Just use # characters to comment them out.

Runtime

The server's runtime configuration should mirror that of the Ubuntu default:

pidfile      /var/run/slapd/slapd.pid
argsfile     /var/run/slapd/slapd.args
modulepath   /usr/lib/ldap
moduleload   back_hdb

Access Control

We will use a fairly standard access control setup. The order of specification both in the file and within the rules is significant. We should work from higher to lower specificity where possible.

Only the users themselves are able to change their own passwords, and password hashes will not be made public. Because of this, the password hash will only be available to the LDAP server, so passwords must be passed to it in plaintext for authentication. We are NOT configuring this host to service any machine other than the localhost. Otherwise, we would have to use SSL for the above reason.

access to attrs=userPassword
       by self write
       by anonymous auth
       by * none

Our fictitious domainComponent (dc) is "corp", serving as placeholder for the company name. We store all POSIX accounts for persons in the 'people' container Organizational Unit:

access to dn=ou=people,dc=corp,dc=com
       by * read

POSIX groups are likewise stored in the tree. We allow group owners to change the membership DNs (probably by some kind of web GUI or whatever, but they could use ldapmodify if they wanted). This can be used to implement mail distribution lists when e.g. Postfix is set to route out of LDAP, without requiring admins to update the lists. This also gives us automatic email lists corresponding to any defined POSIX group, for example. This is a big advantage of 2307bis.

access to dn.subtree="ou=groups,dc=corp,dc=com"
       attrs=description,Member
       by dnattr=owner write
       by * read

The default is read access to everything else. This is a corporate directory, and anything requiring stricter permissions has already been specified.

access to *
       by * read

Directory

We use a traditional Berkeley database in its own directory here. The HDB format allows for subtree renames (i.e. moves), which would otherwise require non-atomic, individual, ordered moves of all the individual entries.

Note that the use of the separate directory here to store the database files is what triggered the AppArmor problem I discussed earlier. If AppArmor is used, the profile would need to be updated to continue using this as written.

defaultsearchbase       dc=corp,dc=com

database                hdb
lastmod                 on

directory               /var/lib/ldap/dc:corp,dc:com
mode                    0660
suffix                  dc=corp,dc=com

readonly                off

Finally, the credentials for the administrative user. We could also store these in the configuration file using a hash, and I believe there is also a way to store it out of band, i.e. in the database itself.

rootdn                  cn=rootdn,dc=corp,dc=com
rootpw                  123456testing123456

I am not concerned with this because (1) it has saved my ass before to have this in the file and (2) the very next step, we will prevent anyone but root and the LDAP server from reading the file:

$ egrep "GROUP|USER" /etc/default/slapd
SLAPD_GROUP="openldap"

$ getent group openldap
openldap:x:110:

$ getent passwd openldap
openldap:x:106:110:OpenLDAP Server Account,,,:/var/lib/ldap:/bin/false

$ sudo chown root:openldap /etc/ldap/slapd.conf
$ sudo chmod 0640 /etc/ldap/slapd.conf

Indices

This is the last step of the server configuration. These are performance optimizations. If indices are not constructed, I believe linear scans are done for e.g. equality. An example is below, but these should be hand-optimized for each site depending on the actual data in the DIT.

index uid                     eq,pres,sub
index cn                      eq,pres,sub
index mail                    eq,pres,sub
index gecos                   eq,pres,sub
index loginShell              eq,pres
index homeDirectory           eq,pres
index uidNumber               eq,pres
index objectClass             eq,pres
index userPassword            eq,pres
index gidNumber               eq,pres

Database

Now that we have everything configured, we will bootstrap our Directory Information Tree. The database must be created, visible to the LDAP server, with the database directory name specified in our LDAP server configuration:

$ sudo -u openldap grep ^directory /etc/ldap/slapd.conf
directory /var/lib/ldap/dc:corp,dc:com

There is no point in keeping this directory inaccessible to users, because the directory listing alone reveals nothing, and it prevents us from entering the directory as an ordinary user. Only the owner (ldap user) should be able to write to the database though.

$ chmod 0755 /var/lib/ldap
$ cd /var/lib/ldap
$ sudo mkdir dc:corp,dc:com
$ sudo chown root:openldap dc:corp,dc:com
$ sudo chmod 0770 dc:corp,dc:com

We must also copy the database configuration file into the database directory we have made. This file sets parameters for the Sleepycat database:

$ sudo -u openldap cp DB_CONFIG dc:corp,dc:com/

Bootstrapping the database involves the addition of our schema support objects which instantiate the structure of the database and allow further updates via LDAP itself. We can bypass certain safety and enforcement requirements with the slapadd utility to accomplish this; the utility works directly with the database files, rather than going through the LDAP protocol input. For this reason, we have not yet started the LDAP server.

Since we only have a root and two container group Organizational Units, this is exceedingly simple to bootstrap:

$ cat bootstrap.ldif
dn: dc=corp,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
dc: corp
o: Corporation Foo

dn: ou=people,dc=corp,dc=com
objectClass: top
objectClass: organizationalUnit
description: actual human objects
ou: people

dn: ou=groups,dc=corp,dc=com
objectClass: top
objectClass: organizationalUnit
description: arbitrary groupings and ACLs
ou: groups

$ sudo -u openldap slapadd < bootstrap.ldif

The slapadd utility by default uses the first mentioned database in slapd.conf. In our case, only one is defined.

Test Accounts

Having encountered no errors, we use the same procedure to instantiate a test user and group:

$ cat groups.ldif
dn: cn=corpusers,ou=groups,dc=corp,dc=com
objectClass: top
objectClass: groupOfNames
objectClass: posixGroup
cn: corpusers
gidNumber: 5001
member: uid=scott,ou=people,dc=corp,dc=com

$ cat people.ldif
dn: uid=scott,ou=people,dc=corp,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: scott
givenName: Scott
sn: Mcdermott
cn: Scott Mcdermott
mail: scott@corp.com
o: Corporation Foo
l: San Francisco
st: California
userPassword:: e1NTSEF9OTZNNFRlSm9PZjlWMUNQdFh3a1JjWnBJMUhUdDduRU4K
mobile: +1 415 730 5477
loginShell: /bin/bash
uidNumber: 5001
gidNumber: 5001
homeDirectory: /home/scott
gecos: Scott Mcdermott
shadowLastChange: 10877
shadowMin: 0
shadowMax: 999999
shadowWarning: 7
shadowInactive: -1
shadowExpire: -1
shadowFlag: 0

$ sudo -u openldap slapadd < people.ldif
$ sudo -u openldap slapadd < groups.ldif

Note the presence of the userPassword attribute and the :: after the LDIF attribute. This signals to the utility that the argument is base64 encoded. To generate this from a given password such as testing, use a command like this:

$ slappasswd -s testing | base64
e1NTSEF9c0tCWE1PNGJ4TWFML2xBTE8vbldPeGVQd0tGeUJsa0cK

If not specified with -s the terminal will be used to gather the password. On our test server, the scott user does indeed have a password of testing. Obviously, this is not a great password and should not be used on a real machine.

Startup

At this point we are ready to go:

$ sudo /etc/init.d/slapd start
Starting OpenLDAP: slapd.

$ pgrep -lx slapd
19707 slapd

$ netstat -ta | grep ldap
tcp        0      0 *:ldap   *:* LISTEN

It's probably wise to make an ldap library config file in your home directory at this point to reduce the amount of information you have to give to the LDAP tools at every single command line invocation:

$ cat ~/.ldaprc
uri     ldap://localhost/
base    dc=corp,dc=com

Test that we get the right data coming out of it:

$ ldapsearch -LLLx uid=scott uid
dn: uid=scott,ou=people,dc=corp,dc=com
uid: scott

$ ldapsearch -LLLx uid=scott | wc -l
29

This looks about right.

Lastly, ensure that the system is configured for it to come up automatically:

$ sudo update-rc.d slapd multiuser
System startup links for /etc/init.d/slapd already exist.

And since we don't know the security of the box, let's check to make sure we aren't

$ netstat -nta | grep :389 | grep LISTEN
tcp 0   0   0.0.0.0:389   0.0.0.0:*  LISTEN

Whoops! We don't want strangers connecting to our LDAP port!!! Let's change that:

$ grep ldap:// /etc/default/slapd
SLAPD_SERVICES="ldap://127.0.0.1:389/ ldapi:///"

$ sudo /etc/init.d/slapd restart

Perhaps also consider using ldapi:/// as the basis for all connections (it's a UNIX domain socket) since we are only servicing localhost. But there's no point in running an LDAP server to only serve localhost anyways...