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
- Manually, by logging in to the server and pasting the key into the
- 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
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
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.)
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.
Now pay close attention:
Let me pause here for a moment to note a thing or two. The file
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:
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
The next step, of course, is to add replace the location of the
sshd_config, and restart the SSH daemon. (
%h is a placeholder
for a user’s home directory, and
%u is substituted by a username.)
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… ;-)