--- myst: html_meta: description: Configure an Ubuntu system as an LDAP client using nslcd and NSS to make directory users and groups available for local authentication. --- (ldap-client)= # Set up an LDAP client This guide explains how to configure an Ubuntu machine as a [Lightweight Directory Access Protocol (LDAP)](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) client, so that users and groups stored in an LDAP directory become available on the system for authentication. This enables users to log in via SSH or other services backed by [Pluggable Authentication Modules (PAM)](https://en.wikipedia.org/wiki/Pluggable_Authentication_Module) using their LDAP credentials. The approach used here is `nslcd` with `libnss-ldapd` and `libpam-ldapd`. The Name Service Switch ({term}`NSS`) and PAM modules communicate with the `nslcd` daemon, which maintains a single shared LDAP connection rather than each process opening its own. This keeps the setup lightweight and easy to debug. ```{note} Ubuntu also supports [SSSD](https://sssd.io/) as an alternative LDAP client. SSSD provides credential caching and offline authentication. If you need those features, see {ref}`sssd-with-ldap` instead. ``` ## Prerequisites - A reachable LDAP server. To set one up, see {ref}`install-openldap`. - The server's base Distinguished Name ({term}`DN`) and URI. - The system CA certificates must trust the LDAP server's TLS certificate. See {ref}`ldap-and-tls` for guidance on TLS setup. - And of course {ref}`users/groups in LDAP `. ## Install the LDAP client daemon Install `nslcd` and its PAM integration library: ```bash sudo apt install nslcd libpam-ldapd libnss-ldapd ``` The installer prompts for your LDAP server URI and base DN. For example: - **LDAP server URI:** `ldaps://ldap.example.com` - **Base DN:** `dc=example,dc=com` Installing `libnss-ldapd` updates `/etc/nsswitch.conf` to include `ldap` as a source for `passwd`, `group`, and `shadow` lookups. ## Configure nslcd Review and adjust `/etc/nslcd.conf`. A minimal configuration looks like this: ```text # /etc/nslcd.conf uid nslcd gid nslcd uri ldaps://ldap.example.com base dc=example,dc=com tls_reqcert demand tls_cacertfile /etc/ssl/certs/ca-certificates.crt ``` Adjust `uri` and `base` to match your directory. The `tls_reqcert demand` setting ensures the server certificate is verified. See {manpage}`nslcd.conf(5)` for all available options. ## Set up PAM The installer updates `/etc/pam.d/common-*` automatically. To confirm that LDAP authentication and automatic home directory creation are enabled, run: ```bash sudo pam-auth-update ``` Select **LDAP Authentication** and **Create home directory on login** in the interactive menu. Restart `nslcd` to apply the configuration: ```bash sudo systemctl restart nslcd ``` ## Test the configuration Verify that a known LDAP user is visible to the system: ```bash id ``` List all users known to {term}`NSS`, including those from LDAP: ```bash getent passwd ``` List all groups: ```bash getent group ``` If the output includes users and groups from the LDAP directory, {term}`NSS` integration is working correctly. If this doesn't work, you can inspect live LDAP queries. Stop the `nslcd` service and run it in the foreground in "debug" mode: ```bash sudo systemctl stop nslcd.service sudo nslcd -n -d ``` Press {kbd}`Ctrl+C` to stop this debug session. You can iterate with configuration or setup adjustments until the log output and `getent` results are satisfying. Finally, restart the service via {ref}`systemctl`: ```bash sudo systemctl start nslcd.service ``` ## Configure home directories By default, `nslcd` uses the `homeDirectory` attribute from the LDAP directory. You can remap this — or any other {manpage}`passwd(5)` field — in `/etc/nslcd.conf` using `map` directives. For example, to construct home directory paths locally regardless of what the LDAP directory provides: ```text map passwd homeDirectory /home/$uid ``` See {manpage}`nslcd.conf(5)` for the full list of mappable attributes. ## Restrict login access By default, all users visible via LDAP can log in. To restrict which users are permitted, use `pam_access`. Edit `/etc/security/access.conf` to define the allowed users and groups: ```conf # Allow members of specific LDAP groups +:(some-admin-group) (some-user-group):ALL # Allow the local root user and members of the local "login" group (group name is customizable) +:root (login):ALL # Deny everyone else -:ALL:ALL ``` Replace `some-admin-group` and `some-user-group` with your actual LDAP group names. To process this config file, activate the `pam_access` module by creating a PAM configuration snippet at `/usr/share/pam-configs/ldap-access`: ```text Name: LDAP group-based login access Default: yes Priority: 128 Auth-Type: Additional Auth: required pam_access.so nodefgroup Account-Type: Primary Account: required pam_access.so nodefgroup Session-Type: Additional Session: required pam_access.so nodefgroup ``` Apply the configuration to add these modules to `/etc/pam.d/common-*`: ```bash sudo pam-auth-update --enable ldap-access --enable ldap --enable mkhomedir ``` ```{note} Local users not in any listed LDAP group can be permitted to log in by adding them to a local `login` group. The `login` group is allowed to log in through `/etc/security/access.conf`, see above. Create the group and add users as needed: sudo groupadd -r login sudo usermod -aG login ``` ## Grant sudo access to an LDAP group You can grant "administration privileges" via `sudo` through group memberships in your LDAP directory. To allow members of an LDAP group to use `sudo` access, add an entry to `/etc/sudoers` using {manpage}`visudo(8)`: ```text %your-admin-group ALL=(ALL) ALL ``` To allow `sudo` usage without entering a password, add this entry instead using {manpage}`visudo(8)`: ```text %your-admin-group ALL=(ALL) NOPASSWD: ALL ``` Replace `your-admin-group` with the relevant LDAP group name. Granting `sudo` access with a remote group like this is no different from using local groups. ## Retrieve SSH public keys from LDAP If your LDAP schema stores SSH public keys (for example in the `sshPublicKey` attribute), you can configure `sshd` to retrieve them automatically. Add the following to `/etc/ssh/sshd_config`: ```text AuthorizedKeysCommand /usr/local/bin/ssh-ldap-key %u AuthorizedKeysCommandUser nobody ``` The following Python script can serve as `/usr/local/bin/ssh-ldap-key`. It performs an anonymous LDAP search for the given username and prints any `sshPublicKey` values it finds. Install the `python3-ldap3` package before use: ```bash sudo apt install python3-ldap3 ``` ```python #!/usr/bin/env python3 """Fetch SSH public keys for a user from an LDAP directory.""" import argparse import logging import ldap3 def main(): cli = argparse.ArgumentParser(description="SSH key fetcher from LDAP") cli.add_argument("username") cli.add_argument("--ldap-server", default="ldaps://ldap.example.com") cli.add_argument("--base-dn", default="dc=example,dc=com") cli.add_argument("--verbose", "-v", action="count", default=0) args = cli.parse_args() log_level = (logging.WARNING, logging.INFO, logging.DEBUG)[min(args.verbose, 2)] logging.basicConfig(level=log_level, format="[%(asctime)s] %(message)s") for key in fetch_keys(args.username, args.ldap_server, args.base_dn): print(key) def fetch_keys(user, server, basedn): conn = ldap3.Connection(server) if not conn.bind(): raise RuntimeError(f"anonymous bind to {server!r} failed") uid = ldap3.utils.conv.escape_filter_chars(user) ok = conn.search( basedn, f"(&(objectClass=inetOrgPerson)(uid={uid}))", attributes=["sshPublicKey"], ) if not ok: raise RuntimeError(f"LDAP search failed: {conn.result}") if not conn.entries: return [] if len(conn.entries) > 1: raise RuntimeError(f"ambiguous username: {len(conn.entries)} entries returned") return [ ldap3.utils.conv.to_unicode(key) for key in conn.entries[0].sshPublicKey ] if __name__ == "__main__": main() ``` Make the script executable and owned by root: ```bash sudo install -o root -g root -m 0755 ssh-ldap-key /usr/local/bin/ssh-ldap-key ``` After modifying `sshd_config`, restart the SSH service: ```bash sudo systemctl restart ssh ``` ## Further reading - {ref}`install-openldap` - {ref}`sssd-with-ldap` - {ref}`ldap-and-tls` - {manpage}`nslcd(8)`, {manpage}`nslcd.conf(5)`, {manpage}`pam_access(8)`, {manpage}`visudo(8)`