Managing SSH Known Hosts With SSSD And LDAP

For various reasons, I manage on-premises, non-cloud infrastructure. For centralized authentication, I use PAM and OpenLDAP along with SSSD for purposes of caching for availability and load reduction. I rely on the openssh-lpk.openldap.schema LDAP schema for passwordless authentication with OpenSSH, and, recently, I discovered that the same schema can be used to centralize SSH known hosts. This post isn't intended to be a how-to, but documents some of my findings that don't seem to be too well documented anywhere else.

Up until my discovery, I had been managing SSH known hosts with an Ansible playbook. In particular, each host public key was added into a managed GlobalKnownHostsFile that each client was configured to use. The SSH hosts keys were manually added into a playbook that was then applied to all the infrastructure. Or, well, maybe applied to all of the infrastructure. This approach worked well enough, but it was inconvenient because not all machines had 24/7 uptime.

If you're unfamiliar with the openssh-lpk.openldap.schema, it allows you to add an arbitrary number of sshPublicKey attributes to an LDAP entry. For example, a posixAccount object may list all public SSH keys that the user may authenticate with:

dn: uid=jsmith,ou=People,dc=example,dc=com
cn: John Smith
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: ldapPublicKey
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDIqh1s3idPZ8h7aSOu9Xx4/tBlSkP1Xy...

When the SSH server is configured to use the AuthorizedKeysCommand command with sss_ssh_authorizedkeys binary, SSHD will connect to the SSSD server and look-up the connecting user's public keys using the configured ldap_search_base and/or ldap_user_search_base SSSD configurations. If the authorized key is listed, authentication succeeds.

I recently discovered that this sshPublicKey mechanism can further be used to manage SSH know hosts!

To accomplish this, SSSD ships with a binary, sss_ssh_knownhostsproxy, that can be used as an SSH ProxyCommand to automatically look-up and cache SSH host keys within LDAP. When paired with an ldap_host_search_base, the sss_ssh_knownhostproxy binary will have the SSSD server perform an LDAP search to get the sshPublicKey attributes of an object representing the SSH server. If the host key offered by the SSH server is listed in its list of sshPublicKey attributes, the key is accepted without any user intervention and the connection succeeds.

The default configuration that SSSD uses didn't entirely match my current LDAP database (I'm probably using it wrong!), but with some configuration, it can be made to work. Given a host entry like this

dn: cn=somehost,ou=Hosts,dc=example,dc=com
objectClass: top
objectClass: device
objectClass: hostObject
objectClass: ldapPublicKey
cn: somehost
host: somehost.example.com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4y3W1prXnx8tc6Amq+tN0fEjIPW6f+qM...

A matching SSSD configuration is capable of performing the SSH host key lookup

ldap_host_search_base = ou=Hosts,dc=example,dc=com
ldap_host_object_class = hostObject
ldap_host_fqdn = host

With SSH clients configured with a ProxyCommand of sss_ssh_knownhostproxy, the task of key management is completely eliminated:

Host *.example.com
    ProxyCommand /usr/bin/sss_ssh_knownhostsproxy -p %p %h
    GlobalKnownHostsFile /var/lib/sss/pubconf/known_hosts

I struggled a fair bit to connect all the dots, but now that it's working, I hope this posting can be useful for somebody else.