Skip to content

LDAP / Active Directory

Synchronize SSH public keys between SSH-KLM and your LDAP or Active Directory infrastructure, enabling centralized SSH authentication using directory-stored public keys.


┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ 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:

  1. SSH-KLM manages key lifecycle (generation, rotation, revocation)
  2. Public keys are synced to LDAP/AD as user attributes
  3. SSH servers query LDAP at authentication time via AuthorizedKeysCommand
  4. No authorized_keys files 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.

{
"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"
}
}
{
"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 sshPublicKey attribute 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”

Add the sshPublicKey attribute to your LDAP schema:

ssh-public-key.ldif
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 Public Key Object'
SUP top
AUXILIARY
MAY ( sshPublicKey ) )

Apply the schema:

Terminal window
ldapadd -Y EXTERNAL -H ldapi:/// -f ssh-public-key.ldif
add-key-to-user.ldif
dn: uid=jdoe,ou=users,dc=example,dc=com
changetype: modify
add: objectClass
objectClass: ldapPublicKey
-
add: sshPublicKey
sshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrN... jdoe@workstation

Apply:

Terminal window
ldapmodify -H ldaps://ldap.example.com -D "cn=admin,dc=example,dc=com" -W -f add-key-to-user.ldif

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

Terminal window
# 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.

/etc/ssh/sshd_config
# Disable local authorized_keys lookup (optional, for strict LDAP-only)
AuthorizedKeysFile none
# Use LDAP lookup command
AuthorizedKeysCommand /usr/local/bin/ssh-ldap-keys %u
AuthorizedKeysCommandUser nobody
# Fallback: allow both LDAP and local keys
# AuthorizedKeysFile .ssh/authorized_keys
# AuthorizedKeysCommand /usr/local/bin/ssh-ldap-keys %u
# AuthorizedKeysCommandUser nobody
/usr/local/bin/ssh-ldap-keys
#!/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 1
fi
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:

Terminal window
chmod 755 /usr/local/bin/ssh-ldap-keys
chown root:root /usr/local/bin/ssh-ldap-keys

AuthorizedKeysCommand Script (Active Directory)

Section titled “AuthorizedKeysCommand Script (Active Directory)”
/usr/local/bin/ssh-ad-keys
#!/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 1
fi
# Map Linux username to AD sAMAccountName
ldapsearch -LLL -H "$LDAP_URI" \
-D "$BIND_DN" \
-y "$BIND_PW_FILE" \
-b "$BASE_DN" \
"(sAMAccountName=$USERNAME)" \
sshPublicKey | \
sed -n 's/^sshPublicKey: //p'
Terminal window
sudo systemctl restart sshd
Terminal window
# Test the script directly
sudo -u nobody /usr/local/bin/ssh-ldap-keys jdoe
# Test SSH authentication
ssh -v jdoe@server.example.com

ModeDescription
SSH-KLM → LDAPKeys managed in SSH-KLM are pushed to LDAP attributes
LDAP → SSH-KLMKeys discovered in LDAP are imported into SSH-KLM inventory
BidirectionalChanges in either system are synchronized
  1. SSH-KLM generates a new key pair (per rotation policy)
  2. New public key is written to the user’s sshPublicKey attribute in LDAP
  3. SSH servers immediately pick up the new key via AuthorizedKeysCommand
  4. Old key is removed from LDAP after a grace period
  5. SSH-KLM marks the old key as revoked in the inventory
{
"rotation_policy": {
"enabled": true,
"interval_days": 90,
"grace_period_hours": 24,
"algorithm": "ed25519",
"notify_user": true,
"auto_sync_ldap": true
}
}

When a key is revoked in SSH-KLM:

  1. The sshPublicKey attribute is removed from the user’s LDAP entry
  2. All SSH servers immediately reject the revoked key
  3. No host-level changes required

Example: Complete LDAP Schema for SSH Keys

Section titled “Example: Complete LDAP Schema for SSH Keys”
/etc/ldap/schema/openssh-lpk.ldif
# Complete schema file for OpenLDAP
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 Public Key Object'
SUP top
AUXILIARY
MAY ( sshPublicKey ) )
dn: uid=jdoe,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: ldapPublicKey
cn: John Doe
sn: Doe
uid: jdoe
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/jdoe
loginShell: /bin/bash
sshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrN... jdoe@workstation
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB... jdoe@laptop

IssueCauseResolution
LDAP connection refusedFirewall or TLS misconfigurationVerify port 636 is open; check CA certificate
AuthorizedKeysCommand not executingIncorrect permissionsScript must be owned by root, mode 755
Keys not found in LDAPWrong base DN or filterTest with ldapsearch manually
Sync not updating LDAPBind DN lacks write permissionsGrant modify access to sshPublicKey attribute
AD schema extension failsNot running on Schema MasterTransfer Schema Master role or run on correct DC
Multiple keys not workingSingle-valued attributeEnsure sshPublicKey is multi-valued in schema
sshd ignoring AuthorizedKeysCommandAuthorizedKeysCommandUser not setMust specify a valid user (e.g., nobody)

  • Always use LDAPS (port 636) or StartTLS — never plain LDAP
  • Use a dedicated service account with minimal permissions for SSH-KLM
  • The AuthorizedKeysCommand script should not be writable by non-root users
  • Store bind passwords in files with 600 permissions, not in scripts
  • Audit LDAP access logs for unauthorized key modifications
  • Consider LDAP connection pooling for high-traffic SSH servers