At ClueTrust we’re constantly re-evaluating our security posture based on both perceived threat and what level of effort it takes to implement various security technologies. In short, being a small shop we're always conscious of the "effort" component of the effort/reward ratio.
Here in the future in 2020 it has finally become possible to do a little bit better with ssh than just requiring keys. OpenSSH 8.2 included support for Universal Two-Factor (U2F/FIDO) tokens. For the uninitiated, this is an open standard for strong crypto-based challenge/response that was championed by a consortium that includes all the folks you might expect to see there . Note that today’s FIDO is called FIDO2. Gaige has done some cursory investigation in the backward compatibility department to quantify the constraints you’ll get from a FIDO-not-FIDO2 key (enumerated below).
If you got this far and said "yeah, we've using Yubikeys for years with ssh, you push the button on the key and a 35 character string comes out; why is this news", let me stop you right there and say "this is not that". Saying “yubikey” is like saying “car” without specifying how many doors or what kind of roof it has. The Yubikey 5 family, for instance, will do the following authentication methods: Secure Static Passwords, Yubico OTP, OATH – HOTP (Event), OATH – TOTP (Time), Smart Card (PIV-Compatible), OpenPGP, FIDO U2F, FIDO2. I’ve configured ssh with PIV keys. It works. A friend of mine swears by ssh with OpenPGP - that is good and spits out keys that are plain ’ol ssh-rsa from a user perspective which is nice for backward compatibility. This is about the way that FIDO2 support is implemented in OpenSSH.
Still got your attention? Great. Until recently, we felt /FIDO.*/ was interesting-but-impractical for general use since the only thing that seemed to support them was Chrome. But over the past year, Safari (on both macOS and iOS) and OpenSSH 8.2 (released in mid-February 2020) have added support. The latter raises it to the point where the cost both in terms of dollars (as low $20 for a FIDO2-only Yubikey token via Amazon) and effort required to make this something we wanted to pilot. And we did.
A little note about the sub-$30 Yubikeys - the full name of the model in question is “Security Key by Yubico” (or its slightly more expensive sibling, "Security Key NFC by Yubico”). You might think these names are purely descriptive since Yubikey’s products might all be described as “security keys” (note lowercase). They're not - those are the actual model identifiers. Yubikey has kindly published a full historical list of Yubikey models. Of particular note,FIDO and FIDO2 aren’t the same thing and any of the older models that you might have lying around from earlier experiments or hacker conference door prizes years ago - in fact anything that doesn’t show FIDO2 or FIDO on that list - will end in frustration. From Gaige’s junk drawer, we know the following:
- The 4C is not listed as FIDO2, but is FIDO U2F and can be used with ecdsa-sk, but not ed25519-sk. Since the 4C self-describes as a 4, it appears series 4 keys will likely work, but don’t go searching out bargains on NOS. • The 5ci works fine with either.
- The “Legacy” products, such as the Standard (2014-2016) do not work at all in this mode.
- Note that if you got a freebie from Ars Technica a couple of years back, it’s likely a 4, which is at least capable of doing ecdsa-sk.
I'm a Mac user. If you use something else for your unix-like desktop applications (say, WSL2 or or some Linux flavor) you’re on your own but you can probably figure this one out since it’s just a matter of compiling openssh-portable from source and exposing a USB device to userland. I am unaware of any non-Unix ssh implementations that support the FIDO2 token on the client side (yet, maybe PuTTY will do this some day soon), though I’ll be super excited when Prompt or Blink support it via NFC the iPad.
Here are the steps for playing with it:
Buy a Security Key by Yubico or perhaps you want to pay an extra $7 to be baller and roll with NFC. It works on relatively modern iDevices - I think you need an iPhone 7 or newer. If you already have one that’s up to the task (see the historical list of Yubikey models above), good on ya - you’re already ahead of the (adoption, not crypto) curve. Of course you can always buy one of the spendy ones too if the spirit moves ya.
You’re probably aware that Amazon has a big supply chain contamination problem. Can’t hurt to test the token you get out by going to Yubico's test site. I haven’t tested with a non-Yubico FIDO2 token, but my guess is that testing devices with baked-in PKI attestation certs for legitimacy is pretty darned easy - in fact that's the whole point of the attestation cert.
You’re already running Homebrew right? Splendid.
brew update ; brew upgrade ; brew install openssh. Hopefully you have your $PATH set properly so that stuff that you install with Homebrew (/usr/local/bin, /usr/local/sbin, etc) is listed first so it takes precedence over stock stuff that’s installed in /usr/bin. If not, you probably should do this. Congratulations, now you have a working OpenSSH that does all the right things and is linked to the libfido2 libraries (which it autoinstalls as a dependency). Note: if you weren’t previously using the homebrew version of ssh, you may need to rehash your paths to get the latest ssh and ssh-keygen (rehash under /bin/csh and derivatives, hash -r under bash and similar). This was tested with latest Homebrew on:
- macOS High Sierra (version 10.13) on a 13” Late 2011 MacBook Pro
- macOS Mojave (version 10.14) on a 15” Late 2015 MacBook Pro
macOS Catalina (version 10.15) on a 2019 Mac Pro
Stick the Yubikey in a port. This should go without saying but you can go no further without said key.
Create yourself a new key. There are some instructions here that I disagree with a bit (on the basis of proper identification of the key (the Security Key by Yubico apparently does not have a serial number) and failure to choose a variant of the One True Collection of Mutually Compatible Date Formats (iso-8601)) but read it anyway before you instead do this:
ssh-keygen -t ecdsa-sk -C "$(hostname)-yourname-example.com-$(date +'%Y%m%d')" -f ".ssh/id_ecdsa_sk-yourname-example.com-$(date +'%Y%m%d’)"
or perhaps if you’re all about the djb curves:
ssh-keygen -t ed25519-sk -C "$(hostname)-yourname-example.com-$(date +'%Y%m%d')" -f ".ssh/id_ed25519-sk-yourname-example.com-$(date +'%Y%m%d’)"
Of course your email address is not email@example.com but you probably already picked up on the need to modify that. Keen and readers will notice that I’ve used the "8601 Basic” format which is specifically not supposed to be used where it will be exposed to humans due to missing syntactic sugar. To appeal to your pedantry, we will refer to this missing symbol as the “hyphen (minus)” glyph, as it is described in ANSI X3.4-1968.
Now that you've got a key generated, you unfortunately can't use it just anywhere. On the remote host that you propose to ssh into, you'll need to either get lucky and pick up OpenSSH 8.2 via a package update on your target system or install it by building OpenSSH-portable from source. Note that this will get easier over time; the difficulties stem from the fact that it has barely been out for a month.
copy the new PUBLIC key over to the destination host. The private part stays on your laptop, as always.
scp .ssh/id_ecdsa_sk-yourname-example.com-20200307.pub firstname.lastname@example.org
- ssh to jumphost.example.com via the normal way with your existing keys and append the key to ~/.ssh/authorized_keys
email@example.com:~$ cat id_ecdsa_sk-yourname-example.com-20200307.pub >> .ssh/authorized_keys
- Back on your laptop, you’re probably running ssh-agent. Later when everything is normalized can still forward the keys in ssh-agent, but you can’t put an “sk” key in ssh-agent (for obvious reasons) and you don’t want to know anything about existing unlocked keys if you have preferences recorded in .ssh/config, so in the interests of avoiding confusion during testing do this:
Gaige was able to get this working just by specifying the -i, but if you have a lot of keys loaded by default, you might need -o IdentitiesOnly=yes. A good recipe for rs (without unsetting SSH_AUTH_SOCK) ended up being:
ssh -p 2200 -i .ssh/id_ecdsa_sk -o IdentitiesOnly=yes firstname.lastname@example.org.
With that said, by using -i with the SK paired with -A one can mix in ssh-agent and forward unlocked keys that aren’t used for this login, to ssh into another machine with a non-sk key (say, from the jumphost to some protected server ona backnet, for instance). More on the slightly confusing behavior in prioritization of keys on Gaige’s blog.
- Let's give it a try!
ssh -p 2200 -i .ssh/id_ecdsa_sk-whatever-the-datestamped-file-ended-up-being-called.pub -o IdentitiesOnly=yes email@example.com
It should prompt you for a password (the same password you used when creating the key) and then a press on the Yubikey to confirm your presence. Note that without the Yubikey present, all of the crypto necessary to unlock the ssh key is not present, and you're outta luck. If all went according to plan, you should be in via 2FA. Isn’t that special…
- You probably want to set some definitions for what key to use in .ssh/config on your laptop. For instance, my shortcuts entries look like so:
host jumphost.example.org jumphost HostName jumphost.example.org User rs ForwardAgent yes identityfile ~/.ssh/id_ecdsa_sk-rs-seastrom-com-20200307
Lastly, please send along any useful feedback including the fine points of getting this working on other platforms, grammatical fixes, clarifications, etc!