OpenLDAP is an open source implementation of the Lightweight Directory Access Protocol. Its server daemon, slapd, stores users and groups in one directory so every Linux server in your fleet can authenticate against it instead of keeping separate /etc/passwd files. In this tutorial you will install OpenLDAP on Ubuntu 24.04, create a basic directory tree with a user and a group, secure it with TLS, and configure a second Ubuntu machine to accept SSH logins for LDAP users through SSSD.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS for the LDAP server (for example a CubePath VPS), with at least 1 GB of RAM.
- A second Ubuntu 24.04 server that will act as the LDAP client.
- A non-root user with
sudoprivileges on both machines. - A domain name with a DNS A record, for example
ldap.example.com, pointing to the LDAP server's public IP. Let's Encrypt needs it to issue the TLS certificate. - Port 80/tcp reachable from the internet on the LDAP server during certificate issuance.
Throughout the guide, replace example.com and dc=example,dc=com with your own domain, and ldap.example.com with your server's hostname.
Step 1 - Setting the hostname
slapd derives its default base DN from the machine's domain, and TLS clients will verify the certificate against the hostname, so set a fully qualified hostname first:
sudo hostnamectl set-hostname ldap.example.com
Confirm the change:
hostname -f
ldap.example.com
If hostname -f prints only ldap, add a line with the server's IP and FQDN to /etc/hosts, for example 203.0.113.10 ldap.example.com ldap.
Step 2 - Installing slapd and the LDAP utilities
Install the server and the command-line tools (ldapadd, ldapsearch, ldapmodify and friends):
sudo apt update
sudo apt install slapd ldap-utils
The package asks for an administrator password but uses a generic base DN. Run the configuration dialog again to set your own domain:
sudo dpkg-reconfigure slapd
Answer the prompts as follows:
- Omit OpenLDAP server configuration? No
- DNS domain name:
example.com(this becomes the base DNdc=example,dc=com) - Organization name: your organization name
- Administrator password: a strong password, used for the
cn=admin,dc=example,dc=comaccount - Do you want the database to be removed when slapd is purged? No
- Move old database? Yes
Check that the service is running:
sudo systemctl status slapd --no-pager
● slapd.service - OpenLDAP standalone server (Lightweight Directory Access Protocol)
Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 5s ago
Verify that the base DN and the admin account are in place with an authenticated search:
ldapwhoami -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W
Enter LDAP Password:
dn:cn=admin,dc=example,dc=com
Step 3 - Creating organizational units
Organizational units (OUs) keep the directory tidy. Create one for people, one for groups and one for service accounts such as the read-only account SSSD will use. Write the entries to an LDIF file:
nano ~/base.ldif
dn: ou=people,dc=example,dc=com
objectClass: organizationalUnit
ou: people
dn: ou=groups,dc=example,dc=com
objectClass: organizationalUnit
ou: groups
dn: ou=services,dc=example,dc=com
objectClass: organizationalUnit
ou: services
Load it with the admin account:
ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f ~/base.ldif
adding new entry "ou=people,dc=example,dc=com"
adding new entry "ou=groups,dc=example,dc=com"
adding new entry "ou=services,dc=example,dc=com"
Step 4 - Adding a group and a user
Linux needs numeric IDs for LDAP accounts. Pick ranges that do not overlap with local accounts; this guide uses GIDs from 5000 and UIDs from 10000.
Create a POSIX group:
nano ~/group.ldif
dn: cn=developers,ou=groups,dc=example,dc=com
objectClass: posixGroup
cn: developers
gidNumber: 5000
memberUid: jsmith
Then create the user jsmith, whose primary group is developers:
nano ~/user.ldif
dn: uid=jsmith,ou=people,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: jsmith
cn: John Smith
givenName: John
sn: Smith
mail: [email protected]
uidNumber: 10000
gidNumber: 5000
homeDirectory: /home/jsmith
loginShell: /bin/bash
Add both entries:
ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f ~/group.ldif
ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f ~/user.ldif
Set the user's password with ldappasswd. The -S flag prompts for the new password and slapd stores it hashed, so no clear text ends up in a file:
ldappasswd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -S "uid=jsmith,ou=people,dc=example,dc=com"
It asks for the new password twice, then for the admin password. Verify by binding as the user:
ldapwhoami -x -H ldap://localhost -D "uid=jsmith,ou=people,dc=example,dc=com" -W
dn:uid=jsmith,ou=people,dc=example,dc=com
Step 5 - Creating a read-only bind account
Clients should not use the admin DN. Create a dedicated service account that SSSD will use to look up users. First generate a hashed password for it:
slappasswd
New password:
Re-enter new password:
{SSHA}q0Vg1l0p0tq7Qm3xUo1nH2cXk4dL9e8a
Copy the {SSHA} hash into a new LDIF file:
nano ~/readonly.ldif
dn: cn=readonly,ou=services,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: readonly
description: Read-only bind account for SSSD clients
userPassword: {SSHA}q0Vg1l0p0tq7Qm3xUo1nH2cXk4dL9e8a
ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f ~/readonly.ldif
Confirm the account can bind and read the user entry:
ldapsearch -x -H ldap://localhost -D "cn=readonly,ou=services,dc=example,dc=com" -W -b "ou=people,dc=example,dc=com" "(uid=jsmith)" uid uidNumber
dn: uid=jsmith,ou=people,dc=example,dc=com
uid: jsmith
uidNumber: 10000
NoteUbuntu's default access rules let any client, including anonymous ones, read non-password attributes. Password hashes are never readable except by the owner and the admin. Only expose port 636 to the networks that need it, as shown in the next step.
Step 6 - Enabling LDAPS with a Let's Encrypt certificate
SSSD refuses to send passwords over an unencrypted connection, so the server needs TLS. Install Certbot and obtain a certificate with its standalone web server:
sudo apt install certbot
sudo ufw allow 80/tcp
sudo certbot certonly --standalone -d ldap.example.com
slapd runs as the openldap user and cannot read /etc/letsencrypt/live, and the Ubuntu AppArmor profile allows it to read files under /etc/ldap. Create a Certbot deploy hook that copies the certificate there on every issuance and renewal:
sudo nano /etc/letsencrypt/renewal-hooks/deploy/slapd.sh
#!/usr/bin/env bash
set -euo pipefail
domain="ldap.example.com"
src="/etc/letsencrypt/live/${domain}"
install -m 0644 -o root -g openldap "${src}/fullchain.pem" /etc/ldap/ldap_cert.pem
install -m 0640 -o root -g openldap "${src}/privkey.pem" /etc/ldap/ldap_key.pem
systemctl restart slapd
Make it executable and run it once to copy the current certificate:
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/slapd.sh
sudo /etc/letsencrypt/renewal-hooks/deploy/slapd.sh
Now point slapd to the files. The certificate and key must be set in the same modification:
nano ~/tls.ldif
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/ca-certificates.crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/ldap_cert.pem
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/ldap_key.pem
Apply it through the local ldapi socket, which authenticates you as root without a password:
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f ~/tls.ldif
modifying entry "cn=config"
By default Ubuntu's slapd only listens on ldap:/// and ldapi:///. Add ldaps:/// in /etc/default/slapd:
sudo nano /etc/default/slapd
Change the SLAPD_SERVICES line to:
SLAPD_SERVICES="ldap:/// ldaps:/// ldapi:///"
Restart the service and check that port 636 is open:
sudo systemctl restart slapd
sudo ss -tlnp | grep slapd
LISTEN 0 2048 0.0.0.0:389 0.0.0.0:* users:(("slapd",pid=2211,fd=8))
LISTEN 0 2048 0.0.0.0:636 0.0.0.0:* users:(("slapd",pid=2211,fd=10))
Test an encrypted bind using the hostname on the certificate:
ldapwhoami -x -H ldaps://ldap.example.com -D "uid=jsmith,ou=people,dc=example,dc=com" -W
dn:uid=jsmith,ou=people,dc=example,dc=com
Finally, allow LDAPS only from your client network (replace 203.0.113.0/24 with your clients' addresses or private network range) and close port 80 until the next renewal needs it:
sudo ufw allow from 203.0.113.0/24 to any port 636 proto tcp
sudo ufw delete allow 80/tcp
NoteCertbot's standalone renewal also needs port 80. Either leave it open, or add
sudo ufw allow 80/tcpandsudo ufw delete allow 80/tcpas--pre-hookand--post-hookfor the renewal.
Step 7 - Configuring a Linux client with SSSD
Run the following commands on the client machine. Install SSSD with its LDAP backend and the NSS and PAM modules:
sudo apt update
sudo apt install sssd-ldap sssd-tools libnss-sss libpam-sss
Create the SSSD configuration:
sudo nano /etc/sssd/sssd.conf
[sssd]
services = nss, pam
domains = example.com
[domain/example.com]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_user_search_base = ou=people,dc=example,dc=com
ldap_group_search_base = ou=groups,dc=example,dc=com
ldap_default_bind_dn = cn=readonly,ou=services,dc=example,dc=com
ldap_default_authtok = your_readonly_password
ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/ssl/certs/ca-certificates.crt
cache_credentials = true
Replace your_readonly_password with the password you chose in Step 5. SSSD refuses to start if the file is readable by other users, so restrict it:
sudo chmod 600 /etc/sssd/sssd.conf
Enable automatic home directory creation on first login, then restart SSSD:
sudo pam-auth-update --enable mkhomedir
sudo systemctl restart sssd
Check that the client resolves the LDAP user and group:
getent passwd jsmith
id jsmith
jsmith:*:10000:5000:John Smith:/home/jsmith:/bin/bash
uid=10000(jsmith) gid=5000(developers) groups=5000(developers)
Now log in as the LDAP user. If your SSH server only allows keys, test with su instead:
su - jsmith
Password:
Creating directory '/home/jsmith'.
jsmith@client:~$
Step 8 - Managing users and groups
Day-to-day changes are LDIF modifications. To add an existing user to another group, write a modify LDIF:
nano ~/add-member.ldif
dn: cn=developers,ou=groups,dc=example,dc=com
changetype: modify
add: memberUid
memberUid: alice
ldapmodify -x -H ldaps://ldap.example.com -D "cn=admin,dc=example,dc=com" -W -f ~/add-member.ldif
To reset a password, use ldappasswd as in Step 4. To remove a user:
ldapdelete -x -H ldaps://ldap.example.com -D "cn=admin,dc=example,dc=com" -W "uid=jsmith,ou=people,dc=example,dc=com"
Clients cache entries, so run sudo sss_cache -E on a client when you need a change to take effect immediately.
Troubleshooting
ldap_bind: Invalid credentials (49). The DN or password is wrong. Check the exact DN with sudo slapcat | grep ^dn: on the server.
ldapmodify: modify: Other (e.g., implementation specific) error (80) when applying TLS. slapd cannot read the certificate or key. Check that /etc/ldap/ldap_key.pem is group-owned by openldap with mode 0640, and look for AppArmor denials with sudo journalctl -k | grep apparmor.
getent passwd jsmith returns nothing on the client. Read the SSSD log with sudo journalctl -u sssd and run sudo sssctl domain-status example.com. Most failures are a wrong bind password, a search base typo, or port 636 blocked by the firewall. Test the path directly with ldapsearch -x -H ldaps://ldap.example.com -D "cn=readonly,ou=services,dc=example,dc=com" -W -b "dc=example,dc=com" "(uid=jsmith)".
TLS errors from clients. Confirm the certificate chain and hostname with openssl s_client -connect ldap.example.com:636 -servername ldap.example.com. Clients must connect using the name on the certificate, not the IP address.
Conclusion
You now have an OpenLDAP server on Ubuntu 24.04 with a structured directory, encrypted LDAPS access, a least-privilege bind account, and an SSSD client that accepts logins for directory users. As next steps, restrict the default read ACL so only authenticated clients can browse the directory, add a second server with syncrepl replication for redundancy, and schedule regular slapcat exports as backups.
