Setting up an OpenLDAP Server with SSL + NFS for User Home Directories on CentOS 7

Setting up an OpenLDAP server on CentOS 7 and deploying NFS to export users’ home directories.

Software

Software used in this article:

  1. CentOS 7
  2. OpenLDAP 2.4.40
  3. nfs-utils 1.3.0

OpenLDAP Setup

Our OpenLDAP server resides on a 10.8.8.0/24 local area network.

Installation and SSL Configuration

# yum install -y openldap-servers openldap-clients

Generate an RSA private key with a certificate sign request.

# cd /etc/pki/tls/private
# DOMAIN=ldap
# openssl genrsa -out "$DOMAIN".key 2048 && chmod 0600 "$DOMAIN".key
# openssl req -new -sha256 -key "$DOMAIN".key -out "$DOMAIN".csr

Now we can get some CA to sign our CSR, or we can sign it ourselves:

# openssl x509 -req -days 1825 -sha256 -in "$DOMAIN".csr -signkey "$DOMAIN".key \
  -out "$DOMAIN".crt

Convert PKCS#1 to PKCS#8:

# openssl pkcs8 -topk8 -inform pem -in "$DOMAIN".key \
  -outform pem -nocrypt -out "$DOMAIN".pem

Ensure the private keys can be read by the ldap group:

# chmod 640 "$DOMAIN".crt "$DOMAIN".key "$DOMAIN".pem
# chgrp ldap "$DOMAIN".crt "$DOMAIN".key "$DOMAIN".pem
# mv "$DOMAIN".crt ../certs/

Open /etc/sysconfig/slapd for editing and put the following to enable LDAPS:

SLAPD_URLS="ldapi:/// ldap://0.0.0.0:389/ ldaps://0.0.0.0:636/"

Enable and start the service:

# systemctl enable slapd.service
# systemctl start slapd.service

Configure firewall:

# iptables -A INPUT -s 10.8.8.0/24 -p tcp -m multiport --dport 389,636 -j ACCEPT

Import Basic OpenLDAP Schemas and Create a DB

Quite a few LDAP schemas are available, import as required.

# ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif
# ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif
# ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif

Although only root can run slapadd, the slapd service runs as the ldap user. Because of this, the directory server is unable to modify any files created by slapadd.

# cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
# chmod 0600 /var/lib/ldap/DB_CONFIG
# chown -R ldap:ldap /var/lib/ldap/
# slaptest

Configure OpenLDAP

We’ll need a directory to store temp config files:

# mkdir /root/ldifconfigs/ && cd /root/ldifconfigs/

Generate a userPassword value for the LDAP admin account:

# slappasswd -h {SSHA}
New password:
Re-enter new password:
{SSHA}qFEhyuvWKiLGtOISbhbRlSijlMQGCAGh

Our initial LDAP configuration file initial_config.ldif can be seen below .

We enable all logging, set password hash to SSHA (rather than SHA), provide paths to SSL certificates and keys, put a high cipher suite to be used.

The RootDN entry is the Distinguished Name (DN) for a user who is unrestricted by access controls or administrative limit parameters set for operations on the LDAP directory. We use cn=admin,dc=top for it. Note that our suffix is set to dc=top as we intend to use multiple domains under the tree (top domain component).

We also define a guest account cn=autobind,dc=top for read-only access. This is mainly to bind to the LDAP server when anonymous binding is not allowed, for services like nslcd.

Then there is an access control list which determines who can authenticate against the OpenLDAP server:

  1. The admin account (rootDN) has complete access,
  2. The autobind account can read-only,
  3. Anonymous users are provided access to the userPassword attribute for the initial connection to occur,
  4. All users have read access to their passwords due to “by self write” permissions.

Create the file.

# cat > ./initial_config.ldif << EOF
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: -1
-
replace: olcPasswordHash
olcPasswordHash: {SSHA}
-
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/pki/tls/certs/startssl.ca.crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/pki/tls/certs/ldap.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/pki/tls/private/ldap.pem
-
replace: olcTLSCipherSuite
olcTLSCipherSuite: HIGH
-
replace: olcTLSVerifyClient
olcTLSVerifyClient: never

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=admin,dc=top" read by * none

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=top
-
replace: olcRootDN
olcRootDN: cn=admin,dc=top
-
replace: olcRootPW
olcRootPW: {SSHA}qFEhyuvWKiLGtOISbhbRlSijlMQGCAGh
-
replace: olcLastMod
olcLastMod: TRUE
-
replace: olcDbCheckpoint
olcDbCheckpoint: 512 30
-
replace: olcAccess
olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=admin,dc=top" write by dn="cn=autobind,dc=top" read by self write by anonymous auth by * none
olcAccess: {1}to dn.base="" by * read
olcAccess: {2}to * by dn="cn=admin,dc=top" write by dn="cn=autobind,dc=top" read by self write by users read by anonymous auth by * none
EOF

Apply configuration form the file:

# ldapadd -Y EXTERNAL -H ldapi:/// -f ./initial_config.ldif

Create a base configuration file. This defines the top domain component dc=top as well as the admin and the autobind users.

# cat > ./base.ldif << EOF
dn: dc=top
objectClass: top
objectClass: dcObject
objectclass: organization
o: top
dc: top

dn: cn=admin,dc=top
objectClass: simpleSecurityObject
objectclass: organizationalRole
description: LDAP Admin Access
userPassword: {SSHA}qFEhyuvWKiLGtOISbhbRlSijlMQGC315

dn: cn=autobind,dc=top
objectClass: simpleSecurityObject
objectclass: organizationalRole
description: LDAP Read-only Access
userPassword:
EOF

Apply changes, and then create a password for the autobind account (we left the userPassword field blank intentionally).

# ldapadd -x -D cn=admin,dc=top -W -f ./base.ldif
# ldappasswd -x -D cn=admin,dc=top -W -S cn=autobind,dc=top

We are now going to create a domain called lisenet.com with a couple of organisational units for users and groups.

# cat > ./lisenet.com.ldif << EOF 
dn: dc=lisenet.com,dc=top
o: lisenet.com
dc: lisenet.com
objectClass: dcObject
objectClass: organization

dn: ou=Users,dc=lisenet.com,dc=top
objectClass: organizationalUnit
ou: Users

dn: ou=Groups,dc=lisenet.com,dc=top
objectClass: organizationalUnit
ou: Groups
EOF

Apply changes:

# ldapadd -x -D cn=admin,dc=top -W -f ./lisenet.com.ldif

As we have a domain created, we can add some users. For those who already have multiple local user accounts created on a system, it would be wise to use the MigrationTools utility.

The MigrationTools are a set of Perl scripts for migrating users, groups, aliases, hosts, netgroups, networks and services from existing nameservices (flat files, NIS, and NetInfo) to LDAP.

We’re going to create two LDAP users, alice and vincent, together with their respective groups. Home directories (/home/guests/) will be exported via NFS. Note the shadowLastChange value.

We need objectClass: person to assign surnames, objectClass: inetOrgPerson for mail and given name, plus objectClass: posixAccount to set up user’s password.

# cat > ./users.ldif << EOF 
dn: uid=alice,ou=Users,dc=lisenet.com,dc=top
uid: alice
uidNumber: 5001
gidNumber: 5001
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword: {SSHA}biYSGrLyQG/+ntABuIYF2rXyzDgZdBaU
cn: Alice Abernathy
gn: Alice
sn: Abernathy
mail: [email protected]
shadowLastChange: 16890
shadowMin: 0
shadowMax: 99999
shadowWarning: 14
shadowInactive: 3
loginShell: /bin/bash
homeDirectory: /home/guests/alice

dn: cn=alice,ou=Groups,dc=lisenet.com,dc=top
gidNumber: 5001
objectClass: top
objectClass: posixGroup
cn: alice

dn: uid=vincent,ou=Users,dc=lisenet.com,dc=top
uid: vincent
uidNumber: 5002
gidNumber: 5002
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword: {SSHA}biYSGrLyQG/+ntABuIYF2rXyzDgZdBaU
cn: Vincent Valentine
gn: Vincent
sn: Valentine
mail: [email protected]
shadowLastChange: 16890
shadowMin: 0
shadowMax: 99999
shadowWarning: 14
shadowInactive: 3
loginShell: /bin/bash
homeDirectory: /home/guests/vincent

dn: cn=vincent,ou=Groups,dc=lisenet.com,dc=top
gidNumber: 5002
objectClass: top
objectClass: posixGroup
cn: vincent
EOF

Apply:

# ldapadd -x -D cn=admin,dc=top -W -f ./users.ldif

In case we want to change the pre-defined passwords, we can do so by issuing the following:

# ldappasswd -x -D cn=admin,dc=top -W -S uid=alice,ou=users,dc=lisenet.com,dc=top
# ldappasswd -x -D cn=admin,dc=top -W -S uid=vincent,ou=users,dc=lisenet.com,dc=top

Here’s how out LDAP structure should look like:

More domains and users can be easily added under the top domain component.

NFS Setup

Package Installation and Firewall

Install nfs utilities, enable and start services:

# yum install nfs-utils
# systemctl enable rpcbind && systemctl start rpcbind
# systemctl enable nfs-server && systemctl start nfs-server

Configure firewall for NFS (rpc-bind, nfs and mountd):

# iptables -A INPUT -s 10.8.8.0/24 -p tcp -m multiport --dport 111,2049,20048 -j ACCEPT
# iptables -A INPUT -s 10.8.8.0/24 -p udp -m multiport --dport 111,2049,20048 -j ACCEPT

Create Home Directories and Configure Exports

Create home directories for LDAP users alice and vincent. Note the user id and the group id numbers which we used earlier when initially creating users on LDAP.

# mkdir -m0750 -p /home/guests/{alice,vincent}
# chown 5001:5001 /home/guests/alice/
# chown 5002:5002 /home/guests/vincent/

Configure NFS exports:

# cat /etc/exports
/home/guests 10.8.8.0/24(rw,sync,no_subtree_check,root_squash)
# exportfs -rav
exporting 10.8.8.0/24:/home/guests

Check the “related posts” section for how to configure CentOS 7 to use LDAP authentication with NFS automount.

Automount configuration should look something like this (where 10.8.8.2 is the NFS server’s IP):

# cat /etc/auto.guests
*  -rw   10.8.8.2:/home/guests/&

Related Posts

Setup LDAP Authentication on CentOS 7

Configure NFS Automount on CentOS 7

15 thoughts on “Setting up an OpenLDAP Server with SSL + NFS for User Home Directories on CentOS 7

  1. You made a pretty detailed guide!, nice work.

    Unfortunately, I’m trying this in Centos 7 and I’m getting into errors with command ldapadd -Y EXTERNAL -H ldapi:/// -f ./initial_config.ldif

    It shows this error:

    SASL/EXTERNAL authentication started
    SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    SASL SSF: 0
    modifying entry “cn=config”

    modifying entry “olcDatabase={1}monitor,cn=config”
    ldap_modify: Other (e.g., implementation specific) error (80)
    additional info: handler exited with 1

    Has anyone else had this situation?, cheers.

    • Thanks for your feedback Javier. The command works for me on CentOS 7.3 with openldap-2.4.40-13:

      # ldapadd -Y EXTERNAL -H ldapi:/// -f ./initial_config.ldif
      SASL/EXTERNAL authentication started
      SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
      SASL SSF: 0
      modifying entry "cn=config"
      
      modifying entry "olcDatabase={1}monitor,cn=config"
      
      modifying entry "olcDatabase={2}hdb,cn=config"

      Please make sure that you create the file initial_config.ldif as per instructions provided.

    • I found the error!,

      By mistake I didnt import the schemas properly, after this all worked well:

      # ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif
      # ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif
      # ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif

      Thanks!!

    • I’m now having a hard time getting Samba on a different server (Centos 7 too) to use this LDAP to authenticate the samba users.

      Do you have any howto for this?

    • Nothing published on this blog I’m afraid. It’s a tricky setup because Samba relies on the host OS and therefore needs uid/gid for a user, which makes it challenging to integrate with LDAP. Good luck!

  2. Hi,

    Thanks for the detailed instructions.. Very helpful..
    where does the startssl.ca.crt come from ?
    Thanks..

    • From StartCom CA (it stopped issuing digital certificates as of January 1, 2018). I’ve switched to Let’s Encrypt.

    • You’re welcome! There isn’t anything specific you need to change to get LE working. In reality, you can use any CA that you want.

  3. Hi Tomas,

    Just wondering where / how do you get startssl.ca.crt in initial_config.ldif? And does olcPasswordHash has to be the same as olcRootPW?

    I am running Cento 7.7 (openldap 2.4.44-21) and had to split each section in initial_config.ldif into separate files as it did was erroring out when it was all in one file.

    • Hey Marcin, StartSSL was a CA based in China that used provided free SSL certificates. There was no Let’s Encrypt back then.

      If you need a free certificate for your LDAP server, use Let’s Encrypt.

      I’m not sure if they have to be the same, but my understanding is that you should use the hashing algorithm that you define in olcPasswordHash. If you configure SSHA, then your olcRootPW password should be in a SSHA form.

Leave a Reply

Your email address will not be published. Required fields are marked *