When logging onto an SSH server with a key, the public portion of the key pair must be known to the server for it to be considered as a candidate. Typically, a user has the public keys the server will accept in his or her ~/.ssh/authorized_keys file. There are a number of ways those keys can be placed there:

  • Manually, by logging in to the server and pasting the key into the authorized_keys file.
  • With any form of configuration management software: Ansible has an authorized_key module, and other systems like Puppet, have something similar.
  • Using scp, rsync, etc.
  • Using a specialized system, such as SSH KeyDB or SKD (neither of which I’ve looked at).

Another idea is to use some form of database which the SSH server uses to determine whether a user’s key exists, but this typically means patching the SSH server. Or does it?

It’s been almost seven years that I first heard of FUSE, the Filesystem in Userspace, and until yesterday’s Sysadvent article by Kelsey Hightower, I’d almost forgotten about it. To recap, with FUSE it’s possible to implement a “virtual” filesystem in a userspace program, using a rather simple API. What this means is when an application lists a directory or opens a file, this file doesn’t really exist; rather it’s a function of your program which provides the data returned to the original invoker of the system call. So our “userspace” program has a function to simulate open(), another for readdir(), possibly one for write(), etc., but note that not all filesystem functions need necessarily be implemented – it depends what service our “filesystem” is to offer. (The hello world example on which my code builds demonstrates how easy it is.)

FUSE architecture

What Kelsey’s PubkeyFS basically does it to provide a marvelously simple alternative to providing SSH public keys to an SSH daemon from LDAP, without having to deploy a patched SSH daemon. I was glancing at (and nitpicking about) the code yesterday evening, and I thought we could maybe try and do something similar for all those lovely people who don’t like LDAP: let’s put it in the DNS. :-) This is just an experiment: instead of using the DNS for public-key storage, I could just as easily use a lightweight database, maybe something like Redis or MongoDB which could replicate keys from a central instance onto the SSH servers.

I won’t bore you with code at this point, because I’m still fiddling with it, but let me show you something “virtual”. I’ll first start my userspace program which will mount itself onto the specified directory.

$ ./sshpubkeys -o allow_other /var/lib/ssh 

Now pay close attention:

$ cd /var/lib/ssh
$ ls -l
total 0
drwxr-xr-x 2 root root 12 2012-12-18 19:17 192.168.33.1
-r--r--r-- 1 root root 10 2012-12-18 19:17 domain

$ cat domain
localhost

$ cat johndoe
cat: johndoe: No such file or directory

$ echo a.aa > domain
$ cat johndoe
ssh-dss AAAAB3NzaC1kc3MAAACBAJQlhMqJ[....]EQXS85ug3PLO9D3D7TBSZ2Pw== for John on MacBP

Let me pause here for a moment to note a thing or two. The file johndoe didn’t exist; we saw it didn’t. I was able to write into the file called domain even though I don’t have write permission because the FUSE driver (i.e. the userspace program) is explicitly allowing write operations.

After changing the domain to a.aa, the file johndoe suddenly appeared. Why? Because the sshpubkeys userspace program is now querying the correct DNS domain, and it could obtain the SSH public keys I stored there:

$ dig @127.0.0.2 johndoe.a.aa txt
;; ANSWER SECTION:
johndoe.a.aa.  50   IN   TXT   "ssh-dss AAAAB3NzaC1kc3MAAACBAJQlhMqJZHpRYfGM43WyPmjOcZL5DwXpto4uUgfEcH/QyGSlhmFi" [...]  "+rEQXS85ug3PLO9D3D7TBSZ2Pw== for John on MacBP"

So, sshpubkeys implements a FUSE driver which queries the DNS for a set of TXT records containing SSH public keys, and returns the text of those keys (multiple keys are supported) as though the content of .authorized_keys were being read.

The next step, of course, is to add replace the location of the AuthorizedKeysFile in sshd_config, and restart the SSH daemon. (%h is a placeholder for a user’s home directory, and %u is substituted by a username.)

# AuthorizedKeysFile   %h/.ssh/authorized_keys
AuthorizedKeysFile     /var/lib/ssh/%u

Does an SSH login to the server with John Doe’s corresponding private key work? Yes, it does.

My sshpubkeys FUSE program currently supports adding nameservers on the fly with a mkdir: just add a couple of directories named after the IP addresses (IPv4 or IPv6) of your nameservers.

I’ll probably be blown to kingdom come by people complaining about me putting yet even more stuff into DNS TXT records, but what the hell: it’s the end of the world on Friday anyway… ;-)

FUSE runs on Linux and on Mac OS/X with FUSE for OS X, and the the FUSE API is portable across these platforms.

Update 2019-03-01

OpenSSH’s AuthorizedKeysCommand was implemented in sshd around the year 2013 if I recall correctly, and I would use that today as a solution.

DNS, SSH, and FUSE :: 18 Dec 2012 :: e-mail