LDAP / Active Directory
LDAP / Active Directory Integration
Section titled “LDAP / Active Directory Integration”Synchronize SSH public keys between SSH-KLM and your LDAP or Active Directory infrastructure, enabling centralized SSH authentication using directory-stored public keys.
Architecture
Section titled “Architecture”┌──────────────┐ ┌──────────────────┐ ┌──────────────┐│ SSH-KLM │◄─────►│ LDAP / Active │◄─────►│ SSH Servers ││ Platform │ │ Directory │ │ (sshd) │└──────────────┘ └──────────────────┘ └──────────────┘ │ │ │ │ • Push/pull keys │ • Store sshPublicKey │ • AuthorizedKeysCommand │ • Rotation events │ • User attributes │ • Real-time lookup │ • Policy enforcement │ • Group membership │ • No local key files ▼ ▼ ▼┌──────────────┐ ┌──────────────────┐ ┌──────────────┐│ Key │ │ User Object │ │ Authenticated││ Inventory │ │ cn=jdoe,ou=... │ │ Session │└──────────────┘ └──────────────────┘ └──────────────┘Flow:
- SSH-KLM manages key lifecycle (generation, rotation, revocation)
- Public keys are synced to LDAP/AD as user attributes
- SSH servers query LDAP at authentication time via
AuthorizedKeysCommand - No
authorized_keysfiles needed on individual hosts
Step 1: Configure LDAP Connection in SSH-KLM
Section titled “Step 1: Configure LDAP Connection in SSH-KLM”Navigate to Settings → Directory Integration and configure the LDAP connection.
OpenLDAP Configuration
Section titled “OpenLDAP Configuration”{ "directory_type": "openldap", "connection": { "url": "ldaps://ldap.example.com:636", "bind_dn": "cn=ssh-klm-service,ou=services,dc=example,dc=com", "bind_password": "...", "base_dn": "ou=users,dc=example,dc=com", "tls": { "verify": true, "ca_cert": "/etc/ssh-klm/ldap-ca.pem" } }, "sync": { "direction": "bidirectional", "interval_minutes": 15, "user_filter": "(objectClass=posixAccount)", "key_attribute": "sshPublicKey" }}Active Directory Configuration
Section titled “Active Directory Configuration”{ "directory_type": "active_directory", "connection": { "url": "ldaps://dc01.corp.example.com:636", "bind_dn": "CN=SSH-KLM Service,OU=Service Accounts,DC=corp,DC=example,DC=com", "bind_password": "...", "base_dn": "OU=Users,DC=corp,DC=example,DC=com", "tls": { "verify": true, "ca_cert": "/etc/ssh-klm/ad-ca.pem" } }, "sync": { "direction": "bidirectional", "interval_minutes": 15, "user_filter": "(&(objectClass=user)(objectCategory=person))", "key_attribute": "msDS-cloudExtensionAttribute1" }}Note: Active Directory does not include an
sshPublicKeyattribute by default. You must either extend the schema or use an available extension attribute.
Step 2: Map SSH Keys to LDAP User Attributes
Section titled “Step 2: Map SSH Keys to LDAP User Attributes”OpenLDAP Schema Extension
Section titled “OpenLDAP Schema Extension”Add the sshPublicKey attribute to your LDAP schema:
dn: cn=openssh-lpk,cn=schema,cn=configobjectClass: olcSchemaConfigcn: openssh-lpkolcAttributeTypes: ( 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 Public Key Object' SUP top AUXILIARY MAY ( sshPublicKey ) )Apply the schema:
ldapadd -Y EXTERNAL -H ldapi:/// -f ssh-public-key.ldifAdd SSH Key to a User
Section titled “Add SSH Key to a User”dn: uid=jdoe,ou=users,dc=example,dc=comchangetype: modifyadd: objectClassobjectClass: ldapPublicKey-add: sshPublicKeysshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrN... jdoe@workstationApply:
ldapmodify -H ldaps://ldap.example.com -D "cn=admin,dc=example,dc=com" -W -f add-key-to-user.ldifActive Directory Schema Extension
Section titled “Active Directory Schema Extension”For Active Directory, extend the schema with a custom attribute or use an existing unused attribute:
Option A: Use existing extension attribute
Map sshPublicKey to extensionAttribute1 or msDS-cloudExtensionAttribute1 (no schema change required).
Option B: Extend AD schema
# Run on Schema Master domain controller$schemaPath = (Get-ADRootDSE).schemaNamingContext
New-ADObject -Name "sshPublicKey" -Type "attributeSchema" -Path $schemaPath -OtherAttributes @{ lDAPDisplayName = "sshPublicKey" attributeId = "1.3.6.1.4.1.24552.500.1.1.1.13" oMSyntax = 4 attributeSyntax = "2.5.5.10" isSingleValued = $false searchFlags = 1}Step 3: Configure sshd for LDAP-Based Key Lookup
Section titled “Step 3: Configure sshd for LDAP-Based Key Lookup”Configure SSH servers to query LDAP for public keys instead of reading local authorized_keys files.
sshd_config
Section titled “sshd_config”# Disable local authorized_keys lookup (optional, for strict LDAP-only)AuthorizedKeysFile none
# Use LDAP lookup commandAuthorizedKeysCommand /usr/local/bin/ssh-ldap-keys %uAuthorizedKeysCommandUser nobody
# Fallback: allow both LDAP and local keys# AuthorizedKeysFile .ssh/authorized_keys# AuthorizedKeysCommand /usr/local/bin/ssh-ldap-keys %u# AuthorizedKeysCommandUser nobodyAuthorizedKeysCommand Script (OpenLDAP)
Section titled “AuthorizedKeysCommand Script (OpenLDAP)”#!/bin/bash# Fetches SSH public keys from LDAP for the given username
LDAP_URI="ldaps://ldap.example.com:636"BASE_DN="ou=users,dc=example,dc=com"BIND_DN="cn=ssh-lookup,ou=services,dc=example,dc=com"BIND_PW_FILE="/etc/ssh/ldap-bind-password"
USERNAME="$1"
if [ -z "$USERNAME" ]; then exit 1fi
ldapsearch -LLL -H "$LDAP_URI" \ -D "$BIND_DN" \ -y "$BIND_PW_FILE" \ -b "$BASE_DN" \ "(uid=$USERNAME)" \ sshPublicKey | \ sed -n 's/^sshPublicKey: //p'Set permissions:
chmod 755 /usr/local/bin/ssh-ldap-keyschown root:root /usr/local/bin/ssh-ldap-keysAuthorizedKeysCommand Script (Active Directory)
Section titled “AuthorizedKeysCommand Script (Active Directory)”#!/bin/bash# Fetches SSH public keys from Active Directory
LDAP_URI="ldaps://dc01.corp.example.com:636"BASE_DN="OU=Users,DC=corp,DC=example,DC=com"BIND_DN="CN=SSH-Lookup,OU=Service Accounts,DC=corp,DC=example,DC=com"BIND_PW_FILE="/etc/ssh/ad-bind-password"
USERNAME="$1"
if [ -z "$USERNAME" ]; then exit 1fi
# Map Linux username to AD sAMAccountNameldapsearch -LLL -H "$LDAP_URI" \ -D "$BIND_DN" \ -y "$BIND_PW_FILE" \ -b "$BASE_DN" \ "(sAMAccountName=$USERNAME)" \ sshPublicKey | \ sed -n 's/^sshPublicKey: //p'Restart sshd
Section titled “Restart sshd”sudo systemctl restart sshdTest the Lookup
Section titled “Test the Lookup”# Test the script directlysudo -u nobody /usr/local/bin/ssh-ldap-keys jdoe
# Test SSH authenticationssh -v jdoe@server.example.comStep 4: Sync and Rotation Workflows
Section titled “Step 4: Sync and Rotation Workflows”Sync Modes
Section titled “Sync Modes”| Mode | Description |
|---|---|
| SSH-KLM → LDAP | Keys managed in SSH-KLM are pushed to LDAP attributes |
| LDAP → SSH-KLM | Keys discovered in LDAP are imported into SSH-KLM inventory |
| Bidirectional | Changes in either system are synchronized |
Key Rotation Workflow
Section titled “Key Rotation Workflow”- SSH-KLM generates a new key pair (per rotation policy)
- New public key is written to the user’s
sshPublicKeyattribute in LDAP - SSH servers immediately pick up the new key via
AuthorizedKeysCommand - Old key is removed from LDAP after a grace period
- SSH-KLM marks the old key as revoked in the inventory
Configure Rotation in SSH-KLM
Section titled “Configure Rotation in SSH-KLM”{ "rotation_policy": { "enabled": true, "interval_days": 90, "grace_period_hours": 24, "algorithm": "ed25519", "notify_user": true, "auto_sync_ldap": true }}Revocation
Section titled “Revocation”When a key is revoked in SSH-KLM:
- The
sshPublicKeyattribute is removed from the user’s LDAP entry - All SSH servers immediately reject the revoked key
- No host-level changes required
Example: Complete LDAP Schema for SSH Keys
Section titled “Example: Complete LDAP Schema for SSH Keys”# Complete schema file for OpenLDAPdn: cn=openssh-lpk,cn=schema,cn=configobjectClass: olcSchemaConfigcn: openssh-lpkolcAttributeTypes: ( 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 Public Key Object' SUP top AUXILIARY MAY ( sshPublicKey ) )Example User Entry with SSH Key
Section titled “Example User Entry with SSH Key”dn: uid=jdoe,ou=users,dc=example,dc=comobjectClass: inetOrgPersonobjectClass: posixAccountobjectClass: ldapPublicKeycn: John Doesn: Doeuid: jdoeuidNumber: 1001gidNumber: 1001homeDirectory: /home/jdoeloginShell: /bin/bashsshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrN... jdoe@workstationsshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB... jdoe@laptopTroubleshooting
Section titled “Troubleshooting”| Issue | Cause | Resolution |
|---|---|---|
| LDAP connection refused | Firewall or TLS misconfiguration | Verify port 636 is open; check CA certificate |
AuthorizedKeysCommand not executing | Incorrect permissions | Script must be owned by root, mode 755 |
| Keys not found in LDAP | Wrong base DN or filter | Test with ldapsearch manually |
| Sync not updating LDAP | Bind DN lacks write permissions | Grant modify access to sshPublicKey attribute |
| AD schema extension fails | Not running on Schema Master | Transfer Schema Master role or run on correct DC |
| Multiple keys not working | Single-valued attribute | Ensure sshPublicKey is multi-valued in schema |
| sshd ignoring AuthorizedKeysCommand | AuthorizedKeysCommandUser not set | Must specify a valid user (e.g., nobody) |
Security Considerations
Section titled “Security Considerations”- Always use LDAPS (port 636) or StartTLS — never plain LDAP
- Use a dedicated service account with minimal permissions for SSH-KLM
- The
AuthorizedKeysCommandscript should not be writable by non-root users - Store bind passwords in files with
600permissions, not in scripts - Audit LDAP access logs for unauthorized key modifications
- Consider LDAP connection pooling for high-traffic SSH servers