The bleeding-edge OpenSSH supports using an AuthorizedKeysCommand statement in sshd_config to get the authorized_keys file for a user. This lets you store your authorized_keys files in LDAP, but avoids linking OpenSSH against OpenLDAP. (You could actually use any data store for your back end, but LDAP is both the most popular and what I have.)
Your AuthorizedKeysCommands script should take one argument, and return a series of authorized keys, one per line. CentOS has a script, which I previously mentioned, and one of my readers was kind enough to put together an OpenBSD port for it.
But there’s some problems with the CentOS approach, from a cross-platform perspective. While it’s fine on CentOS, porting and installing it on other platforms is more difficult than it needs to be. I’d really like something that has very few dependencies, is easy to install, and is easily portable across platforms.
When setting up a couple of Ubuntu boxes I came across an Ubuntu AuthorizedKeysCommand script. His approach is much simpler than the CentOS approach, but his script didn’t work for me out of the box, and it wouldn’t work on other operating systems without modification. But it inspired me to write my own script, one that works across all of my operating systems, using only the tools I already have on all of my servers. My results follow. But before I offer my script, I feel obliged to warn you.
The bad news is, I am not a programmer. I have been told by multiple independent parties that my code makes the Baby Jesus cry.
The really bad with this is that you need an ldapsearch command that returns your public keys, and this command is hard-coded with this script. The ideal script would read the system ldap config, but you’d probably have to hard-code a path to ldap.conf anyway.
The appalling news is, I wrote it in Perl. Why? Well, the evidence indicates that I am insane. But every server in my environment runs Perl, and it’s been ten years since I used enough sed/awk to make this work. A proper authentication credentials script would not use Perl.
I’m not suggesting that you take my code. I’m hoping to motivate one of my programmer readers, whose code only makes the Baby Jesus grimace a little, to do better. I mean, I’ve set a pretty low bar here.
#!/usr/bin/perl
#takes one argument, the username to get the keys for
die unless $ARGV[0];
open (LDAP, "/usr/bin/ldapsearch -L -xZb\"dc=michaelwlucas,dc=com\" '(&(objectClass=posixAccount)(uid=$ARGV[0]))' sshPublicKey |") || die "ldapsearch failed $!\n";
while (<LDAP>) {
next if /^#|^version|^dn\:|^\s*$/;
s/\n//;
s/\://g;
s/sshPublicKey/\n/;
s/^ //;
print;
}
print "\n";
One catch with this is that you’re processing the raw output of ldapsearch. An acceptable ldapsearch will return keys that look something like this:
...
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAw9zmtbk8bT+7OVpVvzuYYQqRhF8j
...blah, blah, blah...
ilXzBw== rsa-key-20110114
Initially, my LDAP searches returned some keys that looked like this:
...
sshPublicKey:: c3NoLWRzcyANCkFBQUFCM056YUMxa2MzTUFBQUNCQVBhZmNhemdmUHI1T29DdFp
...blah, blah, blah...
y5sb2NhbA==
This key doesn’t start with ssh-rsa or ssh-dss, and it doesn’t even look like a SSH key. But if you look at the key in a graphical tool like phpLDAPAdmin, it has the leading ssh-rsa. In my case, the key had an extra carriage return at the end of the key, which meant that ldapsearch had to base64 encode it. Get rid of extra whitespace. The LPK patch doesn’t notice that space, but a simple external script does.
Of course, if someone wanted to write an AuthorizedKeysCommand that was portable, handled that encoding, and didn’t suck as bad as mine, it wouldn’t hurt my feelings at all. Really.