This is a multi-part article where I will cover different areas of configuration of OpenLDAP server in CentOS 7 Linux node. You can use below links to refer different parts of this tutorial
Basics LDAP Tutorial for Beginners – Understanding Terminologies & Usage
Step-by-Step Tutorial: Install and Configure OpenLDAP
Step-by-Step Tutorial: Configure OpenLDAP with TLS certificates CentOS 7 Linux
Step-by-Step Tutorial: Configure LDAP client to authenticate with LDAP server
Install and Configure OpenLDAP
In my last article I gave you an overview on OpenLDAP and it's terminologies. Before starting with this article to install and configure openldap in Linux you must be aware of basic terminologies. For the demonstration of this article I am using CentOS 7. In this article I will share detailed steps to install and configure OpenLDAP on Linux platform using ldapmodify
. In legacy releases of openldap, the configuration was performed using slapd.conf
but now the configuration is kept in cn=config
database. So we will install and configure OpenLDAP using cn=config
and ldapmodify
.
Installing OpenLDAP
To get the OpenLDAP server and client components up and running, these packages are required on Fedora, RHEL, and CentOS systems:
- openldap-2*.rpm - Provides the configuration files and libraries for OpenLDAP.
- openldap-clients*.rpm - Provides the client programs needed for accessing and modifying OpenLDAP directories.
- openldap-servers*.rpm - Provides the servers (
slapd
,slurpd
) and other utilities necessary to configure and run LDAP.
openldap-servers*.rpm
package.[root@ldap-server ~]# yum -y install openldap-clients openldap-servers
We make sure that the slapd
service is configured to boot automatically, and we start the service.
[root@ldap-server ~ ]# systemctl start slapd [root@ldap-server ~ ]# systemctl enable slapd ln -s '/usr/lib/systemd/system/slapd.service' '/etc/systemd/system/multi-user.target.wants/slapd.service'
Check the status of the service
[root@ldap-server ~]# systemctl status slapd
● slapd.service - OpenLDAP Server Daemon
Loaded: loaded (/usr/lib/systemd/system/slapd.service; disabled; vendor preset: disabled)
Active: active (running) since Sat 2019-02-09 21:46:23 IST; 17min ago
Docs: man:slapd
man:slapd-config
man:slapd-hdb
man:slapd-mdb
file:///usr/share/doc/openldap-servers/guide.html
Process: 17350 ExecStart=/usr/sbin/slapd -u ldap -h ${SLAPD_URLS} $SLAPD_OPTIONS (code=exited, status=0/SUCCESS)
Process: 17336 ExecStartPre=/usr/libexec/openldap/check-config.sh (code=exited, status=0/SUCCESS)
Main PID: 17352 (slapd)
Tasks: 3
Memory: 13.5M
CGroup: /system.slice/slapd.service
└─17352 /usr/sbin/slapd -u ldap -h ldapi:/// ldap:///
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=0 RESULT tag=97 err=0 text=
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=1 MOD dn="olcDatabase={0}config,cn=config"
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=1 MOD attr=olcRootDN
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: ldif_read_file: checksum error on "/etc/openldap/slapd.d/cn=config/olcDatabase={0}config.ldif"
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=1 RESULT tag=103 err=0 text=
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=2 MOD dn="olcDatabase={0}config,cn=config"
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=2 MOD attr=olcRootPW
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=2 RESULT tag=103 err=0 text=
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 op=3 UNBIND
Feb 09 22:01:32 ldap-server.example.com slapd[17352]: conn=1007 fd=11 closed
Customizing the Installation
Once installed, we have to generate a password for the admin
user. In this example, we use a simple password: “redhat
”
[root@ldap-server cn=config]# slappasswd New password: Re-enter new password: {SSHA}6zHtA20qkTmdLrJSfxo+VV3QLGS7m0CZ
/etc/openldap/slapd.d/slapd.conf
in which the configuration of the OpenLDAP server was kept. But now the configuration is kept in the LDAP database itself.[root@ldap-server ~]# ls /etc/openldap/slapd.d/ cn=config cn=config.ldif
Modifying Objects
Replace olcSuffix and olcRootDN attribute
OpenLDAP actually stores its information in storage back ends. We could think of these back ends as the databases used by OpenLDAP. One of the most used back ends has always been the Berkeley DB back ends
, such as bdb
, or the more recent hdb
. The information stored in the hdb back end can be found in the /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif
file.
In an LDIF file, we first identify the element we want to add, change, etc. To uniquely identify an element, we use the dn
(distinguished name) attribute, which was created precisely for that reason. So, the first line of our LDIF file could be something like this:
dn: olcDatabase={2}hdb,cn=config
Next, we specify if we want to add an attribute, modify it, etc.
changeType: modify
If we want to modify an entry, we also must clarify whether we’ll be replacing an attribute, deleting it, etc.
replace: olcSuffix
And, finally, we type the new value of the modified attribute.
olcSuffix: dc=example,dc=com
You’ll see many LDIF examples throughout the article, but for now, let’s get back to the /etc/openldap/s-lapd.d/cn=config/olcDatabase={2}hdb.ldif
file. We have to modify (at least) these two entries:
olcSuffix: dc=my-domain,dc=com olcRootDN: cn=Manager,dc=my-domain,dc=com
To make all these changes with ldapmodify
, we have to prepare an LDIF file such as this:
[root@ldap-server ~]# cat my_config.ldif 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=admin,dc=example,dc=com
The first line identifies the main entry in the LDAP that we are going to change. Just a moment ago, we saw the parameter olcSuffix
inside the /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif file
. In this file, the dn attribute is dn: olcDatabase={2}hdb
, and as the file is inside the config folder, the full dn
attribute is dn: olcDatabase={2}hdb,cn=config
.
Another, and maybe better, way to identify the data we require to create the LDIF file could be to use the ldapsearchcommand
.
[root@ldap-server ~]# ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config olcDatabase=\* SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 # extended LDIF # # LDAPv3 # base <cn=config> with scope subtree # filter: olcDatabase=* # requesting: ALL # # {-1}frontend, config dn: olcDatabase={-1}frontend,cn=config objectClass: olcDatabaseConfig objectClass: olcFrontendConfig olcDatabase: {-1}frontend # {0}config, config dn: olcDatabase={0}config,cn=config objectClass: olcDatabaseConfig olcDatabase: {0}config olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external ,cn=auth" manage by * none # {1}monitor, config dn: olcDatabase={1}monitor,cn=config objectClass: olcDatabaseConfig olcDatabase: {1}monitor olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external ,cn=auth" read by dn.base="cn=Manager,dc=my-domain,dc=com" read by * none # {2}hdb, config dn: olcDatabase={2}hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {2}hdb olcDbDirectory: /var/lib/ldap olcSuffix: dc=my-domain,dc=com olcRootDN: cn=Manager,dc=my-domain,dc=com olcDbIndex: objectClass eq,pres olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub # search result search: 2 result: 0 Success # numResponses: 5 # numEntries: 4
We save the LDIF file with an appropriate name, for example, my_config.ldif
, and we execute ldapmodify
.
[root@ldap-server ~]# ldapmodify -Y EXTERNAL -H ldapi:/// -f my_config.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={2}hdb,cn=config" modifying entry "olcDatabase={2}hdb,cn=config"
Add olcRootPW attribute
To add a new attribute we use "add
" and then the attribute name as shown in the below example.
Here we create another LDIF file (my_config2.ldif)
to add the olcRootPW
attribute.
[root@ldap-server ~]# cat my_config2.ldif dn: olcDatabase={2}hdb,cn=config changeType: modify add: olcRootPW olcRootPW: {SSHA}6zHtA20qkTmdLrJSfxo+VV3QLGS7m0CZ
And we execute ldapmodify again.
[root@ldap-server ~]# ldapmodify -Y EXTERNAL -H ldapi:/// -f my_config2.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={2}hdb,cn=config"
Replace olcAccess attribute
We also have to allow access to the LDAP database to the admin user we just specified before (cn=admin,dc=example,dc=com)
. If we take a look at the olcDatabase={1}monitor.ldif
, file we’ll see the following line:
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external, cn=auth" read by dn.base="cn=manager,dc=my-domain,dc=com" read by * none
We’ll have to edit the file or use ldapmodify to change the entry. If we use ldapmodify, the LDIF file should be something like this:
[root@ldap-server ~]# cat my_config3.ldif 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=example,dc=com" read by * none
Once again, we execute ldapmodify
by passing the new LDIF file as a parameter.
[root@ldap-server ~]# ldapmodify -Y EXTERNAL -H ldapi:/// -f my_config3.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={1}monitor,cn=config"
Validate the new attribute values
Now we can check with ldapsearch
whether the value for the attribute was actually changed.
[root@ldap-server ~]# ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config olcDatabase=\* SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 # extended LDIF # # LDAPv3 # base <cn=config> with scope subtree # filter: olcDatabase=* # requesting: ALL # # {-1}frontend, config dn: olcDatabase={-1}frontend,cn=config objectClass: olcDatabaseConfig objectClass: olcFrontendConfig olcDatabase: {-1}frontend # {0}config, config dn: olcDatabase={0}config,cn=config objectClass: olcDatabaseConfig olcDatabase: {0}config olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external ,cn=auth" manage by * none # {1}monitor, config dn: olcDatabase={1}monitor,cn=config objectClass: olcDatabaseConfig olcDatabase: {1}monitor olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external , cn=auth" read by dn.base="cn=admin,dc=example,dc=com" read by * none # {2}hdb, config dn: olcDatabase={2}hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {2}hdb olcDbDirectory: /var/lib/ldap olcDbIndex: objectClass eq,pres olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub olcRootPW: {SSHA}6zHtA20qkTmdLrJSfxo+VV3QLGS7m0CZ olcSuffix: dc=example,dc=com olcRootDN: cn=admin,dc=example,dc=com # search result search: 2 result: 0 Success # numResponses: 5 # numEntries: 4
As we can see, the value was changed according to what we specified in the LDIF file.
Another tool we can use to check the configuration is the slaptest
command.
[root@ldap-server ~]# slaptest -u config file testing succeeded
Adding Objects
Now we have to manually create an entry for dc=example,dc=com
in our LDAP server. The easiest way to do this is to create an LDIF file for this entry and pass it to the ldapadd
command.
So, we create a file named example.ldif
, with the following content:
[root@ldap-server ~]# cat example.ldif dn: dc=example,dc=com objectClass: dcObject objectClass: organization dc: example o: example
We specify a series of attributes, such as distinguished name (dn), domain component (dc), and organization (o). We also define the new entry as an object of the type dcObject and organization.
Now we execute ldapadd and pass it the example.ldif file as a parameter. We specify with (-f) the name of the file, the admin user (-D), and the password we defined for that admin user (-w).
[root@ldap-server ~]# ldapadd -f example.ldif -D cn=admin,dc=example,dc=com -w redhat adding new entry "dc=example,dc=com"
We can check whether the entry was created successfully by using the ldapsearch command.
[root@ldap-server ~]# ldapsearch -x -b dc=example,dc=com # extended LDIF # # LDAPv3 # base <dc=example,dc=com> with scope subtree # filter: (objectclass=*) # requesting: ALL # # example.com dn: dc=example,dc=com objectClass: dcObject objectClass: organization dc: example o: example # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1
You just saw how to add the object dc=example,dc=com
to our LDAP. Now you’ll see how to add organizational units , groups, and users.
Adding an Organizational Unit
Maybe we’d like to have an organizational unit (OU) called users
in which to store all LDAP users. To do so, we’ll create a new LDIF file named users.ldif
, with the following content:
[root@ldap-server ~]# cat users.ldif
dn: ou=users,dc=example,dc=com
objectClass: organizationalUnit
ou: users
We execute ldapadd
again to create the OU.
[root@ldap-server ~]# ldapadd -f users.ldif -D cn=admin,dc=example,dc=com -w redhat adding new entry "ou=users,dc=example,dc=com"
Adding a User
We can now include a user inside the organizational unit. The procedure is quite similar to what we have seen so far. First, we create a file named archimedes.ldif
, with the following content:
[root@ldap-server ~]# cat archimedes.ldif dn: cn=Archimedes of Syracuse,ou=users,dc=example,dc=com cn: Archimedes sn: Syracuse objectClass: inetOrgPerson userPassword: eureka uid: archimedes
Then we execute ldapadd
again.
[root@ldap-server ~]# ldapadd -f archimedes.ldif -x -D cn=admin,dc=example,dc=com -w redhat adding new entry "cn=Archimedes of Syracuse,ou=users,dc=example,dc=com" ldap_add: Invalid syntax (21) additional info: objectClass: value #0 invalid per syntax
What this message means is that the object inetOrgPerson
isn’t loaded in the core schema, so we’ll have to include it. In the /etc/openldap/schema
folders, there are many LDIF files to extend the schema when we need it. We can see there is an inetorgperson.ldif
file, which contains the schema definition for the inetOrgPerson
object.
The schema itself is contained in the LDAP database, so we can add new definitions to it with the ldapadd
command. As we’re going to modify the configuration itself, instead of the data, we’ll authenticate ourselves as the external root user (-Y EXTERNAL
).
[root@ldap-server ~]# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/inetorgperson.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "cn=inetorgperson,cn=schema,cn=config" ldap_add: Other (e.g., implementation specific) error (80) additional info: olcObjectClasses: AttributeType not found: "audio"
As we can see, we get an error, because the attribute type audio isn’t defined. So, we have to include this definition in the schema too .
If we perform a search of the string audio in the files located in the /etc/openldap/schema/
folder, we’ll see that the attribute audio is defined in the cosine.ldif
file. So, we extend the schema with this LDIF file first.
[root@ldap-server ~]# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/cosine.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=cosine,cn=schema,cn=config"
Now we do the same thing with the inetorgperson.ldif
file.
[root@ldap-server ~]# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/inetorgperson.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=inetorgperson,cn=schema,cn=config"
Next we also add nis.ldif
file
[root@ldap-server ~]# # ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/nis.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=nis,cn=schema,cn=config"
Now we can add the user with the archimedes.ldif
file we created before.
[root@ldap-server ~]# ldapadd -f archimedes.ldif -x -D cn=admin,dc=example,dc=com -w redhat adding new entry "cn=Archimedes of Syracuse,ou=users,dc=example,dc=com"
If at some point we have to take a look at the currently used schema, we can use the slapcat
command like this:
[root@ldap-server ~]# slapcat -b "cn=schema,cn=config"
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/openldap/slapd.args
olcPidFile: /var/run/openldap/slapd.pid
olcTLSCACertificatePath: /etc/openldap/certs
olcTLSCertificateFile: "OpenLDAP Server"
olcTLSCertificateKeyFile: /etc/openldap/certs/password
structuralObjectClass: olcGlobal
entryUUID: 35694bfc-c0d9-1038-87ee-811b58b4e51a
creatorsName: cn=config
createTimestamp: 20190209170930Z
entryCSN: 20190209170930.732544Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20190209170930Z
--
--
<OUTPUT TRUNCATED>
--
--
dn: olcDatabase={2}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {2}hdb
olcDbDirectory: /var/lib/ldap
olcDbIndex: objectClass eq,pres
olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
structuralObjectClass: olcHdbConfig
entryUUID: 3569c776-c0d9-1038-87f4-811b58b4e51a
creatorsName: cn=config
createTimestamp: 20190209170930Z
olcRootPW:: e1NTSEF9NnpIdEEyMHFrVG1kTHJKU2Z4bytWVjNRTEdTN20wQ1o=
olcSuffix: dc=example,dc=com
olcRootDN: cn=admin,dc=example,dc=com
entryCSN: 20190209171424.210710Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20190209171424Z
Adding a Group
To add a group, we repeat the same process . First we create the group.ldif
file with the following content:
[root@ldap-server ~]# cat group.ldif dn: cn=scientists,ou=users,dc=example,dc=com cn: scientists objectClass: groupOfNames member: cn=Archimedes of Syracuse,ou=users,dc=example,dc=com
And we add the group with ldapadd
.
[root@ldap-server ~]# ldapadd -f group.ldif -x -D cn=admin,dc=example,dc=com -w redhat adding new entry "cn=scientists,ou=users,dc=example,dc=com"
Deleting Objects
Apart from adding or editing, we can also delete objects from the LDAP server. The procedure is even easier, as we don’t have to create any LDIF file. We just execute ldapdel
with the cn
we want to delete.
[root@ldap-server ~]# ldapdelete "cn=Archimedes of Syracuse,ou=users,dc=example,dc=com" -D cn=admin,dc=example,dc=com -w redhat
We can check that the entry was actually suppressed.
[root@ldap-server ~]# ldapsearch -x -b "dc=example,dc=com" "(cn=Archimedes)" # extended LDIF # # LDAPv3 # base <dc=example,dc=com> with scope subtree # filter: (cn=Archimedes) # requesting: ALL # # search result search: 2 result: 0 Success # numResponses: 1
Conclusion
In this tutorial I have shared step by step instructions to install and configure openldap from scratch on a CentOS 7 Linux node. I have tried to be descriptive while explaining every step throughout the tutorial, although I would recommend for freshers to first learn more about the openldap terminologies before jumping into the configuration.
Lastly I hope the steps from the article to install and configure OpenLDAP on Linux was helpful. So, let me know your suggestions and feedback using the comment section.
What's Next
Now since our ldap server is configured, next we will
- Create TLS certificates to enable secure communication between ldap client and server.
- Configure LDAP Client using SSSD
References
I have used below external references for this tutorial guide
Learn CentOS Linux Network Services
Superb tutorial. The best I have seen for centos.
Thanks for the well-written tutorials. Just a tiny thing. I think in the first paragraph the sentence “but not the configuration is kept in cn=config database.” contains a mistake. I’m pretty sure it should say “but NOW the configuration…” as it is opposed to what was done in legacy ldap implementations.
Regards
Thank you for highlighting this, I have corrected the text.
Thank you for your effort , just a tip
openldap-clients-2.4.44-21.el7_6.x86_64
In my installation
CentOS Linux release 7.8.2003 (Core)
openldap-servers-2.4.44-21.el7_6.x86_64
sssd-ldap-1.16.4-37.el7_8.3.x86_64
openldap-2.4.44-21.el7_6.x86_64
When I am trying to add/edit/delete binding with the user “
cn=admin,dc=example,dc=com
” without the-H
option I getldap_bind: Invalid credentials (49)
but with -H ldapapi:///
adding new entry “cn=scientists,ou=users,dc=example,dc=com”
(I hope that it helps)
Thank you for sharing!
Hello,
Firstly: Thank you so much for putting this out !
Secondly: Could it be possible to install OpenLDAP other than in /etc ?
Regards,
you mean to use a config file at a different location?
I have set up LDAP before by following other articles. All them gave the same instructions as you have. However, you explain each step as to why it is done and why those particular values were chosen. Thank you for a well written tutorial.
Your comment made my day. Thank You!
Very Well written article. I followed it and did not have any trouble executing any steps.
Hello,sorry but do you know how to add a user into the group?
I belive you have to look up for the term “GroupofNames”
Thank you for your article. I followed it and everything seems to work after I installed it on my Redhat 7 virtual machine.
What I’m trying to do right now is to connect to this server from my windows client, however, I’m unable to do it so far.
Could you please tell me if the following field values are correct based on your example?
LDAP URI: ldap://example.com
user : CN=Archimedes of Syracuse,OU=scientists,DC=example,DC=com
user root: OU=users,DC=example,DC=com
Hello, I’ve follow your step to modify {2}hdb file, however, when I tried to replace olcSuffix and olcRootDN by
command you provided, it prompts “ldap_modify: No such object (32)
matched DN: cn=config
could you please provide some help? this has been trouble me for a day in my two machine.
Thank you very much!
I am afraid I also have no clue here, you may have to troubleshoot this by checking more symptoms locally.
I followed the instructions and it worked very well for me! Thank you for taking your time to do this tutorial!