Adding login for LDAP users to Ubuntu 17.10 client with TLS

I’ve configured an LDAP database with users, now I need to setup a client(fangorn) to log in.  This client is running Ubuntu 17.10.

david@fangorn:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 17.10
Release:	17.10
Codename:	artful

Installing the Client LDAP Modules

Using apt, install the libpam and libnss ldap modules.

apt install libpam-ldapd libnss-ldapd

The debconf prompts the user to enter the nslcd daemon configuration data:
set the ldap server to ldap://
set ldap search base to dc=telperion,dc=org
select passwd, group, shadow as services to configure

Configuring the Modules for TLS

Unfortunately, that isn’t enough, we also need to configure the nslcd daemon for secure tls access. Edit /etc/nslcd.conf to configure. The nslcd.conf manpage has helpful hints for any further configuration.

# The user and group nslcd should run as.
uid nslcd
gid nslcd

# The location at which the LDAP server(s) should be reachable.
uri ldap://

# The search base that will be used for all queries.
base dc=telperion,dc=org

# The LDAP protocol version to use.
ldap_version 3

# force ldap to use start_tls protocol to setup tls connections
ssl start_tls

# force the tls connection to verify the other end
# using a cert signed by the CA
tls_reqcert demand

# path to pem file containing CA and CA intermediate files
tls_cacertfile = /etc/ssl/certs/

# path to the client certificate
tls_certfile = /etc/ssl/certs/

# path to key file containing the private key for the cert
tls_key = /etc/ssl/private/

# uncomment to enable logging to syslog for debugging issues
#log syslog debug

If you experience issues, try adding logging to the nslcd service by adding the following line to /etc/nslcd.conf and restarting nslcd service.

log syslog debug

pam ldap uses the default ldap.conf file located in /etc/ldap. Edit /etc/ldap/ldap.conf to add similar key paths and requirements for verification.

BASE    dc=telperion,dc=org
URI     ldap://
TLS_CACERT      /etc/ssl/certs/
TLS_CERT        /etc/ssl/certs/
TLS_KEY         /etc/ssl/private/
TLS_REQCERT     demand

Make sure permissions are setup correctly on the private keys. The private key should be readable by root and ssl-cert group.

root@fangorn:/etc/ssl/private# ls -l 
-r--r----- 1 root ssl-cert 288 Feb 17 10:44

The user nslcd must be added to the ssl-cert group using the command usermod -a -G ssl-cert nslcd, otherwise start_tls will fail.

Also make sure the certs have the correct permissions:

root@fangorn:/etc/ssl/certs# ls -l | grep telperion
-r--r--r-- 1 root root   1208 Feb 17 10:44
-r--r--r-- 1 root root   1836 Feb 17 10:43

Now that everything is configured, we need to restart the nslcd and nscd services to use the configuration. On ubuntu, run systemctl restart nscd and systemctl restart nslcd.

Testing Configuration

To validate that our configuration is working for nss (using the nslcd daemon installed above), we can use genent passwd to see your ldap users. I recently added a user account for Lobelia Sackville-Baggins as a member of the telperionusers group.

root@fangorn:~# getent passwd | grep lobelia
lobelia:x:10000:5000:Lobelia Sackville-Baggins:/home/lobelia:/bin/bash
root@fangorn:~# getent group | grep telperionusers
root@fangorn:~# getent shadow | grep lobelia

To get pam working out of the box, I didn’t need to do much. Simply restarting the nscd daemon was all that was required.

Running su -l lobelia with the password, I was able to log in.   Lobelia doesn’t yet have a home directory though.   We can configure pam to handle some of these setup items for us… another post.

Secure LDAP Transport Layer With TLS

So we’ve got an OpenLDAP server (slapd) running and we’ve added some users.

Searching for or authenticating with or modifying passwords without TLS is unsafe. The following ldapsearch and wireshark capture shows the password in cleartext.

root@fangorn:~# ldapsearch -D uid=lobelia,ou=people,dc=telperion,dc=org -LLL -H ldap:// -w  ihatebilbo -b uid=lobelia,ou=people,dc=telperion,dc=org -x  userPassword
dn: uid=lobelia,ou=people,dc=telperion,dc=org
userPassword:: e1NTSEF9dmJ5N0Iwbk5IeEx1VXR0WjBXSWRUQVdrQWlXRlpsSzQ=
wireshark shows cleartext ldap traffic

If we want to use it to authenticate, we should secure the traffic. The easiest way to do that is with SSL.

Setting up a certificate hierarchy is not trivial but it is best to either generate a self signed certificate authority or get a certificate signed by a reputable CA (e.g. LetsEncrypt). Because this sample is on a closed personal network, I’ve chosen to generate a self-signed domain certificate authority and an intermediate. The CA signs the intermediate CA and the intermediate CA signs the cert for each of the nodes in the network.

the root is self signed but installed in a trusted location on each node

The default slapd package in debian stretch uses gnutls to perform it’s crypto operations by default so when pouring over the slapd manpages, keep that in mind (some options are meant for slapd built against Mozilla’s NSS and some are for OpenSSL). gnutls is pretty strict about self-signed CAs. If building the CA with OpenSSL, make sure you use the CA extensions. Also, if using Elliptic Curves, make sure you use named curves. I had trouble getting gnutls to parse OpenSSL generated certs that had curve parameters explicitly defined. Switching to using the named NIST curves solved the problem. Establishing an appropriate PKI can be done with the certtool command (from gnutls) or using OpenSSL’s command line suite. I’ve used OpenSSL to generate a CA PKI, loosely following this guide: OpenSSL Certificate Authority.

Configure the Keys and Certs

Now to configure slapd to use this cert. First copy your certs into an appropriate location. I copied my ca cert chain (a file consisting of the concatenation of the root CA and the intermediate CA) and the server cert into /etc/ssl/certs. I put the private key in /etc/ssl/private. Change the group ownership of the private key to ssl-cert to allow the openldap user (a member of the ssl-cert group) to use the key.

Configure the Server Keys (barad-dur)

Place keys in the appropriate locations and give them the appropriate permissions.

root@barad-dur:~# ls -ltr /etc/ssl/certs | tail -n2
-r--r--r-- 1 root root   1836 Feb  3 16:27
-r--r--r-- 1 root root   1208 Feb  3 16:27
root@barad-dur:~# ls -ltr /etc/ssl/private/ | tail -n1
-r--r----- 1 root ssl-cert  288 Feb  3 16:27
root@barad-dur:~# groups openldap
openldap : openldap ssl-cert

Configure the Client Keys (fangorn)

Set up the keys:

root@fangorn:~# ls -ltr /etc/ssl/certs/ | grep telperion
-r--r--r-- 1 root root   1200 Feb  3 17:09
-r--r--r-- 1 root root   1836 Feb  3 17:09
root@fangorn:~# ls -ltr /etc/ssl/private/ | grep telperion
-r--r----- 1 root ssl-cert  288 Feb  3 17:09

Configure LDAP to use TLS

Now that we have a PKI that makes sense, we can tell slapd and libldap to use the keys we’ve provided.
On the client, it is pretty easy. Configure the default ldap configuration file (/etc/ldap/ldap.conf) on the client machine (fangorn in this case) as follows:

# See ldap.conf(5) for details
# This file should be world readable but not world writeable.

BASE    dc=telperion,dc=org
URI     ldap://
TLS_CACERT      /etc/ssl/certs/
TLS_KEY         /etc/ssl/private/
TLS_CERT        /etc/ssl/certs/

Configuring the server is a little more involved because slapd uses its own directory as configuration. This means we need to use the ldapmodify command to update the config database. Create an ldif file called update-certs.ldif with the following contents:

dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/

Use ldapmodify to update the configuration:

root@barad-dur:~# ldapmodify -Y EXTERNAL -H ldapi:/// -f update-certs.ldif 
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifying entry "cn=config"

Verify that our addition “took”:

root@barad-dur:~# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config "(objectClass=olcGlobal)"
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/slapd/slapd.args
olcLogLevel: none
olcPidFile: /var/run/slapd/
olcToolThreads: 1
olcTLSCACertificateFile: /etc/ssl/certs/
olcTLSCertificateFile: /etc/ssl/certs/
olcTLSCertificateKeyFile: /etc/ssl/private/

Now we can use the “startTLS” version of ldapsearch to send ldap results over a secure channel.
Using the following command (-ZZ option forces TLS), we don’t see any cleartext passwords:

root@fangorn:~# ldapsearch -D uid=lobelia,ou=people,dc=telperion,dc=org -LLL -H ldap:// -w  ihatebilbo -b uid=lobelia,ou=people,dc=telperion,dc=org -x -ZZ  userPassword
dn: uid=lobelia,ou=people,dc=telperion,dc=org
userPassword:: e1NTSEF9dmJ5N0Iwbk5IeEx1VXR0WjBXSWRUQVdrQWlXRlpsSzQ=
wireshark shows encrypted cleartext ldap traffic