hazardous

implementing u2f usability with ssh

solo dolo


awhile back - perhaps a year - a coworker participated in the SoloKey kickstarter campaign, and gave me one of the SoloKeys he received knowing that I had an interest in that sort of thing. at the time - and currently, i suppose - the dominant product for this particular market is the line of products manufactured by Yubikey, and as much as i would like one, it's hard to justify spending $30-50 on something that would seamlessly work with only a handful of things i currently secure via the usual means of passwords, 2fa, et cetera.

a bit later, the devs maintaining OpenSSH announced they had begun implementing FIDO2/U2F compatibility in the test builds of upcoming SSH versions, and in April, Ubuntu 20.04 "focal fossa" dropped - and with it came the by-default shipping of OpenSSH 8.2, which can be configured to accept security keys as an acceptable method of authentication.

i've installed 20.04 on a few boxes and so far I am extremely excited by all the new features and improvements, but I am still going to wait a bit before i begin upgrading my important machines from the last LTS - for example, the server hosting this website. no particular reason: just a habit, one based on experience, of waiting to run a new program so that other users can be the unlucky folks who discover a bug or glitch :)

so for now i have been content with simply upgrading older SSH installs to the more recent versions, so i can find a nice compromise between the unsecured convenience of a passwordless SSH key, and the secure inconvenience of a key requiring a password to use. i figure that sharing my process to get, build, and set up OpenSSH 8.22 might be useful, so here goes!

setting up the required files


i am going to assume that if you're this far into reading, you are familiar with both linux and using the commandline. (or you might be my mom. hi mom!) so i will simply launch right into it:

build the required library: Yubico maintains the libfido2 library which has the code your computer craves for all that security-key goodness. so we're going to grab a copy of it and install it before we get to the good stuff.

on debian/ubuntu-based systems, you will need build-essentials, devscripts, cmake, libcbor-dev, libudev-dev packages to build the following.

git clone https://github.com/Yubico/libfido2.git
cd libfido2
mkdir build && cd build
cmake ..

hopefully i didn't miss any required packages, and cmake spat out a completed set of buildfiles. from here, you simply need to run the make, and then sudo make install. from the build folder:

make -j8
sudo make install

i highly recommend using the '-j8' flag when running the make command. it tells the make engine to utilize all available processors to build, and will dramatically increase the speed of your compilation.

all done! you should be good to go, and we now move on to the main event.

installing OpenSSH v8.2


by the time you read this, OpenSSH might be available as a public release which includes support by default. however, isn't it more fun to roll your own program? personally it is something i do because it's a great way to learn a lot about compiling programs, and the various methods and tricks used to do so.

you always have the option of grabbing the latest code straight from the source on github, but it's a rolling codebase which may or may not introduce issues and bugs - at any moment it may change as something is added, edited, or removed. so for this post i will be using a stable, packaged tarball of the 8.2 source code to build with.

cd ~/lab
wget https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.2p1.tar.gz
tar xvzf openssh-8.2p1.tar.gz
mv openssh-8.2p1.tar.gz openssh
cd openssh

i make heavy use of my tab key while mashing keys on the command line, so there is likely a more efficient way to decompress a tarball into a folder named openssh (without the 8.2p1 suffix), but this is how i did it.

the process here is simple, but you will want to include a few flags that will instruct the make engine to include the FIDO2 security key feature. you might also want to configure the make to output the finished files into a custom folder - as opposed to installing to, and replacing, your current SSH installation which is typically at /usr/bin/ssh.

./configure --with-security-key-builtin --sysconfdir=$HOME/lab/openssh --prefix=$HOME/lab/openssh
make -j8
make install

whew! even with that -j8 flag, it definitely took a hot minute to cobble all those bits together. but hey, that's how you know you're getting something which will provide excellent security - it will take those bastards at the NSA ten minutes to crack your security, as opposed to the usual thirty seconds or so they would use to compromise your average newbie's files!

run a quick check to make sure you've got the classic ssh menu we all know and love:

cd ~/lab/openssh
bin/ssh
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]
           [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
           [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]
           [-i identity_file] [-J [user@]host[:port]] [-L address]
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
           [-Q query_option] [-R address] [-S ctl_path] [-W host:port]
           [-w local_tun[:remote_tun]] destination [command]

you might be annoyed that, for all this effort, you're getting an ssh binary that looks and acts in every way like the typical, boring ol' ssh menu. however, the more enlightened among you know the skill and effort it takes to make something new and great appear to be no different than what it is intended to replace.

using SSH


so i might have exaggerated the header above slightly, because we're still not quite ready. come to think of it, this might need to be something to address in the stable releases, if it isn't already - i recall having to do the following on my ubuntu 20.04 box at any rate. by default, linux tends to play it real conservative with new and unfamiliar USB devices - for good reason - and so we're going to have to modify the udev rules and let our OS know that your security key is a welcome, invited guest.

don't worry, compared to everything else this is the simplest task: simply add a new rule for udev to follow by creating a textfile in the proper location, and then add a set of rules. for me, using a solokey, i was able to find the latest rule file here.

open a new file in /etc/udev/rules.d named 90-fido.rules and paste in the rules list:

UPDATE!!! it looks like there's a package which should help you skip all this: libu2f-udev, avaliable on debian and ubuntu. running sudo apt install libu2f-udev will likely allow you to skip all this; if so simply proceed to the next step.

sudo vim /etc/udev/rules.d/90-fido.rules
# Notify ModemManager this device should be ignored
 ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
 SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
 ENV{DEVTYPE}!="usb_device",  GOTO="mm_usb_device_blacklist_end"

 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"

 LABEL="mm_usb_device_blacklist_end"

 # Solo bootloader + firmware access
 SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
 SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"

 # ST DFU access
 SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"

 # U2F Zero
 SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"

remember to restart udev! sudo systemctl restart udev

bringing it all together


i swear the moment of truth is upon us!

it's pretty straightforward if you're familiar with using ssh, and if you choose to install your compiled program over the default ssh install, the only difference when creating a key will be to add the flag -t ecdsa-sk when using ssh-keygen. the prompts will be similar, with the exception of a new prompt that instructs you on when to press the button on your key, and when this registers, SSH will create a new key for you - one that has no password, and will instead unlock only when you insert your SoloKey and press the auth button on it.

cd ~/lab/openssh
bin/ssh-keygen -t ecdsa-sk -f /tmp/test_ecdsa_sk
Generating public/private ecdsa-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in -f /tmp/test_ecdsa_sk/test_ecdsa_sk
Your public key has been saved in -f /tmp/test_ecdsa_sk/test_ecdsa_sk.pub
The key fingerprint is:
SHA256:.../... host@localhost

as you can see, i used the -f flag to save the output in a temporary test folder, so depending on how you're following this, you might want to either omit this flag entirely or modify it to suit your needs.

but, there you have it - an awesome and secure SSH key locked with a physical FIDO2 device! wild.

BEWARE! this will only work with an SSH server that is version 8.2 or higher, so it will still be some time before you can use this whereever you can use the regular SSH keys we all know and love. so for now, it's most useful on servers and machines where you can upgrade the default SSH install - and of course it's usable on any machine running the ubuntu 20.04 LTS.

voila: a very cool way to avoid having obnoxiously complex passwords, while avoiding the potential security pitfalls associated with using a passwordless key. now go out there and lock all your stuff down!