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...