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
attributes to an LDAP entry. For example, a
posixAccount object may list all public SSH keys that the user may
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_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
automatically look-up and cache SSH host keys within LDAP. When paired with an
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
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
sss_ssh_knownhostproxy, the task of key management is completely
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.