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.)
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.