RHCSA Preparation - Script to Configure a Simple OpenLDAP Server

The ability to quickly configure an LDAP (Lightweight Directory Access Protocol) client for accessing user and group accounts is one of the skills you are expected to have when you sit the Red Hat RHSCA exam.

LDAP is a application-level protocol for accessing and maintaining distributed directory services over an IP-based network. It is specified in a series of RFCs (Request for Comments) using ASN.1. The latest LDAP specification is Version 3, published as RFC 4511.

Typically, at a minimum, LDAP is used to facilitate centralized user and group account administration. Instead of storing user and group account information locally on each host, an LDAP directory service stores them on one host and makes the relevant information available to other hosts. In this sense, LDAP is just another authentication service similar to NIS (Network information Services) or Microsoft Active Directory.

Here is the current version of my script:

#   Author: Finnbarr P. Murphy
#     Date: December 2016
#  License: BSD
#  Purpose: Configure OpenLDAP server on RHEL 7.2 or later
#           Two users added - ldapuser1, ldapuser2

# modify as necessary
PACKAGES="openldap-clients openldap-servers"

if (( $EUID != 0 )); then 
    echo "ERROR: You need root privileges to run this script"
    exit 1

if (( $# >= 1 )); then
   if [[ "$1" = "-h" || "$1" = "--help" ]]; then
      echo "Usage: $(basename $0) [-d]"
      exit 0
   if [[ "$1" = "-d" ]]; then

if yum list installed openldap-servers  > /dev/null 2>&1
    systemctl -q is-active slapd && {
        systemctl stop slapd 
        systemctl -q disable slapd
    echo -n "Removing existing LDAP server files ..... "
    yum remove -y -q -e0 $PACKAGES 
    rm -rf /etc/openldap/slapd.d/
    rm -rf /var/lib/ldap/*
    id -u ldapuser1 > /dev/null && userdel -frZ  ldapuser1 2>/dev/null 
    id -u ldapuser2 > /dev/null && userdel -frZ  ldapuser2 2>/dev/null
    echo "Done"

if [[ "$DELETE_ONLY" = "1" ]]; then
    exit 0

echo -n "Installing $PACKAGES ..... "
yum install -y -q -e0 $PACKAGES 
echo "Done"

# Handle hostname issues here
hostnamectl set-hostname $HOSTNAME 
if grep $HOSTNAME $HOSTS  > /dev/null  2>&1
    sed -i".bak" '/^#/ ! s/\(^.*'$HOSTNAME'.*\)/\#\ \1/' /etc/hosts
echo "  $HOSTNAME  server" >> $HOSTS 
echo "Modified hostname and /etc/hosts as needed."

# Generate LDAP password from a secret key (Pa$$w0rd()
slappasswd -s $PASSWORD -n > /etc/openldap/passwd

# FPM - test passwd file

# Generate X.509 certificate good for approx 6 months 
openssl req -new -x509 -nodes -out /etc/openldap/certs/cert.pem \
-keyout /etc/openldap/certs/priv.pem -days 180 \
-subj '/C=US /O=Training /OU=RHCSA RHCE Training /' \
> /dev/null 2>&1 

cd /etc/openldap/certs
chown ldap:ldap *
chmod 600 priv.pem
cd - >/dev/null

cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
slaptest > /dev/null  2>&1
chown ldap:ldap /var/lib/ldap/*

systemctl -q enable slapd
systemctl start slapd
sleep 10
echo -n "Start LDAP server daemon ..... "
systemctl -q is-active slapd
if (( $? == 0 )); then
    echo "Success"
    echo "Failed"
    exit 1

ldapadd -Y EXTERNAL -H ldapi:/// -D "cn=config" -f /etc/openldap/schema/cosine.ldif > /dev/null  2>&1

ldapadd -Y EXTERNAL -H ldapi:/// -D "cn=config" -f /etc/openldap/schema/nis.ldif > /dev/null  2>&1

cat > /etc/openldap/changes.ldif << EOF
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=Manager,dc=example,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: $(</etc/openldap/passwd) 

dn: cn=config
changetype: modify
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/cert.pem

dn: cn=config
changetype: modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/priv.pem

dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: -1

dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=Manager,dc=example,dc=com" read by * none

ldapmodify -Y EXTERNAL -H ldapi:/// -f /etc/openldap/changes.ldif > /dev/null  2>&1

cat > /etc/openldap/base.ldif << EOF
dn: dc=example,dc=com
dc: example
objectClass: top
objectClass: domain

dn: ou=People,dc=example,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=Group,dc=example,dc=com
ou: Group
objectClass: top
objectClass: organizationalUnit

ldapadd -x -w $PASSWORD -D cn=Manager,$SUFFIX -f /etc/openldap/base.ldif > /dev/null 2>&1

# Create two OpenLDAP test users
[ -d /home/ldap ] || mkdir /home/ldap
useradd -d /home/ldap/ldapuser1 ldapuser1  > /dev/null  2>&1
echo "ldapuser1:$PASSWORD" | chpasswd
useradd -d /home/ldap/ldapuser2 ldapuser2 > /dev/null  2>&1
echo "ldapuser2:$PASSWORD" | chpasswd

# Migrate existing local users with UID > 1000
echo -n > $LDIF
grep "x:10[0-9][0-9]:" /etc/passwd | 
    # U_GECOS="$(echo "$U_GECOS" | cut -d' ' -f1,2)"
    [ ! "$U_GECOS" ] && U_GECOS="$U_NAME"

    S_ENT=$(grep "${U_NAME}:" /etc/shadow)

    S_AGING=$(passwd -S "$U_NAME")

    # build up array of group IDs
    [ ! "$(echo "${GROUP_IDS[@]}" | grep "$U_GID")" ] && GROUP_IDS=("${GROUP_IDS[@]}" "$U_GID")

    echo "dn: uid=$U_NAME,ou=People,$SUFFIX" >> $LDIF
    echo "objectClass: account" >> $LDIF
    echo "objectClass: posixAccount" >> $LDIF
    echo "objectClass: shadowAccount" >> $LDIF
    echo "objectClass: top" >> $LDIF
    echo "cn: $(echo "$U_GECOS" | awk -F',' '{print $1}')" >> $LDIF
    echo "uidNumber: $U_UID" >> $LDIF
    echo "gidNumber: $U_GID" >> $LDIF
    echo "userPassword: {crypt}$(echo "$S_ENT" | cut -d':' -f2)" >> $LDIF
    echo "gecos: $U_GECOS" >> $LDIF
    echo "loginShell: $U_SHELL"  >> $LDIF
    echo "homeDirectory: $U_DIR" >> $LDIF
    echo "shadowExpire: ${S_AGING_ARRAY[6]}" >> $LDIF
    echo "shadowWarning: ${S_AGING_ARRAY[5]}" >> $LDIF
    echo "shadowMin: ${S_AGING_ARRAY[3]}" >> $LDIF
    echo "shadowMax: ${S_AGING_ARRAY[4]}" >> $LDIF
    echo >> $LDIF

for G_GID in "${GROUP_IDS[@]}"
    L_CN="$(grep ":$G_GID:" /etc/group | cut -d':' -f1)"
    echo "dn: cn=$L_CN,ou=Group,$SUFFIX" >> $LDIF
    echo "objectClass: posixGroup" >> $LDIF
    echo "objectClass: top" >> $LDIF
    echo "cn: $L_CN" >> $LDIF
    echo "gidNumber: $G_GID" >> $LDIF
    echo >> $LDIF

ldapadd -x -w $PASSWORD -D cn=Manager,$SUFFIX -f $LDIF > /dev/null 
rm -rf $LDIF

echo -n "Testing operation of LDAP server ..... "
ldapsearch -x  -b dc=example,dc=com cn=ldapuser01 > /dev/null
if (( $? == 0 )); then
    echo "Success"
    echo "Failed"
    exit 1

exit 0

This script has dependencies on systemd and certain features of recent versions of bash and OpenLDAP.

If you examine the script, you will notice that it creates a number of plain text files with a .ldif extension. The extension is not actually required but is to remind readers that the script is creating plain text files complying with LDIF (LDAP Data Interchange Format). LDIF is a data interchange format for representing LDAP directory content and update requests as one or more content records.

Each content record is represented as a group of attributes, with records separated from one another by blank lines. The individual attributes of a record are represented as single logical lines (multiple plain text lines comprised of attribute name-value pairs. Values that contain characters outside of a portable subset of ASCII characters are base64 encoded into ASCII.

OpenLDAP includes a number of utilities including:

  • ldapsearch: Export data from a LDAP server to LDIF content records
  • ldapadd: Emport data from LDIF content records to a LDAP server
  • lpapmodify: Applying LDIF content change records to a LDAP server

You should know the basic usage of ldapsearch for the RHCSA exam so that you can quickly verify your LDAP client configuration.

You should also have noticed that the script imported two LDAP schemas, COSINE and NIS, when it was configuring the OpenLDAP server (slapd). A schema defines a set of rules that govern the kinds of information that an Directory Service such as the OpenLDAP server can contain. By the way, the s in slapd means “single” or “standalone”. COSINE stands for Cooperation for Open Systems Interconnection Networking in Europe, i.e. Eureka project 8, and is a mandatory schema in OpenLDAP.

Rather than using the Perl-based tools provided by the migrationtools package to create the LDIF content records for the selected users and groups, the script generates these content records. Most tutorials on setting up an OpenLDAP server simply use the tools from migrationtools but I happen to like more control of the content records.

The script can be run from anywhere as root and it tests the OpenLDAP server installation and configuration at various stages. If it runs to completion, you can assume with some confidence, that a working LDAP Directory Service is available.

Finally, you should be aware that an LDAP client is also known as a Directory User Agent (DUA).

Good luck with your studies.

5 comments to RHCSA Preparation – Script to Configure a Simple OpenLDAP Server

  • mp4

    hello found the script and getting a error , ./ returns a error . thanks in advance

  • turgun

    Installing openldap-clients openldap-servers ….. Done
    Modified hostname and /etc/hosts as needed.
    Start LDAP server daemon ….. Success
    ldap_bind: Invalid credentials (49)
    Testing operation of LDAP server ….. Failed
    [root@server ~]#

  • f0rb1dd3n403

    I’ve the same issue on CENTOS 7