LDAP
We use OpenLDAP for directory services. Our primary LDAP server is auth1 and our secondary LDAP server is auth2.
ehashman's Guide to Setting up OpenLDAP on Debian
Welcome to my nightmare.
What is LDAP?
LDAP: Lightweight Directory Access Protocol
An open, vendor-neutral, industry standard application protocol for accessing and maintaining distributed directory information services over an Internet Protocol (IP) network. — Wikipedia: LDAP
In this case, "directory" refers to the user directory, like on an old-school Rolodex. Many groups use LDAP to maintain their user directory, including the University (the "WatIAM" identity management system), the Computer Science Club, and even the UW Amateur Radio Club.
This is a guide documenting how to set up LDAP on a Debian Linux system.
First steps
Ensure that openldap is installed on the machine:
# apt-get install slapd ldap-utils
- Debian will do a lot of magic and set up a skeleton LDAP server and get it running. We need to configure that further.
Let's set up logging before we forget. Create the following files in
/var/log
:# mkdir /var/log/ldap # touch /var/log/ldap.log
Set ownership correctly:
# chown openldap:openldap /var/log/ldap
Set up rsyslog to dump the LDAP logs into
/var/log/ldap.log
by adding the following lines:# vim /etc/rsyslog.conf ... # Grab ldap logs, don't duplicate in syslog local4.* /var/log/ldap.log
Set up log rotation for these by creating the file
/etc/logrotate.d/ldap
with the following contents:/var/log/ldap/*log { weekly missingok rotate 1000 compress delaycompress notifempty create 0640 openldap adm postrotate if [ -f /var/run/slapd/slapd.pid ]; then /etc/init.d/slapd restart >/dev/null 2>&1 fi endscript } /var/log/ldap.log { weekly missingok rotate 24 compress delaycompress notifempty }
As of OpenLDAP 2.4, it doesn't actually create a config file for us. Apparently, this is a "feature": LDAP maintainers think we should want to set this up via dynamic queries. We don't, so the first thing we need is our
slapd.conf
file.
Building slapd.conf
from scratch
Get a copy to work with:
# scp uid@auth1.csclub.uwaterloo.ca:/etc/ldap/slapd.conf /etc/ldap/ ## you need CSC root for this
- You'll want to comment out the TLS lines, and anything referring to Kerberos and access for now. You'll also want to comment out lines specifically referring to syscom and office staff.
- Make sure you remove the reference to
nonMemberTerm
as an index, as we're going to remove this field. You'll also need to generate a root password for the LDAP to bootstrap auth, like so:
# slappasswd New password: Re-enter new password: {SSHA}longhash
Add this line below
rootdn
in theslapd.conf
:rootpw {SSHA}longhash
Now we want to edit all instances of "csclub" to be "wics" instead, e.g.:
suffix "dc=wics,dc=uwaterloo,dc=ca" rootdn "cn=root,dc=wics,dc=uwaterloo,dc=ca"
Next, we need to grab all the relevant schemas:
scp -r uid@auth1.csclub.uwaterloo.ca:/etc/ldap/schema/ /tmp/schemas
- Use the include directives to help you find the ones you need. I noticed we were missing
sudo.schema
,csc.schema
, andrfc2307bis.schema
. - Open up the
csc.schema
for editing; we're not using it verbatim. Remove the attributesstudentid
andnonMemberTerm
and the objectclassclub
. Also make sure you change the OID so we don't clash with the CSC. Because we didn't want to go through the process of requesting a PEN number, we chose arbitrarily to use 26338, which belongs to IWICS Inc. We also need to can the auto-generated config files, so do that:
# rm -rf /etc/openldap/slapd.d/*
Also nuke the auto-generated database:
# rm /var/lib/ldap/__db.*
Configure the database:
# cp /usr/share/slapd/DB_CONFIG /var/lib/ldap/ # chown openldap:openldap /var/lib/ldap/DB_CONFIG
Now we can generate the new configuration files:
# slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/
And ensure that the permissions are all set correctly, lest this break something:
# chown -R openldap:openldap /etc/ldap/slapd.d
If at this point you get a nasty error, such as
5657d4db hdb_db_open: database "dc=wics,dc=uwaterloo,dc=ca": db_open(/var/lib/ldap/id2entry.bdb) failed: No such file or directory (2). 5657d4db backend_startup_one (type=hdb, suffix="dc=wics,dc=uwaterloo,dc=ca"): bi_db_open failed! (2) slap_startup failed (test would succeed using the -u switch)
Just try restarting slapd, and see if that fixes the problem:
# service slapd stop # service slapd start
Congratulations! Your LDAP service is now configured and running.
Getting TLS Up and Running
Now that we have our LDAP service, we'll want to be able to serve encrypted traffic. This is especially important for any remote access, since binding to LDAP (i.e. sending it a password for auth) occurs over plaintext, and we don't want to leak our admin password.
Our first step is to copy our SSL certificates into the correct places. Public ones go into
/etc/ssl/certs/
and private ones go into/etc/ssl/private/
.Since the LDAP daemon needs to be able to read our private cert, we need to grant LDAP access to the private folder:
# chgrp openldap /etc/ssl/private # chmod g+x /etc/ssl/private
Next, uncomment the TLS-related settings in
slapd.conf
. These areTLSCertificateFile
(the public cert),TLSCertificateKeyFile
(the private key),TLSCACertificateFile
(the intermediate CA cert), andTLSVerifyClient
(set to "allow").# enable TLS connections TLSCertificateFile /etc/ssl/certs/wics-wildcard.crt TLSCertificateKeyFile /etc/ssl/private/wics-wildcard.key # enable TLS client authentication TLSCACertificateFile /etc/ssl/certs/GlobalSign_Intermediate_Root_SHA256_G2.pem TLSVerifyClient allow
Update all your LDAP settings:
# rm -rf /etc/openldap/slapd.d/* # slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ # chown -R openldap:openldap /etc/ldap/slapd.d
And last, ensure that LDAP will actually serve
ldaps://
by modifying the init script variables in/etc/default/
:# vim /etc/default/slapd ... SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///" ...
Now you can restart the LDAP server:
# service slapd restart
And assuming this is successful, test to ensure LDAP is serving on port 636 for
ldaps://
:# netstat -ntaup Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN 22847/slapd tcp 0 0 0.0.0.0:636 0.0.0.0:* LISTEN 22847/slapd
Populating the Database
Now you'll need to start adding objects to the database. While we'll want to mostly do this programmatically, there are a few entries we'll need to bootstrap.
Root Entries
Start by creating a file
tree.ldif
to create a few necessary "roots" in our LDAP tree, with the contents:dn: dc=wics,dc=uwaterloo,dc=ca objectClass: dcObject objectClass: organization o: Women in Computer Science dc: wics dn: ou=People,dc=wics,dc=uwaterloo,dc=ca objectClass: organizationalUnit ou: People dn: ou=Group,dc=wics,dc=uwaterloo,dc=ca objectClass: organizationalUnit ou: Group
Now attempt an LDAP add, using the password you set earlier:
# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f tree.ldif Enter LDAP Password: adding new entry "dc=wics,dc=uwaterloo,dc=ca" adding new entry "ou=People,dc=wics,dc=uwaterloo,dc=ca" adding new entry "ou=Group,dc=wics,dc=uwaterloo,dc=ca"
Test that everything turned out okay, by performing a query of the entire database:
# ldapsearch -x -h localhost # extended LDIF # # LDAPv3 # base <dc=wics,dc=uwaterloo,dc=ca> (default) with scope subtree # filter: (objectclass=*) # requesting: ALL # # wics.uwaterloo.ca dn: dc=wics,dc=uwaterloo,dc=ca objectClass: dcObject objectClass: organization o: Women in Computer Science dc: wics # People, wics.uwaterloo.ca dn: ou=People,dc=wics,dc=uwaterloo,dc=ca objectClass: organizationalUnit ou: People # Group, wics.uwaterloo.ca dn: ou=Group,dc=wics,dc=uwaterloo,dc=ca objectClass: organizationalUnit ou: Group # search result search: 2 result: 0 Success # numResponses: 4 # numEntries: 3
Users and Groups
Next, add users to track the current GID and UID. This will save us from querying the entire database every time we make a new user or group. Create this file,
nextxid.ldif
:dn: uid=nextuid,ou=People,dc=wics,dc=uwaterloo,dc=ca cn: nextuid objectClass: account objectClass: posixAccount objectClass: top uidNumber: 20000 gidNumber: 20000 homeDirectory: /dev/null dn: cn=nextgid,ou=Group,dc=wics,dc=uwaterloo,dc=ca objectClass: group objectClass: posixGroup objectClass: top gidNumber: 10000
You'll see here that our first GID is 10000 and our first UID is 20000.
Now add them, like you did with the roots of the tree:
# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f nextxid.ldif Enter LDAP Password: adding new entry "uid=nextuid,ou=People,dc=wics,dc=uwaterloo,dc=ca" adding new entry "cn=nextgid,ou=Group,dc=wics,dc=uwaterloo,dc=ca"
Special sudo
Entries
We also need to add a sudoers OU with a defaults object for default sudo settings. We also need entries for syscom, such that members of the syscom group can use sudo on all hosts, and for termcom, whose members can use sudo on only the office terminals. Call this one
sudoers.ldif
:dn: ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca objectClass: organizationalUnit ou: SUDOers dn: cn=defaults,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca objectClass: top objectClass: sudoRole cn: defaults sudoOption: !lecture sudoOption: env_reset sudoOption: listpw=never sudoOption: mailto="wics-sys@lists.uwaterloo.ca" sudoOption: shell_noargs dn: cn=%syscom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca objectClass: top objectClass: sudoRole cn: %syscom sudoUser: %syscom sudoHost: ALL sudoCommand: ALL sudoRunAsUser: ALL dn: cn=%termcom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca objectClass: top objectClass: sudoRole cn: %termcom sudoUser: %termcom sudoHost: honk sudoHost: hiss sudoHost: gosling sudoCommand: ALL sudoRunAsUser: ALL
Now add them:
# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f sudoers.ldif Enter LDAP Password: adding new entry "ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca" adding new entry "cn=defaults,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca" adding new entry "cn=%syscom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca" adding new entry "cn=%termcom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca"
Last, add some special local groups via
local-groups.ldif
:# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f local-groups.ldif
The local groups are special because they usually are present on all systems, but we want to be able to add users to them at the LDAP level. For instance, the audio group controls access to sound equipment, and the adm group controls log read access.That's all the entries we have to add manually! Now we can use software for the rest. See
ceo
for more details.
Querying LDAP
There are many tools available for issuing LDAP queries. Queries should be issued to ldap1.csclub.uwaterloo.ca. The search base you almost certainly want is dc=csclub,dc=uwaterloo,dc=ca. Read access is available without authentication; Kerberos is used to authenticate commands which require it.
Example:
ldapsearch -x -h ldap1.csclub.uwaterloo.ca -b dc=csclub,dc=uwaterloo,dc=ca uid=ctdalek
The -x option causes ldapsearch to switch to simple authentication rather than trying to authenticate via SASL (which will fail if you do not have a Kerberos ticket).
The University LDAP server (uwldap.uwaterloo.ca) can also be queried like this. Again, use "simple authentication" as read access is available (from on campus) without authentication. SASL authentication will fail without additional parameters.
Example:
ldapsearch -x -h uwldap.uwaterloo.ca -b dc=uwaterloo,dc=ca "cn=Prabhakar Ragde"
Replication
While ldap1.csclub.uwaterloo.ca (auth1) is the LDAP master, an up-to-date replica is available on ldap2.csclub.uwaterloo.ca (auth2).
In order to replicate changes from the master, the slave maintains an authenticated connection to the master which provides it with full read access to all changes.
Specifically, /etc/systemd/system/k5start-slapd.service maintains an active Kerberos ticket for ldap/auth2.csclub.uwaterloo.ca@CSCLUB.UWATERLOO.CA in /var/run/slapd/krb5cc. This is then used to authenticate the slave to the server, who maps this principal to cn=ldap-slave,dc=csclub,dc=uwaterloo,dc=ca, which in turn has full read privileges.
In the event of master failure, all hosts should fail LDAP reads seamlessly over to the slave.
Modifying LDAP entry
Editing entries can be easily done with ldapvi
. First search for the entry using ldapsearch
like above, and change ldapsearch -x
to ldapvi -Y GSSAPI
to make your edits.
Note that if your EDITOR enviroment is set to something not avaliable it will give out errors like
error (misc.c line 180): No such file or directory editor died error (ldapvi.c line 83): No such file or directory
This can be fixed by something like
EDITOR=vi ldapvi ******
Changing a user's username
Only a member of the Systems Committee can change a user's username. At all times, a user's username must match the user's username in WatIAM.
All changes to an account MUST be done in person so that identity can be confirmed. If a member cannot attend in person, then an alternate method of identity verification may be chosen by the Systems Administrator.
- Edit entries in LDAP (
ldapvi -Y GSSAPI
)- Find and replace the user's old username with the new one (
%s/$OLD/$NEW/g
)
- Find and replace the user's old username with the new one (
- Change the user's Kerberos principal (on auth1,
renprinc $OLD $NEW
) - Move the user's home directory (on aspartame,
mv /users/$OLD /users/$NEW
) - Change the user's csc-general (and csc-industry, if subscribed) email address for
$OLD@csclub.uwaterloo.ca
to$NEW@csclub.uwaterloo.ca
- If the user has vhosts on caffeine, update them to point to their new username
If the user's account has been around for a while, and they request it, forward email from their old username to their new one.
- Edit
/etc/aliases
on mail.$OLD: $NEW
- Run
newaliases