[Network Administration]: LDAP Directory Service – Backend

I have my authentication service up with the Kerberos services. Next I need a service to perform authorization. This will determine what services are granted once authorization has been performed. For this I’m using an LDAP directory. LDAP stands for Lightweight Directory Access Protocol. LDAP is a protocol for accessing a directory. The actual implementation that I’m using is OpenLDAP which is an open source implementation of the LDAP directory server running on a Linux machine. The directory can contain all types of information that conforms to a set of schema that is specified and is searchable.

I’ve used this in the past for both authentication and also authorization, as well as a directory. There were some issues with using the LDAP directory for authentication that I didn’t like. To enable use of stronger SASL methods for authentication the passwords in the directory needed to be stored in cleartext. This was something that has always made me uneasy. If the passwords were encrypted, then this precludes the use of any of the stronger SASL methods for authentication to the directory. Secondly, Kerberos is the method to enable single sign-on.

Much of what’s listed below is the back end portion derived from LDAP System Administration which is a good overall reference, but is now pretty outdated as the configuration has changed since the book was published. There is some translation that needs to be done from the old method of configuring the service to the newer method. All of the ideas are still correct, but you’ll have to determine all of the newer configuration names. Mainly, you need to look it up in the slapd-conf man page. There is also more than enough information in the Ubuntu LTS official documentation and the community page to get started. Both have good information in terms of getting a server up and running, but don’t deal with connecting it with the other services coherently, or any discussion about how to go about really designing a directory for use. Both are much more of a getting started type of documentation. There is more information using it in concert with other services on the Ubuntu Single Sign-on page. For complete adminstrative documentation, the OpenLDAP documentation page is the place to go. It goes into all of the details of getting the server up and running and what all of the different configuration options mean.

Again, like with the Kerberos setup, I’ve compiled the version of OpenLDAP that I’m using from the source code. This is mainly due to the fact that the OpenLDAP package that is available in the Ubuntu repositories is not compiled with openssl certificate support for SSL/TLS, instead relying on the GnuTLS libraries for SSL/TLS. Using my OpenSSL certificates has caused problems with the service accepting connections resulting errors along the ones of “main: TLS init def ctx failed”. I’ve already created all of the contexts for generating OpenSSL certificates through the CAs that I’ve setup, so I did not want to now start going though a new flow just for generating certificates for my LDAP server.

Installing the packages or compiling the source
The binaries and libraries are also available in package form. For Ubuntu, the packags is slapd. LDAP utilities are also available in the package ldap-utils. Install the package with:

apt-get install slapd ldap-utils

The package installation will guide you through some configuration steps.

The source is also available from the OpenLDAP site here. Actually going to the release directory ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/ also has both MD5 and SHA1 checksums to verify the integrity of the tar file. If building from the source files, there is a dependency on both openssl libraries and a backend database. I’ve used BerkeleyDB from Oracle.
NOTE: Due to licensing changes, only versions of BerkeleyDB previous to 6.0 are compatible with OpenLDAP license. If you are looking at the Linux From Scratch page is states to modify the Makefile but that will put you in violation of the Oracle BerleleyDB license.

These are both also available as packages under Ubuntu.

apt-get install libssl-dev libdb++-dev

Some other packages that are needed to compile are:

package_dependencies += libsasl2-dev libltdl-dev libperl-dev

The source needs to be configured. I’ve used the flags listed below.

cd openldap-
./configure
make depend

conf_flags       = --prefix=/usr/local \
                   --sysconfdir=/usr/local/etc \
                   --localstatedir=/srv \
                   --libexecdir=/usr/local/lib \
                   --disable-static \
                   --enable-dynamic \
                   --enable-crypt \
                   --enable-spasswd \
                   --enable-modules \
                   --enable-rlookups \
                   --enable-backends=mod \
                   --enable-overlays=mod \
                   --disable-ndb \
                   --disable-sql \
                   --with-tls=openssl \
                   --with-cyrus-sasl \
                   --with-threads
# GSSAPI flag no longer supported in 2.4.40 (perhaps automatic/required)
#conf_flags     += --with-gssapi

I used the Linux From Scratch OpenLDAP guide for determining the compile flags. One of them is to specify that openssl is used for the TLS implementation, --with-tls=openssl, which is why I’m doing this in the first place. I’ve compiled mine to be installed under /usr/local since I’m compiling this as a separate package to in installed. I also gave it a non-standard location for the localstatedir configure flag to put the database under the /srv partition of my machine. This is in line with how I’ve also compiled my Kerberos source. We also want to support dynamically linked libraries, and enable the databases as modules in case we wanted to support different ones in the future without needing to recompile from scratch each time. We’re also going to compile in support for Cyrus SASL.

After configuring, do the actual compile

make

Again, more beer.

After it’s done, we’ll run the tests that are included to make sure that it’s compiled properly and doing what it needs to do. There is a test target to run the tests.

make test

This also takes a while. Maybe more beer and then call it a night.

Once all of the tests are done and passing, we can install the package. I install my packages using check install to generate a package and then install it. The actual install target is install

make install

This will install the compiled libraries and executables to /usr/local/ as the base install directory as we specified when we configured the package. Some specific locations — It will expect configuration files and the slapd DIT to be located under /usr/local/etc/openldap, and the database holding the directory to be located under /srv/openldap-data.

After installing the openldap server, a daemon user and group are created for the server to run under

addgroup --system openldap && \
adduser  --system --ingroup openldap --gecos "OpenLDAP Daemon" --home /srv/openldap-data --shell /bin/false openldap

We’ll also want to tighten permissions and we’ll have to create the location of the database

chown -v -R openldap:openldap /srv/openldap-data && \
chmod -v -R u=rwX,g=rX,o= /srv/openldap-data && \
chown -v -R root:openldap /usr/local/lib/openldap && \
chmod -v -R u=rwX,g=rX,o= /usr/local/lib/openldap && \
chown -v -R root:openldap /usr/local/etc/openldap && \
chmod -v -R u=rwX,g=rX,o=rX /usr/local/etc/openldap && \
chmod -v -R u=rwX,g=rX,o= /usr/local/etc/openldap/slapd.d

This will change the database location to be owned by the openldap user and group and give it full permissions to the owner, and read/execute permissions to the group. Everything else has no permissions. The library and configuration directories are owned by root and the openldap group. The libraries have full root permissions, and read/execute permissions for the openldap group. These allow the openldap daemon to run the server, but it cannot itself make any changes to the files. The configuration directory /usr/local/etc/openldap has read and execute permissions for everyone, and write permission only by root. The ldap utilities expect to be able to read the ldap client configuration file located there, so read permissions need to be given to these files. The slapd.d directory however needs to have restrictive permissions. This is where it stores its backend configuration. This will need to have read permissions by the openldap group, and write permission by root to allow it to be configured. No one else should have permission to access the directory. To be able to have the daemons startup, some startup files need to also be created. For this, I’ve reused the Ubuntu slapd package init.d files and their apparmour file with some slight tweaks so that they can find what they need. These files are:

  • /etc/apparmour.d/usr.local.bin.slapd — Slapd Apparmour file
  • /etc/init.d/slapd — Slapd init.d startup script
  • /etc/default/slapd — Slapd default configuration parameter file

Configuring Slapd
At this point the server should be able to start up, but it’s going to complatin that it doesn’t have any configuration specified. If the packages were installed, then a basic configuration should be created by the package. It can be manipulated through the standard LDAP protocol. I’ve written my own configuration with help from the O’Rielly LDAP System Administration book, the slapd-config man page, and the OpenLDAP administration documentation. Newer versions of OpenLDAP keep a runtime directory of the configuration itself (the DIT), and it can be described by an LDIF file, just like the rest of the information in the directory. I’ll describe the parts of the LDIF file. There are no spaces between the different parts unless noted.

# See slapd-config(5) for details on configuration options.
# This file should NOT be world readable.
#
dn: cn=config
objectClass: olcGlobal
cn: config
#
# Set the logging parameters
olcLogLevel: any
#olcLogLevel: conns ACL
#olcLogLevel: ACL
# This is for stderr redirect
# log messages from above log level are sent to syslog,
# but are redirected to SLAPD_LOG_DIR.
olcLogFile: /var/log/slapd/slapd-stderr.log
#
#
# Define global ACLs to disable default read access.
#
# Define the args/pid files
olcArgsFile: /var/run/openldap/slapd.args
olcPidFile: /var/run/openldap/slapd.pid
#
# Do not enable referrals until AFTER you have a working directory
# service AND an understanding of referrals.
#olcReferral:   ldap://root.openldap.org
#

First we set some general parameters. These here all belong to the olcGlobal object class. It sets the stderr log file, the args file, the pid file, and the log level for the daemon. The log level can be specified in different ways, either using the symbolic aliases, or by OR-ing the bit fields. The man page and documentation for olcLogLevel describes the different values that the log level attribute can take. Also, referrals are not enabled. There is just one directory and one server.

# Sample security restrictions
#       Require integrity protection (prevent hijacking)
#       Require 112-bit (3DES or better) encryption for updates
#       Require 64-bit encryption for simple bind
#olcSecurity: ssf=1 update_ssf=112 simple_bind=64
olcSecurity: ssf=64
#
# Setup SSL encryption
olcTLSCipherSuite: HIGH
olcTLSCACertificateFile: <CA certificate chain>
olcTLSCertificateFile: <LDAP Server Certificate File>
olcTLSCertificateKeyFile: &ltLDAP Server Private Key>
#
# SASL options
# Setup the mappings from SASL authorization to user entries
olcSaslSecProps: noplain,noanonymous,minssf=56
olcAuthzRegexp:
  uid=([^,]*),cn=[^,]*,cn=auth
  uid=$1,ou=users,dc=example,dc=com

This section is our security settings section. It still falls under the olcGlobal object class. The first line sets the overall security strength factor. A value of zero means that there is no protection. A value of 1 implies integrity protection. A value greater than 1 implies an effective key size in bits. This specific line specifies that the overall security is set to 64-bits encryption. This means that either TLS has to be at least 64-bits, or SASL authentication needs to be 64-bits strong. The man page has a good description of the different types of settings that can be used for the olcSecurity attribute. I just set the overall strength factor instead of individual tis or sasl strength facts. This allows for no SASL authentication when using TLS equivalent to 64-bits or higher, or without TLS if the SASL authentication of 64-bits or higher. For example, I can connect through the local unix socket using PAM to authenticate, but I don’t need to do TLS in this case. However, I cannot do unauthenticated queries without a TLS encrypted connection. I currently haven’t required a stronger security factor for performing updates, but this would be set here.
The next lines setup the TLS connections. The first line specifies the cipher suite (HIGH) that I’m going to use, and the next three lines point at the CA certificate chain, the server public certificate file, and the private key for TLS public key encryption. The CA certificate chain is used to verify the server certificate.
The last two attributes are for SASL authentication. The first line specifies the SASL mechanisms that are available to use. It disables SASL methods that use plain password transform, and also disable anonymous methods. The third part specifies that the SASL methods must have at least 56 bits of strength. The second line will map the SASL name onto a user entry in the database.

#
# Load dynamic backend modules:
#
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulepath:  /usr/local/lib/openldap
olcModuleload:  back_bdb.la

Next we specify our backend modules with the olcModuleList object class. This is straightforward. The first attribute is the path to the modules to load, and the second is the module for the database. I’ve specified that we are to use the Berkeley DB backend.

dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema

include: file:///usr/local/etc/openldap/schema/core.ldif
include: file:///usr/local/etc/openldap/schema/cosine.ldif
include: file:///usr/local/etc/openldap/schema/nis.ldif
include: file:///usr/local/etc/openldap/schema/inetorgperson.ldif
include: file:///usr/local/etc/openldap/schema/autofs.ldif
include: file:///usr/local/etc/openldap/schema/samba.ldif
include: file:///usr/local/etc/openldap/schema/apple_auxillary.ldif
include: file:///usr/local/etc/openldap/schema/apple.ldif

We specify the schema that we are going to be supporting for our directory. The schemas that we include are in LDIF file format. Most schemas will need to be converted from *.schema format to the LDIF format before we can include them here. The first ones are pretty standard. They specify users and groups, and a lot of the core functionality. The next one is autofs.ldif. This specifies object classes that describe automount maps stored in the directory. This lets us push automounter settings through our LDAP directory to all machines that are clients. The autofs.schema file is available through the autofs-ldap package on Ubuntu. The last three are required for OSX compatibility. I’m using the openldap directory to also enable authentication on OSX systems. Otherwise, the last three are not really needed. The samba.ldif schema describes support for Samba, but OSX opendirectory also uses some of the attributes. The Samba schema is available in the samba-doc package on Ubuntu. This one is even available as an LDIF file in the package, so no conversion needed. The last two are available on OSX systems (and samba.schema also) in /etc/openldap/schema. The OSX schema apple.schema needs tweaking to make them work with the other schemas.

  • Uncomment the “container” object class
  • Comment out all of the authmount object classes since we have the autofs.schema file
  • Move the “authAuthority” attribute all the way up before the first time it’s used
  • Fix the syntax error in the “ttl” attribute

When the slapd ldif file is imported, the schemas should be able to be included. Usually any errors are descriptive enough to determine where the problem is.

# ldif settings
#
dn: olcDatabase=frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: frontend
# Set the password hash as cleartext. This
# is needed for updates so that SASL can
# access the actual password
olcPasswordHash: {CLEARTEXT}
#
# Sample global access control policy:
#       Root DSE: allow anyone to read it
#       Subschema (sub)entry DSE: allow anyone to read it
#       Other DSEs:
#               Allow self write access
#               Allow authenticated users read access
#               Allow anonymous users to authenticate
#
#olcAccess: to dn.base="" by * read
#olcAccess: to dn.base="cn=Subschema" by * read
#olcAccess: to *
#       by self write
#       by users read
#       by anonymous auth
#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.  (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!
#

This section describes the fronted database. The main thing here is that the password is stored as cleartext. This will allow SASL authentication to the directory since the SASL modules need the actual password so that they can compute the proper hashes to compare against what the client sends. I’ll allow everyone to read anything, but updates restricted to root. I’ll set more specific ones later.

# Config settings
# The "* break" causes the evaluation of subsequent ACL statements
dn: olcDatabase=config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: config
olcAccess: to * 
  by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage 
  by * break

Now we describe the configuration for the backend (or cn=config) database. This is the database that we are currently looking at. Here we can see that we set specific access permissions here for the configuration database. The olcAccess line specifies that only the root user on the machine (through the external SASL authentication mechanism) has access to manage the database, everyone else’s access is denied. This means that the only way to access the backend database is through the root account on the machine, which in my case means that you need to have sudo access through a local account on the machine (which if you do, then you can do pretty much anything, and there is really no stopping someone at that point), but one cannot remotely manage the backend database by authenticating to the LDAP server over the network.

#######################################################################
# BDB database definitions
#######################################################################
#
dn: olcDatabase=bdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcBdbConfig
olcDatabase: bdb
olcSuffix: dc=example,dc=com
# Specify that external root access is the rootDN. The rest subject to ACL
olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth 
# Cleartext passwords, especially for the rootdn, should
# be avoided.  See slappasswd(8) and slapd-config(5) for details.
# Use of strong authentication encouraged.
#olcRootPW: secret
olcAccess: to attrs=userPassword
  by dn="cn=admin,dc=example,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=example,dc=com" write
  by  * read
# The database directory MUST exist prior to running slapd AND 
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
olcDbDirectory: /srv/openldap-data
# Indices to maintain
olcDbIndex: objectClass eq
# Should have database tuning parameters here

The last part describes the actual Berkeley DB database. The attribute olcSuffix specifies that this database stores the directory specified by the base dc=example,dc=com. This will be for the network domain example.com which we have been putting together. Again, we give root access to the root account on the local machine. We don’t specify a password since we’re letting PAM do the authentication for us. There are also 4 lines that set access permissions for our database. The lines are applied in order that they are described until one matches. More specific lines need to come at the top so that they will be applied. The first line is for the userPassword attribute. We allow the admin account to write the password, allow anonymous connections to authenticate against the password, the user itself to write the password and restrict anyone else from any access to the attribute. Access to the shadowLastChange attribute is similar, except that the admin account doesn’t have write access. Only the use can change the value (when the password is updated). The third line specifies that there is read access to DN=”” (there is some more configuration there. The last line specifies that everything in the database is readable (assuming that it’s not covered by a previous rule. Order is important. Finally, we specify where the database is located on the filesystem, and the attributes that we will use as indices into our database.

Now that we have our directory configuration, we’ll need to actually use it to configure the directory daemon. We can add this LDIF file to the slapd configuration directory using slapdadd. We’ll also create the slapd.d directory if it doesn’t exists and set restrictive permissions for it. It the package was installed, the slapd.d directory is located at /etc/ldap/slapd.d and it should already exist with proper permissions and have a configuration tree in it.

install -v -dm700 -o openldap -g openldap /usr/local/etc/openldap/slapd.d && \
/usr/local/sbin/slapadd -n 0 -F /usr/local/etc/openldap/slapd.d -l slapd.ldif && \
chown -v -R openldap:openldap /usr/local/etc/openldap/slapd.d && \
chmod -v -R u=rwX,g=,o= /usr/local/etc/openldap/slapd.d && \
service slapd start || exit 3

The LDAP daemon should start up. You should be able to access the backend database using ldapsearch. We can read the backend database as the root account like we specified using the SASL EXTERNAL mechanism.

ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"

Configuration and Data Backup
I backup the directories /usr/local/etc/openldap and /srv/openldap-data. The configuration and slapd ldif files are stored in /usr/local/etc/openldap and the directory database is stored in /srv/openldap-data. I should be able to fully recreate my server with the configuration directory in the backup once the binaries are rebuild and re-installed.

Advertisements

3 thoughts on “[Network Administration]: LDAP Directory Service – Backend

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s