Configuring LDAP services (revision 1.2)

Last-Updated: 12/02/2022

Introduction

Think about the traditional Unix user, password and group definitions:

  • they are stored as flat file databases (i.e. text files in which one line is one record) in /etc/passwd, /etc/shadow and /etc/group
  • they are simple text files, therefore …
  • … we can modify them with a text editor (e.g. vi, emacs).

But with LDAP:

  • they are stored in a hierarchical database, known in LDAP parlance as a Directory Information Tree (DIT) under /var/lib/ldap
  • we are not concerned with how the data is encoded, therefore …
  • … we may only modify the database with commands that do understand how the data is encoded, or rather, with commands (ldapadd, ldapmodify, ldapdelete) that communicate with the program (slapd) that does understand how the data is encoded.

DITs are identified by their base Distinguished Name (DN). The base DN is something you decide for yourself, but typically it is the ‘dc’-prefixed and comma-separated list of labels in the fully qualified domain name of an organisation. For example, my LDAP server is orzo.pasta.net and my home network’s domain is pasta.net, so my base DN should probably be ‘dc=pasta,dc=net’. That particular base DN is referenced in all the commands below, so remember to adjust to suit your own requirements!

The entries in the DIT are identified using their Relative Distinguished Name (RDN) followed by the parent entry’s DN; e.g. ‘ou=Users,dc=pasta.dc=net’.

Many entries in the DIT (e.g. ‘ou=Users,dc=pasta.dc=net’, ‘ou=Groups,dc=pasta.dc=net’) are created to act as containers for other entries (‘cn=fred,ou=Users,dc=pasta.dc=net’, ‘cn=bert,ou=Users,dc=pasta.dc=net’), allowing like entries to be grouped together.

LDAP configuration data (e.g. the LDAP admin’s password, which SASL authentication mechanisms to allow, the name of the DIT) can be stored in one of two ways:

  • in files under /etc/ldap/slapd.d/ and are managed by editing these files and then restarting the LDAP daemon (slapd), or
  • in a separate DIT called the Runtime Configuration (RTC) DIT (typically with base DN set to ‘cn=config’) and are managed (like the regular DIT) by using commands (e.g. ldapadd, ldapmodify).

While the first option looks like it might be easier to manage, the second option seems to be what most people are using now, so I will use the second option. We will refer to the two DITs as the regular DIT and the RTC DIT. (There is an additional third DIT called ‘frontend’; see section 5.2.5.1 of this document for details.)

There are various vendor-specific conventions regarding the names of OUs within the regular DIT (e.g. some put user records under a ‘People’ node, others put them under a ‘Users’ node). There are no fixed rules except that:

  • you need to be consistent
  • you will need to tell LDAP clients (e.g. the passwd program) where in the DIT the data they read/write is stored.

In this document I will store:

  • all user entries under ‘ou=Users’
  • all associated group entries under ‘ou=Groups’
  • all associated automounter entries under ‘ou=admin,ou=automounter’ (which seems to be what the automounter is configured to expect by default)

This document is heavily based on the work of others; see the links at the bottom of the page for details.

This document was written for Debian 11, which uses LDAP server version 2.4.57, so remember to adjust to suit your own requirements!

Basic server installation

  1. Install the LDAP server and tools without being asked any questions by running:
    DEBIAN_FRONTEND=noninteractive apt -y install slapd ldap-utils

    (We skip the questions because otherwise it would have asked only a subset of the questions we want to answer! The full list will be answered shortly.)

  2. Check the name of the RTC DIT by running:
    ldapsearch -H ldap:// -x -s base -b "" -LLL "configContext"
    

    (Probably that reports that the configuration DIT is called ‘cn=config’.)

  3. Check the name of the regular DIT by running:
    ldapsearch -Y EXTERNAL -H ldapi:/// -s base -b "" -LLL "namingContexts"

    and if that reports nothing or something other than ‘dc=pasta,dc=net’ then answer the above-avoided questions plus some more by running:

    dpkg-reconfigure slapd 

    answering the questions as follows:

    1. omit OpenLDAP server configuration: accept the default (i.e. the server configuration will be completed).
    2. DNS domain name: accept the default
    3. Organization name: accept the default (the same as the DNS domain name)
    4. set the password for the LDAP administrator (actually, I think this means: set the password for the administrator of the regular DIT)
    5. database backend to use: accept the default (MDB)
    6. remove database when slapd is purged: accept the default (no)
    7.  move the old database: accept the default (yes)
  4. Note that, if the base DN was correct (which it might well be and was in my case) then this means we have not set a password for the LDAP administrator yet.
  5. Test as follows:
    1. Run:
      orzo# ldapwhoami -x
      anonymous
      orzo#

      and verify the output is as shown. (It reports ‘anonymous’ as root has not been properly authenticated when using this command.)

    2. Attempt the same command using SASL authentication, just pressing ENTER when prompted for a password:
      orzo# ldapwhoami -U admin
      SASL/DIGEST-MD5 authentication started
      Please enter your password: 
      ldap_sasl_interactive_bind_s: Invalid credentials (49)
      	additional info: SASL(-13): user not found: no secret in database
      orzo#
    3. Note, as described here, that:

      Some documentation and examples in the wild assume the existence of the entry cn=admin,cn=config in the RTC DIT and use this as the root Distinguished Name (DN). However, the default installation does not create any RootDN/RootPW entries in the RTC DIT. You must use the EXTERNAL mechanism to bind and manage the RTC DIT in the default installation. To manage the RTC DIT with tools such as slapadd or ldapmodify, bind with -Y EXTERNAL -H ldapi:///:

      #test if OpenLDAP/slapd is running correctly:
      sudo ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config

       

    4. Note that that means we do not have to set a password for the LDAP administrator in order to be secure.
    5. So from here on:
      • commands accessing the RTC DIT will use options to bind (i.e. to authenticate oneself, possibly specifying the ID of an entry in the DIT and the credentials stored in that entry)  -Y EXTERNAL -H ldapi:/// (the first option selects the SASL authentication mechanism that authenticates the user with UID==0 and GID==0; the second option means to connect over the Unix socket /var/run/ldap)
      • commands accessing the regular DIT will use options to bind -x -D cn=admin,dc=pasta,dc=net -W (the first option selects simple/non-SASL authentication, which requires specifying the ID of an entry in the DIT and the credentials stored in that entry; the second option specifies the ID of the entry in the DIT; the third options forces the password to be prompted for, rather than requiring it to be specified on the command line).
    6. So now attempt that ldapwhoami command again, but this time using SASL authentication over the Unix socket channel:
      ziti# ldapwhoami -Y EXTERNAL -H ldapi:///
      SASL/EXTERNAL authentication started
      SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
      SASL SSF: 0
      dn:gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
      ziti#
  6. Reset the LDAP administrator’s password as described below.

Nicer management tools

While ldapadd, ldapdelete, ldapmodify are sufficient (and required for most of this procedure), there are nicer tools available. This section installs a couple of them.

  1. To install, configure and test shelldap:
    1. Run:
      apt -y install shelldap
    2. Create ~/.shelldap.rc containing:
      server: localhost
      binddn: cn=admin,dc=pasta,dc=net
      bindpass: <admin-password-in-plaintext>
      basedn: dc=pasta,dc=net
      tls: no
      
    3. Run shelldap and verify the output as below:
      orzo# shelldap 
      ~ > ls
      - cn=admin
      ~ > exit
      orzo#
  2. To install, configure and test ldap-ui:
    1. Use shelldap to ensure that the cn=admin,dc=pasta,dc=net entry in the DIT has the following attributes:
      objectClass: uidObject
      uid: admin

      (While shelldap seems happy without these attributes, ldap-ui will not be.)

    2. Run:
      apt -y install libsasl2-dev python-dev libldap2-dev libssl-dev python3-venv python3-ldap python3-pip
      mkdir -p /usr/local/opt
      cd /usr/local/opt
      git clone https://github.com/dnknth/ldap-ui.git
      cd ldap-ui
      make
    3. Press CTRL-C to exit.
    4. Run:
      export LDAP_URL=ldap://localhost/
      export BASE_DN=dc=pasta,dc=net
      /usr/local/opt/ldap-ui/.venv3/bin/hypercorn -b 0.0.0.0:5000 -c file:/usr/local/opt/ldap-ui/settings.py app:app &

      (Note LDAP_URL and BASE_DN could be propagated into the environment of this one process, but the commands for setting up ldap-ui more permanently, a little bit further down this sub-procedure, rely on them being more widely scoped.)

    5. Login as user ‘admin’ (not ‘cn=admin,dc=pasta,dc=net’) with the admin’s password.
    6. Press CTRL-C to exit.
    7. To configure ldap-ui to start at boot time, run:
      sed -e "s@/opt/@/usr/local/opt/@g" etc/ldap-ui.service \
          -e "s@^Environment.*@Environment=LDAP_URL=$LDAP_URL BASE_DN=$BASE_DN@" \
          -e 's/127\.0\.0\.1/0.0.0.0/' \
          > /etc/systemd/system/ldap-ui.service
      systemctl daemon-reload
      systemctl start ldap-ui
      systemctl enable ldap-ui

Expanding the structure

Before we add users, groups, automount details, we need to add the containers (e.g. ‘ou=Users’) to accomodate them. You may wish to archive the LDIF files you import or create.

  1. You might wan to save a list of the currently known schemas:
    ldapsearch -o ldif-wrap=no -H ldap://localhost/ -x -s base -b "cn=subschema" objectclasses | \
        sed -nr "s/.* NAME '([^']+)'.*/\\1/p" | sort > before-schemas
  2. If autofs support is required then:
    1. As per instructions here, define the automountMap object class by running:
      wget -qO classes-autofs.ldif https://launchpadlibrarian.net/55451730/autofs.ldif
      ldapadd -Y EXTERNAL -H ldapi:/// -f classes-autofs.ldif

      Note that:

      • In case the LDIF files disappears from that location, here is a copy:
        dn: cn=autofs,cn=schema,cn=config
        objectClass: olcSchemaConfig
        cn: autofs
        olcAttributeTypes: {0}( 1.3.6.1.1.1.1.25 NAME 'automountInformation' DESC 'Inf
         ormation used by the autofs automounter' EQUALITY caseExactIA5Match SYNTAX 1.
         3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
        olcObjectClasses: {0}( 1.3.6.1.1.1.1.13 NAME 'automount' DESC 'An entry in an 
         automounter map' SUP top STRUCTURAL MUST ( cn $ automountInformation $ object
         class ) MAY description )
        olcObjectClasses: {1}( 1.3.6.1.4.1.2312.4.2.2 NAME 'automountMap' DESC 'An gro
         up of related automount objects' SUP top STRUCTURAL MUST ou )
        

        (It is correct that words are split in the middle at the end of some lines.)

      • The object class can also be defined by installing the autofs-ldap package but that pulls in a lot of dependencies, including autofs, which is probably not wanted on the LDAP server.
    2. Create structure-autofs.ldif containing
      dn: ou=automount,dc=pasta,dc=net
      ou: automount
      objectClass: top
      objectClass: organizationalUnit
      
    3. If you want to store the auto.master map itself in LDAP (I do not!) then add this to the same file (with a blank line separating it from any existing content in the file):
      dn: ou=auto.master,ou=automount,dc=pasta,dc=net
      ou: auto.master
      objectClass: top
      objectClass: automountMap
      
    4. If you want to store the auto.home map in LDAP (I do!) then add this to the same file (with a blank line separating it from any existing content in the file):
      dn: ou=auto.home,ou=automount,dc=pasta,dc=net
      ou: auto.home
      objectClass: top
      objectClass: automountMap
      
    5. If you want to store the auto.staging map in LDAP (I do!) then add this to the same file (with a blank line separating it from any existing content in the file):
      dn: ou=auto.staging,ou=automount,dc=pasta,dc=net
      ou: auto.staging
      objectClass: top
      objectClass: automountMap
    6. Run:
      ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f structure-autofs.ldif
  3. Create structure-users.ldif containing:
    dn: ou=Users,dc=pasta,dc=net
    objectClass: organizationalUnit
    ou: Users
    
    dn: ou=Groups,dc=pasta,dc=net
    objectClass: organizationalUnit
    ou: Group
  4. Run:
    ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f structure-users.ldif

SASL authentication

Note that this procedure appears to work but it does not! As soon as ldappasswd is run then the password reverts from plaintext to SSHA. Since plaintext is not secure anyway (root can see users’ passwords) then I won’t use SASL, but will try LDAP over SSL instead. I.e. I did not do this!

  1. Create a file entries-config-sasl.ldif containing:
    dn: cn=config
    changetype: modify
    add: olcAuthzRegexp
    olcAuthzRegexp: uid=([^,]*),cn=digest-md5,cn=auth ldap:///dc=pasta,dc=net??sub?(uid=$1)

    Note the use of a regular expression to help locate crendentials. This way it doesn’t matter if users info is stored unde ou=Users, ou=People or whatever.

  2. Apply this change by running:
    ldapmodify -Y EXTERNAL -H ldapi:/// -f  entries-config-sasl.ldif
    
  3. To test SASL authentication:
    1. Create a file entries-users-test.ldif containing:
      dn: uid=test,ou=Users,dc=pasta,dc=net
      objectClass: account
      objectClass: posixAccount
      cn: test
      gidNumber: 1000
      homeDirectory: /home/test
      uid: test
      uidNumber: 1000
      userPassword: opensesame
    2. Apply the change by running:
      ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f entries-users-test.ldif
    3. Check if SASL authentication is now working for that single user by running:
      ldapwhoami -U test

      and checking the looks like this:

      SASL/DIGEST-MD5 authentication started
      Please enter your password: 
      SASL username: test
      SASL SSF: 128
      SASL data security layer installed.
      dn:uid=test,ou=users,dc=pasta,dc=net
    4. Delete the test user again:
      ldapdelete -x -D cn=admin,dc=pasta,dc=net -W uid=test,ou=Users,dc=pasta,dc=net

Let’s Encrypt certificate

Beware that replacing an LDAP server will be more complicated since we don’t want to break the certificate chain for the old server.

  1. Ensure that the front-end webserver to does not forward requests that it receives over https to the mail server (it is not needed).
  2. Ensure that the front-end webserver forwards requests that it receives over http to the LDAP server, with a configuration something like this:
    <VirtualHost *:80>
        ServerName ldap.pasta.freemyip.com
        ServerAdmin webmaster@dont-use-this-address
        CustomLog /var/log/apache2/ldap.pasta.freemyip.com.pasta.freemyip.com/ldap.pasta.freemyip.com.pasta.freemyip.com-access.log combined2
        ErrorLog /var/log/apache2/ldap.pasta.freemyip.com.pasta.freemyip.com/ldap.pasta.freemyip.com.pasta.freemyip.com-error.log
        LogLevel warn
        ServerSignature Off
    
        <Location />
            ProxyPass http://ldap.pasta.net/
            ProxyPassReverse http://ldap.pasta.net/
        </Location>
    </VirtualHost>
  3. On the DNS server, update the CNAME record and RPZ for ‘ldap.pasta.freemyip.com’ to point to the new LDAP server.
  4. On the LDAP server install and run certbot as follows:
    apt -y install certbot
    certbot certonly --standalone -d  <public-dns-name>    # e.g. ldap.pasta.freemyip.com

    (The option --standalone will – whenever requesting/renewing a certificate – start a standalone internal webserver to allow letsencrypt.org authenticate the renewal request; /etc/cron.d/certbot will respect this option when it is used to initially request the certificate.)

  5. Locate the certificate and key generated by certbot:
    find /etc/letsencrypt/live/ -name fullchain.pem -o -name privkey.pem -o -name cert.pem

LDAP over SSL

  1. Set some environment variables:

    FQDN=<fully-qualified-domain-name> # e.g. FQDN=ldap.pasta.freemyip.com
  2. Edit /etc/default/slapd and set:
    SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"
  3. Run:
    systemctl restart slapd
  4. Check that slapd is now listening on port 636, as shown in this output:
    orzo# netstat -plutn | grep slapd
    tcp 0 0 0.0.0.0:636 0.0.0.0:* LISTEN 3913/slapd 
    tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN 3913/slapd 
    orzo#
  5. Copy the LetsEncrypt certificate files to where the slapd process can read them:
    mkdir /etc/ldap/certs
    cd /etc/letsencrypt/live/$FQDN
    for FILE in *.pem; do
        cp $FILE /etc/ldap/certs/$FQDN.$FILE
    done
    chown -R openldap:openldap /etc/ldap/certs
  6. Create entries-config-cert1.ldif containing:
    dn: cn=config
    changetype: modify
    replace: olcTLSCACertificateFile
    olcTLSCACertificateFile: /etc/ldap/certs/FQDN.fullchain.pem
  7. Create entries-config-cert2.ldif containing:
    dn: cn=config
    changetype: modify
    replace: olcTLSCertificateKeyFile
    olcTLSCertificateKeyFile: /etc/ldap/certs/FQDN.privkey.pem
    
    
  8. Create entries-config-cert3.ldif containing:
    dn: cn=config
    changetype: modify
    replace: olcTLSCertificateFile
    olcTLSCertificateFile: /etc/ldap/certs/FQDN.cert.pem
  9. Make the necessary substitutions in those files:
    sed -i "s/FQDN/$FQDN/g" entries-config-cert*.ldif
  10. Run:
    ldapsearch -o ldif-wrap=no -Y EXTERNAL -H ldapi:/// -z 0 -b "cn=config" "(objectclass=*)" > before
    ldapmodify -Y EXTERNAL -H ldapi:/// -f entries-config-cert1.ldif
    ldapmodify -Y EXTERNAL -H ldapi:/// -f entries-config-cert2.ldif
    ldapmodify -Y EXTERNAL -H ldapi:/// -f entries-config-cert3.ldif
    ldapsearch -o ldif-wrap=no -Y EXTERNAL -H ldapi:/// -z 0 -b "cn=config" "(objectclass=*)" > after
    diff before after

    You should expect the diff command to show lines have been added. (This may seem a stupid wau to do this, but putting all three stanzas in one file generally doesn’t work and this way facilitates reordering most easily. See the many questions on Stackexchange regarding this.)

  11. Test by running:
    orzo# ldapwhoami -H ldaps://ldap.pasta.freemyip.com/ -x -D "cn=admin,dc=pasta,dc=net" -W
    Enter LDAP Password: 
    dn:cn=admin,dc=pasta,dc=net
    orzo#
  12. Note that the host name in the LDAP URL must agree with the CN for which the certificate was issued; here is a command showing what happens when it doesn’t:
    orzo# ldapwhoami -H ldaps://ldap.pasta.net/ -x -D "cn=admin,dc=pasta,dc=net" -W -d-1
    Enter LDAP Password: 
    ...
    TLS: hostname (ldap.pasta.net) does not match common name in certificate (ldap.pasta.freemyip.com).
    TLS: can't connect: (unknown error code).
    ldap_err2string
    ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
    orzo#
  13. Note the procedure is enough to make LDAP over SSL work … until the certificate gets renewed! At that point the copy of the certificate files in /etc/ldap is out of date with respect to those in /etc/letsencrypt! To address that we need a simple plugin.
  14. Create a script to instruct slapd to reload its configuration when the Let’s Encrypt certificate gets renewed:
    {
        echo '#!/bin/sh'
        echo "FQDN=$FQDN"
        echo 'cp /etc/letsencrypt/live/$FQDN/fullchain.pem /etc/ldap/certs/$FQDN.fullchain.pem'
        echo 'cp /etc/letsencrypt/live/$FQDN/privkey.pem /etc/ldap/certs/$FQDN.privkey.pem'
        echo 'cp /etc/letsencrypt/live/$FQDN/privkey.pem /etc/ldap/certs/$FQDN.chain.pem'
        echo 'cp /etc/letsencrypt/live/$FQDN/privkey.pem /etc/ldap/certs/$FQDN.cert.pem'
        echo 'chown openldap:openldap /etc/ldap/certs/$FQDN.fullchain.pem'
        echo 'chown openldap:openldap /etc/ldap/certs/$FQDN.privkey.pem'
        echo 'chown openldap:openldap /etc/ldap/certs/$FQDN.chain.pem'
        echo 'chown openldap:openldap /etc/ldap/certs/$FQDN.cert.pem'
        echo 'systemctl restart slapd'
    } > /etc/letsencrypt/renewal-hooks/deploy/slapd
    chmod 755 /etc/letsencrypt/renewal-hooks/deploy/slapd
  15. Edit ~/.shelldap.rc and adjust server and tls.

Adding real entries

  1. If you want to add an entry to auto.staging for the mailserver to automount the mail directory, using a symlink pointing from /var/mail to /staging/mail (I did but not any more) then create entries-autofs-mail.ldif containing:
    dn: cn=mail,ou=auto.staging,ou=automount,dc=pasta,dc=net
    cn: mail
    objectClass: top
    objectClass: automount
    automountInformation: -nordirplus,noatime,nodiratime,nfsvers=3,nolock,proto=tcp filer-on-storage-net.pasta.net,filer-on-public-net.pasta.net:/vol/small/mail
    

    and run:

    ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f entries-autofs-mail.ldif
  1. If you want to add an entry to auto.staging for any machine to automount /pub, using a symlink pointing from /pub to /staging/pub (I do) then create entries-autofs-pub.ldif containing:
    dn: cn=pub,ou=auto.staging,ou=automount,dc=pasta,dc=net
    cn: pub
    objectClass: top
    objectClass: automount
    automountInformation: -nordirplus,noatime,nodiratime,nfsvers=3,nolock,proto=tcp filer-on-storage-net.pasta.net,filer-on-public-net.pasta.net:/vol/pub

    and run:

    ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f entries-autofs-pub.ldif
    

Creating a normal user

  1. Create entries-users-alexis.ldif containing:
    dn: cn=alexis,ou=Groups,dc=pasta,dc=net
    objectClass: posixGroup
    cn: alexis
    gidNumber: 1000
    description: Group account
    
    dn: uid=alexis,ou=Users,dc=pasta,dc=net
    objectClass: account
    objectClass: posixAccount
    cn: alexis
    uid: alexis
    uidNumber: 1000
    gidNumber: 1000
    homeDirectory: /home/alexis
    loginShell: /bin/bash
    gecos: Alexis Huxley
    description: User account
    

    which is deliberately missing the userPassword attribute.

  2. If using the automounter (I do!) then add this to the same file (with a blank line separating it from any existing content in the file):
    dn: cn=alexis,ou=auto.home,ou=automount,dc=pasta,dc=net
    cn: alexis
    objectClass: top
    objectClass: automount
    automountInformation: -nordirplus,noatime,nodiratime,nfsvers=3,nolock,proto=tcp filer-on-storage-net.pasta.net,filer-on-public-net.pasta.net:/vol/home/alexis
  3. Create the user by running:
    ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f entries-users-alexis.ldif
  4. Set the user’s password by running:
    ldappasswd -x -D cn=admin,dc=pasta,dc=net  -W -S "uid=alexis,ou=Users,dc=pasta,dc=net"
  5. Repeat for other users.

Client configuration

Note that pcms takes care of this for me!

  1. Run:
    apt-get install libnss-ldapd nslcd libldap-common

    (libnss-ldapd and nslcd are required for programs that consult nssswitch.conf, e.g. ‘getent’, ‘id’; libldap-common is required for programs that consult /etc/ldap/ldap.conf and then contact the LDAP server directly, e.g. ldapsearch).

  2. At the ‘Configuring nslcd’ window, when prompted ‘LDAP server URI’, enter ‘ldap://<ldap-server-ip-address>/’.
  3. At the ‘Configuring nslcd’ window, when prompted ‘LDAP server search base’, enter the correct search base as set earlier (e.g. ‘dc=pasta,dc=net’)
  4. At the ‘Configuring libnss-ldapd’, when prompted ‘Name services to configure’, enable passwd, group and shadow services.
  5. Test by running:
    getent passwd alexis
    

Resetting the LDAP administrator’s password

  1. Log in to the LDAP server.
  2. Set the password for the RTC DIT as follows:
    1. Get the LDIF-formatted entry specifying the current password, as in this example:
      orzo# ldapsearch -H ldapi:// -LLL -Q -Y EXTERNAL -b "cn=config" "(olcRootDN=*)" dn olcRootDN olcRootPW
      dn: olcDatabase={0}config,cn=config
      olcRootDN: cn=admin,cn=config
      
      dn: olcDatabase={1}mdb,cn=config
      olcRootDN: cn=admin,dc=pasta,dc=net
      olcRootPW: {SSHA}YYYYYYYYYYYYYYYYYYYYYYYYYY
      
      orzo# 
      

      and note:

      • the use of -H ldapi:// -Y EXTERNAL to communicate over a channel that obviates the need to provide credentials in order to authenticate oneself
      • the hash type used to store the old password, which in this case is {SSHA}
      • the database format, which in this case is mdb.
    2. Use slappasswd, possibly with the -h option to use the same hash type as before, to generate the hash of the new password, e.g.:
      ziti# slappasswd -h '{SSHA}'
      New password: 
      Re-enter new password: 
      {SSHA}ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
      ziti#
    3. Create a new LDIF file called entries-config-admin-passwd.ldif containing:
      dn: olcDatabase={1}<database-format-e.g.-mdb>,cn=config
      changetype: modify
      replace: olcRootPW
      olcRootPW: <hashed-password-including-hash-type-prefix-as-reported-by-slappasswd-above>
      
    4. Apply this by running:
      ldapmodify -H ldapi:// -Y EXTERNAL -f entries-config-admin-passwd.ldif
  3. How to the password for the regular DIT, depends on whether you are modifying the entry (because you did run dpkg-reconfigure slapd above, which will have created the entry for you) or creating the entry (because you did not run dpkg-reconfigure slapd above). If you are modifying the entry then:
    1. Create entries-admin-passwd.ldif containing:
      dn: cn=admin,dc=pasta,dc=net
      changetype: modify
      replace: userPassword
      userPassword: <hashed-password-including-hash-type-prefix-as-reported-by-slappasswd-above>
      
    2. Apply this by running:
      ldapmodify -H ldap:// -x -D "cn=admin,dc=pasta,dc=net" -W -f entries-admin-passwd.ldif
      

      using simple/non-SASL authentication (-x) using the RTC password just set above.

    and if you are adding the entry then:

    1. Create entries-admin-passwd.ldif containing:
      dn: cn=admin,dc=pasta,dc=net
      objectClass: simpleSecurityObject
      objectClass: organizationalRole
      cn: admin
      description: LDAP administrator
      userPassword: <hashed-password-including-hash-type-prefix-as-reported-by-slappasswd-above>
      
    2. Apply this by running::
      ldapadd -H ldap:// -x -D "cn=admin,dc=pasta,dc=net" -W -f entries-admin-passwd.ldif

      using simple/non-SASL authentication (-x) using the RTC password just set above.

  4. Test that you can authenticate yourself as admin against the admin’s entry in the regular DIT:
    orzo# ldapwhoami -W -x -D cn=admin,dc=pasta,dc=net
    Enter LDAP Password: 
    dn:cn=admin,dc=pasta,dc=net
    orzo#

Determining which SASL authentication mechanisms are enabled

  1. Run:
    ldapsearch -LLL -H ldapi:// -Y EXTERNAL -D dc=pasta,dc=net -s base -b "" "(objectclass=*)" supportedSASLMechanisms

    (This is taken from here.)

  2. For reasons I don’t yet understand the following command gives different results:
    ldapsearch -LLL -x -s base -b "" "(objectclass=*)" supportedSASLMechanisms

    (This is based on something taken from here.)

ldapscripts

We deliberately use only ldapadd, ldapdelete and ldapmodify as these should work everywhere and keep the software stack to a minimum, but there is a package that will allow some shortcuts, especially for creating groups and users. I do not use it!

  1. Run:
    apt-get install ldapscripts
  2. Edit /etc/ldapscripts/ldapscripts.conf and set the following, being sure to ignore the Debian-specific ways of doing things:
    SERVER="ldap://localhost"
    SUFFIX="dc=pasta,dc=net"
    GSUFFIX="ou=Groups"
    USUFFIX="ou=Users"
    MSUFFIX="ou=Machines"
    SASL_AUTH=""
    BINDDN="cn=admin,dc=pasta,dc=net"
    PASSWORDGEN="<ask>"

    (That’s a literal ‘<‘ and ‘>’.)

  3. Run:
    echo -n '<the-ldap-password-you-set-earlier>' > /etc/ldapscripts/ldapscripts.passwd
    chmod 600 /etc/ldapscripts/ldapscripts.passwd
  4. Test by running:
    lsldap

List contents of DITs

  1. To list the whole contents of the regular DIT run
    ldapsearch -z 0 -W \
        -b "dc=pasta,dc=net" \
        -D "cn=admin,dc=pasta,dc=net" \
        "(objectclass=*)"
  2. To list the whole contents of the RTC DIT run:
    ldapsearch -W -Y EXTERNAL -H ldapi:/// -z 0 \
        -b "cn=config" \
        "(objectclass=*)"
    

Backing up and restoring DITs

How To Backup and Restore OpenLDAP

Stuff for me to investigate further

  • chsh probably doesn’t work
  • force changing password see https://serverfault.com/questions/255603/ldap-force-user-to-change-password

See also