Apr 2015

Configuring a Basic OpenLDAP Server

Recently I had the joy of setting up a very basic OpenLDAP server and client where I work. It was not nearly as hard as I thought it would be, however, my requirements were simple:

  • Central POSIX Password Management
  • Central POSIX Group Management
  • Central Wheel group for sudo access
  • Two admins with management rights
  • Users can change their own LDAP entry

The plan, as it is, pretty basic:

  1. Setup a basic POSIX management solution with openldap
  2. Setup a management system for it (one which users can also use)
  3. Stand up a test client system.
  4. Stand up a pilot environment.

For the purposes of this text, step four is being omitted simply because this can apply to many things; in my case it was a matter of repeating step 3 someplace else and as such is redundant.

The Challenges of OpenLDAP

As of this writing, there is no central document that really covers all of the details of how to go about what I did. Indeed I scoured the internet and had to apply multiple sources of information I found. It is worth noting that the way I do things in this document may not always be the right way it is simply what I managed to frikn find.

Assumptions

Making a few assumptions:

  • The required software on the server side is installed.
  • Not using tls or ssl at this time.
  • The basic requirements for the system are met (disk space, cpu, mem)
  • The reader is a sysadmin with about 4 years experience or more.
  • This text uses the new way of managing openldap, that is directly feeding ldap input files (ldif) to the system or directly modifying db config files. Not using slapd.conf for anything.
  • The db config files are in /etc/openldap.
  • The Name being used is src.systhread.net; note that there is no real convention here unless one plans on latching into DNS/DHCP with LDAP. Generally speaking one should use whatever the local domain is because; you just never know you might need LDAP to interact with it later.

The Initial Setup

Openldap sets up a bunch of example db configs with basic or core schemas in etc upon installation. The first thing to do is customize that:

cd /etc/openldap/slapd.d/cn\=config
for i in olcDatabase*; do
    perl -npe 's/dc=my-domain,dc=com/dc=src,dc=systhread,dc=net/' -i $i
done
for i in olcDatabase*bdb.ldif; do
    echo "olcRootPW: $(slappasswd -s fug4z1_rul3z)" >> $i
done

What is happenning there? Easy; changing the bdb files to reflect the dc being used, in this case src.systhread.net. Also we assign the Manager password. Next up, and this is more for my own setup, enable logging:

echo "local4.* /var/log/slapd.log" >> /etc/rsyslog.conf

Then restart syslog however your platform's rc works.

Create the Initial Tree

Now for the fun part. Time to setup the basics. For the setup we just need users and groups plus the basic data about our directory. This goes in a file called initial-dit.ldif:

dn: dc=src,dc=systhread,dc=net
dc: src
o: src.systhread.net
objectclass: dcObject
objectclass: organization
objectclass: top

dn: ou=Users, dc=src,dc=systhread,dc=net
ou: Users
objectclass: organizationalUnit

dn: ou=Groups, dc=src,dc=systhread,dc=net
ou: Groups
objectclass: organizationalUnit

A little note here: see the line dc: src? That is important. The first part of the DC, whatever it is (even if it is part of a TLD name) must go in there or it simply will not work. Time to add it to the server:

ldapadd -a -c -f initial-dit.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Adding Users & Login Group

Where I work I follow the model of each user has their own login group plus any other groups they might be a part of (more or less standard Linux distribution model). Again, we use the ldif format to add users with one caveat; we generate the initial password using slappasswd. So the procedure is broken up into three parts:

  1. Create the user record
  2. Append the encrypted password to the record
  3. Add the initial login group to the record

Example user information:

  • Login ID: jrf
  • User name: Jay Fink
  • UID: 666
  • Email: jay.fink@gmail.com

Initial User Record

jrf.ldif

dn: uid=jrf, ou=Users, dc=src,dc=systhread,dc=net
uid: jrf
displayName: Jay Fink
cn: Jay Fink
givenName: Jason
sn: Fink
initials: jf
mail: jay.fink at gmail.com
uidNumber: 666
gidNumber: 666
homeDirectory: /home/jrf
loginShell: /bin/bash
gecos: Jay FInk,,,,
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: shadowAccount

There is a lot of redundancy in that record, however, it is the only way I could get it to work. If I omitted even the initials the server would not take it. I am assuming (I do not know this has something to do with the schema requirements for the objectclasses. Now append the password to the record:

 echo "userPassword: $(slappasswd -s wh3r31s_r4tm_)" >> /root/jrf.ldif
 echo " " jrf.ldif

Lastly add the initial login group for jrf to the ldif:


dn: cn=jrf, ou=Groups, dc=src,dc=systhread,dc=net
cn: jrf
gidNumber: 666
memberUid: jrf
objectclass: posixGroup

Just like adding the initial tree feed the ldif file to ldap:

ldapadd -a -c -f jrf.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Adding a Group

For out example I am using the wheel group, my login and a fictional login named goober (whom for some reason, even with a login ID like that, we trust in the wheel group).

wheel.ldif

dn: cn=wheel, ou=Groups, dc=src,dc=systhread,dc=net
cn: wheel
gidNumber: 10
memberUID: jrf
memberUID: goober
description: Wheel group for sudo
objectclass: posixGroup

And yet again....

ldapadd -a -c -f wheel.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Organizing LDIFs

If ones LDAP installation is going to be larger than a hundred users it should be pretty obvious that feeding ldifs gets old real fast. In a later text (because I haven't finished it yet!) we will look at writing tools to create ldifs and feed them. But for now, supposing the initial setup is still small, it is a good idea to organize ldifs. What I did was about as simple as it gets. For the time being I keep them in /root/ldifs with subdirs:

base/
groups/
users/
config/

I can see how that will get burdensome later, however, during the initial setup it helps keep from creating name clashes in files.

Admin Changes

Now things are going to get weird. It is time to change the config db to allow users to change their own password and grant admin rights over the src.systhread.net tree for our admins jrf and goober. There is a way to do this by feeding a file in using ldapmodify but this is what I found:

cd /etc/openldap/slap*/*

And add these lines to olcDatabase={2}bdb.ldif:

olcAccess: {0}to attrs=userPassword by self write by dn.base="cn=Manager,dc=src,dc=systhread,dc=net" write by anonymous auth by * none
olcAccess: {1}to * by dn.base="cn=Manager,dc=src,dc=systhread,dc=net" write by dn="uid=jrf,ou=Users,dc=src,dc=systhread,dc=net" write  by dn="uid=goober,ou=Users,dc=src,dc=systhread,dc=net" write  by self write by * read

So what does all of that mean? It is an ACL directive and the format is read in order. So for instance the first line would be parsed like so:

olcAccess: {0}to attrs=userPassword \
        by self write  \
        by dn.base="cn=Manager,dc=,dc=src,dc=systhread,dc=net" write \
        by anonymous auth \
        by * none

And that is about as good an explanation as I got and can give. The first line allows users to change their own password. The second line grants access to jrf and goober to make changes to everything in src.systhread.net.

Summary & Next Time

This is a lot of stuff. A lot. But when you really break it down it isn't too difficult to setup a very basic openldap server. Next time I will go over setting up a client and a management/user interface. Perhaps a little later some scripting ideas to add large groups of users, generate ldifs etc.

Quick Start Steps

Following are all of the steps needed to setup the example given less the yapping:

Initial Setup

cd /etc/openldap/slapd.d/cn\=config
for i in olcDatabase*; do
    perl -npe 's/dc=my-domain,dc=com/dc=src,dc=systhread,dc=net/' -i $i
done
for i in olcDatabase*bdb.ldif; do
    echo "olcRootPW: $(slappasswd -s fug4z1_rul3z)" >> $i
done

Initial Tree

dn: dc=src,dc=systhread,dc=net
dc: src
o: src.systhread.net
objectclass: dcObject
objectclass: organization
objectclass: top

dn: ou=Users, dc=src,dc=systhread,dc=net
ou: Users
objectclass: organizationalUnit

dn: ou=Groups, dc=src,dc=systhread,dc=net
ou: Groups
objectclass: organizationalUnit
ldapadd -a -c -f initial-dit.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Adding a User

dn: uid=jrf, ou=Users, dc=src,dc=systhread,dc=net
uid: jrf
displayName: Jay Fink
cn: Jay Fink
givenName: Jason
sn: Fink
initials: jf
mail: jay.fink at gmail.com
uidNumber: 666
gidNumber: 666
homeDirectory: /home/jrf
loginShell: /bin/bash
gecos: Jay FInk,,,,
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: shadowAccount
 echo "userPassword: $(slappasswd -s wh3r31s_r4tm_)" >> /root/jrf.ldif
 echo " " jrf.ldif

dn: cn=jrf, ou=Groups, dc=src,dc=systhread,dc=net
cn: jrf
gidNumber: 666
memberUid: jrf
objectclass: posixGroup
ldapadd -a -c -f jrf.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Adding a Group

dn: cn=wheel, ou=Groups, dc=src,dc=systhread,dc=net
cn: wheel
gidNumber: 10
memberUID: jrf
memberUID: goober
description: Wheel group for sudo
objectclass: posixGroup
ldapadd -a -c -f wheel.ldif -H ldap:/// -D \
  "cn=Manager,dc=src,dc=systhread,dc=net" -W

Users can change password/Admin rights

cd /etc/openldap/slap*/*

Add these lines to olcDatabase={2}bdb.ldif:

olcAccess: {0}to attrs=userPassword by self write by dn.base="cn=Manager,dc=src,dc=systhread,dc=net" write by anonymous auth by * none
olcAccess: {1}to * by dn.base="cn=Manager,dc=src,dc=systhread,dc=net" write by dn="uid=jrf,ou=Users,dc=src,dc=systhread,dc=net" write  by dn="uid=goober,ou=Users,dc=src,dc=systhread,dc=net" write  by self write by * read