LDAP Authentication with OpenSSH

A complete, production-ready guide to centralize SSH authentication with LDAP using SSSD, PAM, and public key lookup from your directory.

Time Required
45 – 90 min
Difficulty
Intermediate
Platforms
Ubuntu 22/24 · RHEL 8/9
Updated
February 2026
This guide covers LDAP-based SSH auth using SSSD (System Security Services Daemon) as the recommended modern approach. SSSD provides caching, failover, offline auth, and integrates cleanly with PAM and NSS. We cover both OpenLDAP and Active Directory backends.
Select Your Platform

Architecture Overview

How it works

When a user connects via SSH to a server configured with LDAP authentication, the following flow occurs:

User Client OpenSSH Server (sshd) SSSD Daemon LDAP Server OpenLDAP / Active Directory SSH Session PAM Auth Result LDAP(S) Bind + Search AuthorizedKeysCommand Fetch SSH public key from LDAP NSS (passwd, group) User / group resolution

Key components:

  • SSSD — Handles LDAP communication, caching, and user/group resolution via NSS. Provides offline auth when the LDAP server is unreachable.
  • PAM — Pluggable Authentication Modules chain that SSSD plugs into for password-based auth and access control.
  • OpenSSH AuthorizedKeysCommand — Lets sshd fetch SSH public keys directly from LDAP instead of ~/.ssh/authorized_keys.
  • NSS — Name Service Switch maps LDAP users/groups into the system so id, getent, and home directory creation work transparently.

Step 1 — Prerequisites

Before you begin

Before configuring LDAP authentication for SSH, verify the following:

A running LDAP server (OpenLDAP or Active Directory) accessible over the network
LDAP base DN (e.g., dc=example,dc=com) and bind credentials
TLS certificate for LDAPS (port 636) or StartTLS (port 389) — never use plaintext LDAP in production
At least one LDAP user with posixAccount objectClass (uidNumber, gidNumber, homeDirectory, loginShell)
Root or sudo access on the target SSH server
Network connectivity from the SSH server to the LDAP server on port 636 (LDAPS) or 389 (StartTLS)
Security note: Always use LDAPS (TLS) or StartTLS for LDAP communication. Plaintext LDAP transmits credentials in clear text and is unsuitable for any environment.

LDAP user entry example (LDIF format):

user.ldif dn: uid=jdoe,ou=People,dc=example,dc=com objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: jdoe cn: John Doe sn: Doe givenName: John mail: [email protected] uidNumber: 10001 gidNumber: 10001 homeDirectory: /home/jdoe loginShell: /bin/bash userPassword: {SSHA}hashed_password_here

If you also want SSH public key lookup from LDAP (covered in Step 6), include the sshPublicKey attribute:

openssh-lpk.ldif (schema extension) dn: cn=openssh-lpk,cn=schema,cn=config objectClass: olcSchemaConfig cn: openssh-lpk olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'OpenSSH Public Key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DESC 'OpenSSH LPK objectclass' SUP top AUXILIARY MAY ( sshPublicKey ) )

Step 2 — Install SSSD & Required Packages

Package installation

Install SSSD and its LDAP backend along with supporting utilities for PAM, NSS, and home directory creation.

Ubuntu / Debian sudo apt update sudo apt install -y sssd sssd-ldap sssd-tools \ libpam-sss libnss-sss \ libsss-sudo \ oddjob-mkhomedir \ ldap-utils
RHEL / CentOS sudo dnf install -y sssd sssd-ldap sssd-tools \ oddjob oddjob-mkhomedir \ openldap-clients

Enable automatic home directory creation so LDAP users get a home directory on first login:

Ubuntu / Debian sudo pam-auth-update --enable mkhomedir
RHEL / CentOS sudo systemctl enable --now oddjobd sudo authselect select sssd with-mkhomedir --force

Step 3 — Configure SSSD for LDAP

Core configuration

SSSD configuration lives in /etc/sssd/sssd.conf. This file must have 0600 permissions — SSSD refuses to start otherwise.

/etc/sssd/sssd.conf — OpenLDAP backend [sssd] domains = example.com services = nss, pam, ssh config_file_version = 2 [domain/example.com] # Identity & Authentication provider id_provider = ldap auth_provider = ldap chpass_provider = ldap # LDAP server URI (use ldaps:// for TLS) ldap_uri = ldaps://ldap.example.com:636 # Base DN for user and group searches ldap_search_base = dc=example,dc=com # Bind DN (service account) for LDAP queries ldap_default_bind_dn = cn=readonly,dc=example,dc=com ldap_default_authtok_type = password ldap_default_authtok = YourBindPasswordHere # TLS settings ldap_tls_reqcert = demand ldap_tls_cacert = /etc/ssl/certs/ca-certificates.crt # User and group object mapping ldap_user_search_base = ou=People,dc=example,dc=com ldap_group_search_base = ou=Groups,dc=example,dc=com ldap_user_object_class = posixAccount ldap_user_name = uid ldap_user_uid_number = uidNumber ldap_user_gid_number = gidNumber ldap_user_home_directory = homeDirectory ldap_user_shell = loginShell ldap_user_ssh_public_key = sshPublicKey # Group mapping ldap_group_object_class = posixGroup ldap_group_name = cn ldap_group_gid_number = gidNumber ldap_group_member = memberUid # Performance & caching cache_credentials = true entry_cache_timeout = 600 ldap_id_use_start_tls = false enumerate = false # Access control (optional — restrict who can log in) access_provider = ldap ldap_access_filter = (objectClass=posixAccount) [nss] filter_groups = root filter_users = root homedir_substring = /home [pam] offline_credentials_expiration = 7 [ssh] # Enable SSH public key retrieval from LDAP
/etc/sssd/sssd.conf — Active Directory backend [sssd] domains = corp.example.com services = nss, pam, ssh config_file_version = 2 [domain/corp.example.com] # Use AD provider for full AD integration id_provider = ad auth_provider = ad chpass_provider = ad access_provider = ad # AD domain and servers ad_domain = corp.example.com ad_server = dc1.corp.example.com, dc2.corp.example.com ad_hostname = server01.corp.example.com # Kerberos realm (auto-detected when using ad provider) krb5_realm = CORP.EXAMPLE.COM # Use short names (jdoe instead of [email protected]) use_fully_qualified_names = false fallback_homedir = /home/%u default_shell = /bin/bash # SSH public key from AD attribute # Requires 'sshPublicKey' attribute added to AD schema # or use altSecurityIdentities/extensionAttribute ldap_user_ssh_public_key = altSecurityIdentities # Performance cache_credentials = true entry_cache_timeout = 600 enumerate = false # Access control — restrict SSH to members of a specific AD group ad_access_filter = memberOf=CN=SSH-Users,OU=Groups,DC=corp,DC=example,DC=com [nss] filter_groups = root filter_users = root [pam] offline_credentials_expiration = 7 [ssh]
When using the AD provider, the machine must be joined to the domain first. Use realm join corp.example.com (install realmd and adcli packages). SSSD's ad provider auto-discovers servers, configures Kerberos, and maps AD attributes to POSIX.

Set correct permissions and start SSSD:

Shell sudo chmod 600 /etc/sssd/sssd.conf sudo chown root:root /etc/sssd/sssd.conf sudo systemctl enable --now sssd sudo systemctl restart sssd

Configure NSS to use SSSD: Edit /etc/nsswitch.conf to add sss as a source for user and group lookups.

/etc/nsswitch.conf (relevant lines) passwd: files sss shadow: files sss group: files sss sudoers: files sss

Verify LDAP user resolution:

Shell # Look up an LDAP user — should return uid, gid, home, shell getent passwd jdoe # Expected output: # jdoe:*:10001:10001:John Doe:/home/jdoe:/bin/bash # Look up LDAP groups getent group developers
Skip the complexity — centralize SSH access in minutes OnePAM replaces LDAP+PAM+SSH plumbing with identity-aware access, session recording, and RBAC. No SSH keys to manage, no SSSD to configure.
Try Free

Step 4 — Configure PAM for SSH Authentication

Authentication chain

PAM (Pluggable Authentication Modules) is the bridge between OpenSSH and SSSD. When UsePAM yes is set in sshd_config, OpenSSH delegates password authentication to the PAM stack, which in turn calls SSSD to verify credentials against LDAP.

On Ubuntu/Debian, PAM is typically auto-configured when installing libpam-sss. Verify the SSH PAM config includes SSSD:

/etc/pam.d/sshd (Ubuntu) # Standard Unix authentication @include common-auth @include common-account @include common-session @include common-password # Create home directory on first login session required pam_mkhomedir.so skel=/etc/skel/ umask=0077

The common-auth include should already reference pam_sss.so. Verify:

Shell grep pam_sss /etc/pam.d/common-auth # Expected: auth [default=1] pam_sss.so use_first_pass

On RHEL/CentOS, use authselect to configure the PAM stack for SSSD:

RHEL / CentOS # Select SSSD profile with home directory creation sudo authselect select sssd with-mkhomedir --force # Verify PAM is configured correctly sudo authselect check # View current profile sudo authselect current

The resulting /etc/pam.d/sshd should include:

/etc/pam.d/sshd (RHEL) auth substack password-auth auth include postlogin account required pam_nologin.so account include password-auth password include password-auth session required pam_selinux.so close session required pam_loginuid.so session optional pam_keyinit.so force revoke session required pam_selinux.so open session required pam_namespace.so session optional pam_oddjob_mkhomedir.so session include password-auth session include postlogin
Do not lock yourself out. Always keep a separate terminal with root access open while modifying PAM. Incorrect PAM configuration can prevent all logins, including SSH and console.

Step 5 — Configure OpenSSH Server

sshd_config

Edit /etc/ssh/sshd_config to enable PAM authentication and (optionally) LDAP public key lookup. This configuration enables both password auth via LDAP and public key auth with keys stored in your directory.

/etc/ssh/sshd_config (LDAP-relevant settings) # Enable PAM — required for LDAP password authentication UsePAM yes # Allow password authentication (through PAM/LDAP) PasswordAuthentication yes # Also allow public key authentication (keys from LDAP or local) PubkeyAuthentication yes # Fetch SSH public keys from LDAP via SSSD # (see Step 6 for the helper script) AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys %u AuthorizedKeysCommandUser nobody # Disable root login (security best practice) PermitRootLogin no # Allow LDAP users to log in # Option A: Allow all LDAP users # (access is controlled via SSSD's ldap_access_filter) # Option B: Restrict to specific groups AllowGroups ssh-users sysadmins # Ensure ChallengeResponseAuthentication is enabled for PAM prompts KbdInteractiveAuthentication yes # Log level for debugging (set to INFO for production) LogLevel VERBOSE

Validate and restart SSH:

Shell # Validate config syntax before restarting sudo sshd -t # If no errors, restart the service sudo systemctl restart sshd # Monitor the log for auth attempts sudo journalctl -u sshd -f
Tip: Use AllowGroups in sshd_config combined with ldap_access_filter in sssd.conf for defense-in-depth. Even if SSSD allows a user, sshd will reject them if they're not in the permitted group.

Step 6 — SSH Public Key Lookup from LDAP

Centralized key management

Instead of managing ~/.ssh/authorized_keys files on every server, store SSH public keys in LDAP as attributes on user entries. SSSD's sss_ssh_authorizedkeys command fetches them automatically when sshd needs to verify a key.

Option A: Using SSSD (recommended)

SSSD natively supports SSH key retrieval via the [ssh] service. Ensure ssh is listed in the services line of sssd.conf and ldap_user_ssh_public_key is set to the correct LDAP attribute name.

Verify SSSD SSH key retrieval # Test that SSSD can fetch the SSH key for a user sss_ssh_authorizedkeys jdoe # Expected output: the user's SSH public key(s) # ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... [email protected]

Option B: Custom script (fallback)

If you cannot use SSSD's SSH service or need more control, use a custom script with ldapsearch:

/usr/local/bin/ldap-ssh-keys.sh #!/bin/bash # Fetch SSH public keys from LDAP for the given username. # Used by OpenSSH's AuthorizedKeysCommand. USER="$1" if [ -z "$USER" ]; then exit 1 fi LDAP_URI="ldaps://ldap.example.com:636" BASE_DN="ou=People,dc=example,dc=com" BIND_DN="cn=readonly,dc=example,dc=com" BIND_PW="/etc/ldap/bind.secret" ldapsearch -LLL -H "$LDAP_URI" \ -x -D "$BIND_DN" -y "$BIND_PW" \ -b "$BASE_DN" \ "(uid=$USER)" sshPublicKey \ 2>/dev/null | \ sed -n 's/^sshPublicKey: //p'
Setup the custom script # Set permissions sudo chmod 755 /usr/local/bin/ldap-ssh-keys.sh sudo chown root:root /usr/local/bin/ldap-ssh-keys.sh # Store bind password securely echo "YourBindPasswordHere" | sudo tee /etc/ldap/bind.secret > /dev/null sudo chmod 600 /etc/ldap/bind.secret sudo chown root:root /etc/ldap/bind.secret # If using custom script, update sshd_config: # AuthorizedKeysCommand /usr/local/bin/ldap-ssh-keys.sh %u # AuthorizedKeysCommandUser nobody

Add SSH public key to an LDAP user:

add-ssh-key.ldif dn: uid=jdoe,ou=People,dc=example,dc=com changetype: modify add: objectClass objectClass: ldapPublicKey - add: sshPublicKey sshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrN... jdoe@workstation
Apply the LDIF ldapmodify -H ldaps://ldap.example.com:636 \ -D "cn=admin,dc=example,dc=com" -W \ -f add-ssh-key.ldif
Eliminate SSH key sprawl entirely OnePAM issues short-lived SSH certificates tied to your identity provider. No keys in LDAP, no authorized_keys files, no key rotation headaches.
Learn More

Step 7 — Testing & Verification

Validate the setup

Run through this checklist to verify every component is working correctly. Test from a separate machine to simulate real user access.

1. Verify LDAP user resolution via NSS:
Shell # Should return LDAP user details getent passwd jdoe id jdoe # Should return LDAP groups getent group developers
2. Verify SSSD can authenticate the user:
Shell # Test PAM authentication directly sudo sssctl user-checks jdoe # Check SSSD domain status sudo sssctl domain-status example.com
3. Verify SSH public key retrieval from LDAP:
Shell # Should output the user's SSH public key(s) sss_ssh_authorizedkeys jdoe # Or test the custom script /usr/local/bin/ldap-ssh-keys.sh jdoe
4. Test SSH login with password:
From a remote machine # Test password authentication ssh -o PreferredAuthentications=password [email protected] # Verbose output for debugging ssh -vvv [email protected] 2>&1 | grep -E "(auth|pam|ldap|sssd)"
5. Test SSH login with public key from LDAP:
From a remote machine # Test public key authentication (key must be in LDAP) ssh -o PreferredAuthentications=publickey -i ~/.ssh/id_ed25519 [email protected]
6. Verify home directory was created:
Shell ls -la /home/jdoe/ # Should show a newly created home directory with correct ownership
If all six checks pass, your LDAP-based SSH authentication is fully operational. Users can now log in with their centralized LDAP credentials, and SSH keys are managed from a single source of truth.

Step 8 — Hardening & Best Practices

Production-ready security

Apply these hardening measures before deploying to production:

1. Enforce LDAPS and certificate verification:
/etc/sssd/sssd.conf # Always use ldaps:// and demand a valid certificate ldap_uri = ldaps://ldap.example.com:636 ldap_tls_reqcert = demand ldap_tls_cacert = /etc/ssl/certs/ca-certificates.crt # If using StartTLS on port 389 instead: # ldap_uri = ldap://ldap.example.com:389 # ldap_id_use_start_tls = true
2. Use a dedicated read-only bind account:
Create a minimal-privilege bind account (LDIF) dn: cn=sssd-bind,ou=ServiceAccounts,dc=example,dc=com objectClass: simpleSecurityObject objectClass: organizationalRole cn: sssd-bind userPassword: {SSHA}secure_hashed_password description: Read-only service account for SSSD LDAP queries
3. Restrict SSH access with LDAP group filters:
/etc/sssd/sssd.conf — access control # Only allow members of 'ssh-users' group to log in access_provider = ldap ldap_access_order = filter ldap_access_filter = (memberOf=cn=ssh-users,ou=Groups,dc=example,dc=com)
4. Harden sshd_config:
/etc/ssh/sshd_config — hardening additions # Disable password auth once LDAP public key auth works # PasswordAuthentication no # Use only strong key exchange and cipher algorithms KexAlgorithms [email protected],curve25519-sha256,[email protected] Ciphers [email protected],[email protected],[email protected] MACs [email protected],[email protected] # Session limits MaxAuthTries 3 MaxSessions 5 LoginGraceTime 30 ClientAliveInterval 300 ClientAliveCountMax 2 # Disable unused features X11Forwarding no AllowAgentForwarding no PermitTunnel no PermitUserEnvironment no # Logging LogLevel VERBOSE
5. Enable SSSD failover and caching:
/etc/sssd/sssd.conf — failover # Multiple LDAP servers for high availability ldap_uri = ldaps://ldap1.example.com:636, ldaps://ldap2.example.com:636 # DNS-based service discovery (alternative to explicit URIs) # ldap_uri = _srv_ # dns_discovery_domain = example.com # Cache credentials for offline authentication cache_credentials = true offline_credentials_expiration = 7 # Reconnection settings ldap_connection_expire_timeout = 900 ldap_opt_timeout = 10
6. Set up monitoring and alerting:
Shell # Monitor SSSD status sudo sssctl domain-status example.com # Check SSSD is online systemctl is-active sssd # Watch for auth failures in real-time sudo journalctl -u sshd -u sssd -f --no-pager | grep -iE "(fail|error|denied)" # Periodic health check (add to cron or monitoring) getent passwd jdoe > /dev/null 2>&1 || echo "LDAP lookup failed"

Step 9 — Troubleshooting

Common issues & fixes
SSSD won't start — "Permission denied" or "Config file has wrong permissions"
Fix sudo chmod 600 /etc/sssd/sssd.conf sudo chown root:root /etc/sssd/sssd.conf sudo systemctl restart sssd
getent passwd username returns nothing
Debug # Enable debug logging in sssd.conf # Add under [domain/example.com]: # debug_level = 6 # Restart and check logs sudo systemctl restart sssd sudo journalctl -u sssd -n 100 # Verify LDAP connectivity directly ldapsearch -H ldaps://ldap.example.com:636 \ -x -D "cn=readonly,dc=example,dc=com" -W \ -b "ou=People,dc=example,dc=com" "(uid=jdoe)" # Clear SSSD cache and retry sudo sss_cache -E sudo systemctl restart sssd getent passwd jdoe
SSH login fails with "Permission denied" despite correct password
Debug # Check SSSD authentication sudo sssctl user-checks jdoe --action=auth # Check PAM is using SSSD grep pam_sss /etc/pam.d/sshd /etc/pam.d/common-auth # Check sshd config allows password auth sudo sshd -T | grep -iE "(passwordauth|usepam|allowgroups|denygroups)" # Verify user is in allowed groups (if AllowGroups is set) id jdoe | grep ssh-users # Check SSSD access filter sudo sssctl user-checks jdoe --action=acct
AuthorizedKeysCommand not working — public key auth fails
Debug # Test the command directly as the configured user sudo -u nobody sss_ssh_authorizedkeys jdoe # Check sshd sees the configuration sudo sshd -T | grep authorizedkeyscommand # Common issues: # 1. Script not executable: chmod 755 # 2. Script not owned by root: chown root:root # 3. AuthorizedKeysCommandUser doesn't exist # 4. SELinux blocking execution (RHEL): sudo ausearch -m avc -ts recent | grep ssh sudo setsebool -P nis_enabled 1
TLS/SSL certificate errors
Debug # Test TLS connection to LDAP server openssl s_client -connect ldap.example.com:636 -showcerts # Verify CA certificate is trusted openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \ /path/to/ldap-server-cert.pem # On RHEL, update CA trust store: sudo cp your-ca.pem /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust
Home directory not created on first login
Ubuntu / Debian # Verify mkhomedir PAM module is enabled grep mkhomedir /etc/pam.d/common-session # Should show: session optional pam_mkhomedir.so # Re-enable if missing sudo pam-auth-update --enable mkhomedir
RHEL / CentOS # Verify oddjobd is running sudo systemctl status oddjobd # Re-enable mkhomedir sudo authselect select sssd with-mkhomedir --force sudo systemctl restart oddjobd

Move beyond LDAP + SSH plumbing

OnePAM provides Zero Trust access to SSH, RDP, databases, and web apps. Identity-based authentication, session recording, RBAC policies, and compliance reports — without managing SSSD, PAM, or SSH keys.

Start Free Trial

Ready to simplify SSH access management?

Replace LDAP/SSSD/PAM complexity with OnePAM's unified PAM platform. Set up in minutes, not hours.

Start Free Trial View Pricing
No SSH keys to manage Session recording built-in SSO with your IdP