
How to use FIDO2 USB keys with SSH - stelabouras
https://www.stavros.io/posts/u2f-fido2-with-ssh/
======
spiridow
I'm really excited to see more and more people talk about FIDO2. If you're
interested about this topic, I gave a talk about it yesterday:
[https://news.ycombinator.com/item?id=23689606](https://news.ycombinator.com/item?id=23689606)

~~~
jjoonathan
Here's a browser compatibility matrix (I know, OP is about SSH).

FIDO2/U2F Just Works in:

    
    
        Chrome on Windows
        Firefox on Windows
        Chrome on Mac
        Firefox on Mac
        Chrome in Ubuntu 20
        Firefox in Ubuntu 20
    

Wall of shame (FIDO2/U2F does not Just Work in):

    
    
        Safari
    
    

EDIT: it does work out-of-the-box in Ubuntu 20, my bad.

~~~
tialaramex
In a _browser_ what you want is WebAuthn, U2F is an older never technically
standardized hack and should _not_ be used for new implementations.

New web sites should do WebAuthn to enable this functionality, here's a guide
someone else wrote that I found helpful in talking about the moving parts to
actually implement this: [https://webauthn.guide/](https://webauthn.guide/)

Firefox's WebAuthn implementation isn't as complete as it would ideally be,
but it does have a nice feature of _asking_ the user whether to give out the
somewhat privacy-infringing "attestation" from a FIDO2 device when it is
requested by a web site. IMNSHO ordinary web sites, especially where a second
factor isn't even mandatory, should _not_ be asking for attestation and I
always refuse.

~~~
lxgr
Care to elaborate how attestation is privacy infringing?

As far as I understand, private attestation is a specific design goal of
WebAuthN, achieved by either sharing an attestation credential with at least
100 000 instances of a given authenticator or via cryptographic means.

There have been instances of authenticator vendors getting this wrong, but I
remember reading that browsers will detect it and strip any attestation
response in this case.

~~~
tialaramex
Not everybody is comfortable that 100 000 is enough.

Is that extreme? Yes, but the _upside_ to giving out attestation data just
isn't there in most scenarios. It's like I have to step over broken glass to
get a stale bagel. Yes these boots mitigate the risk from the glass very well
but I don't even want a stale bagel anyway.

There are some scenarios where attestation makes sense. If you issued every
one of your employees a genuine Yubico Yubikey then I guess it could make
sense to insist on checking with attestation that nobody is using some
homebrew device they built instead. But for general use? Even the tiny risk
isn't justified, so it should be "off".

Here's someone _much_ smarter than me proposing something you could do _if_
you really care about the features from attestation but don't want people to
give up privacy. If you insist on offering stale baked goods, here is how to
clear up that glass:

[https://www.imperialviolet.org/2019/01/01/zkattestation.html](https://www.imperialviolet.org/2019/01/01/zkattestation.html)

------
DCKing
EDIT: I misunderstood the post, and what I describe below is not true!

I'm incredibly excited about FIDO2, but this is quite underwhelming honestly.
I'd like to SSH with a credential on my Yubikey, not by a credential or
configuration _already stored on my computer_ that is unlocked by my Yubikey.
I'd like to be able to plug in my Yubikey anywhere and go. My Linux desktop,
my Macbook, my Windows desktop, my Android phone.

\- Yubikey with GPG/PIV for SSH: your Yubikey stores your private key. You can
take it anywhere, plug it in, [have to go through all the setup required for
your computer to talk GPG/PIV], and log in.

\- Yubikey with FIDO2 for SSH: your Yubikey stores a symmetric key to unlock
your private key on your computer. [You _cannot_ take it anywhere], plug it
in, don't have to set anything up if your client and server have this
(eventually), and log in.

FIDO2 is solving a lot of authentication convenience problems, but not this
one I think. I get that this pretty nice when integrating with Windows Hello
or Apple's TouchID, but I don't think FIDO2 USB key with SSH is that great.

~~~
StavrosK
You misunderstand how FIDO2 works. Read the article, it details how to do
exactly what you say it can't. You can resume your excitement now!

~~~
DCKing
In that case I'll need to update my understanding, but this still requires you
to do manual configuration for your SSH key - the id_mykey_sk file in your
example.

> ssh-keygen -t ecdsa-sk -O resident -f ~/.ssh/id_mykey_sk

I know this is just a reference, but it's still manual configuration. On a
host with an SSH client that can speak PIV [this is a challenge], I can just
plug in, enter the PIV PIN code, and go.

~~~
StavrosK
Read farther down, you don't need this key, you can delete it if you want.
You'll just have to run `ssh-add -K` every session if you do, so your agent
reads the key from the device.

~~~
DCKing
Ah I missed that. My apologies, I learned something new!

~~~
StavrosK
No problem, it's a fantastic way to use SSH, I'm just glad it exists.

------
ptspts
I managed to make this work today as described in the article, after
installing and configuring the software dependencies.

Client-side hardware dependencies:

* USB token with U2F (FIDO) support. FIDO2 is optional. Any old YubiKey or similar will work.

* For the resident key feature only: USB token with FIDO2 support.

* To avoid confusion, only a single USB token should be connected when ssh-keygen is run. (When ssh is run, multiple USB tokens work, the user can touch the wrong one many times, and authentication succeeds after the user touches the right one.)

* ED25519 support in the token is optional. (`ssh-keygen -t ecdsa-sk ...' uses the NIST P-256 curve, which works with all U2F tokens.)

Client-side software dependencies:

* For communicating with the token over USB, OpenBSD or (Linux with udev).

* OpenSSH 8.2p1 or later.

* OpenSSH client (ssh) compiled with `configure --with-security-key-builtin'. Without this, eventually authentication will fail locally with `internal security key support not enabled'. It's possible to work around this by compiling an .so file and specifying it with `ssh -o SecurityKeyProvider=....so', but it's complicated.

Server-side software dependencies:

* OpenSSH 8.2p1 or later.

* Default OpenSSH server (sshd) settings (without PubkeyAcceptedKeyTypes), or PubkeyAcceptedKeyTypes in /etc/ssh/sshd_config containing sk-ecdsa-sha2-nistp256@openssh.com and (optionally, for ed25519-sk keys) sk-ssh-ed25519@openssh.com .

~~~
ptspts
FYI Another client-side software dependency: libfido2 >=1.3.0. It doesn't work
with libfido2 1.2.x or earlier.

Also, if it doesn't work on your client system only because OpenSSH 8.2 was
compiled without `configure --with-security-key-builtin', here is how to make
it work: [https://github.com/pts/external-sk-
libfido2](https://github.com/pts/external-sk-libfido2)

------
michaelt
_> be extra careful when using SSH forwarding (the -A option), as the server
can then ask your computer to authenticate to other servers on your behalf._

That would require an extra press of the token's button for each extra
authentication, right?

~~~
staticassertion
I would assume the agent caches the key for signing.

Would someone please confirm this? If it did require another key press that
would be pretty huge.

~~~
tialaramex
> I would assume the agent caches the key for signing.

For that to happen the agent would need to have some way to get the key out of
the FIDO authenticator, which is deliberately not intended to be possible. I
will now go away and confirm that this behaves as I expected and update this
message shortly.

Update: Yes, the OpenSSH agent just has code to go talk to the authenticator
each time it needs to sign something. The authenticator may or may not (most
seem to not) allow this to happen without verifying user presence (e.g. via a
button press or touching a contact) _but_ even if your device does allow this
the _signed payload_ says whether the user was present, so a remote SSHD can
(if it wanted) demand to see signed evidence of user presence or refuse login,
and I think a SSH agent can't fake that without help from the FIDO
authenticator itself.

~~~
jlgaddis
> _... so a remote SSHD can (if it wanted) demand to see signed evidence of
> user presence or refuse login, ..._

I'd like to be able to, server-side, 1) require that the private key used for
authentication be stored on a hardware device and 2) require user presence,
but I've never read or heard that this is possible (granted, I haven't looked
into too much).

If you have links to any documents that discuss how to implement this, I'd
appreciate you sharing them. The only relevant documentation I can find is
regarding FIDO/U2F support and even the release notes for 8.2 mention that
"OpenSSH does not currently make use of this [attestation certificate]".

~~~
tialaramex
So there's two parts, let's take them separately.

> 1) require that the private key used for authentication be stored on a
> hardware device

So for this you're going to need the attestation data, which as you observe
OpenSSH currently doesn't do anything with. It _is_ willing to stash it in a
file, and because it's a _certificate_ it's safe for the token's owner to send
this somewhere. So you could construct a mechanism to examine the attestation
file and decide whether to accept the proffered public key based on that
attestation. e.g. "This is a genuine Yubikey 5C, so OK" or "This is a Crap
Corp Funky Fake, no thanks".

I expect this would be a bunch of work, and I don't expect Free Software
people to help build something to do it any time soon. But from what I can see
it's possible with the components that already exist. Maybe somebody who
really wants this will do all the work and put it on GitHub.

> 2) require user presence

The FIDO "options" bitflags have a flag for "user presence" (sometimes
labelled UP). If this isn't set, user presence was not checked by the
authenticator.

The FIDO authenticator includes this options parameter as part of the message
it's signing.

So this means if a SSH client/ agent sends you an options field with UP set,
but actually user presence wasn't checked it won't be able to provide a
signature that matches. The SSH server doesn't need to do anything special
except look at the UP bitflag if it cares (all WebAuthn servers are required
to check this, it's on the long list of steps to perform WebAuthn
authentication properly).

To be fair this isn't OpenSSH (though I'm sure they have at least somebody
paying attention) it's FIDO itself that designed the signatures to also sign
the user presence indication, they couldn't have avoided it.

~~~
jlgaddis
> _I expect this would be a bunch of work, and I don 't expect Free Software
> people to help build something to do it any time soon._

That's basically the conclusion I ended up at but I was hoping you were aware
of some recent progress that I didn't know about! Thank you, though, I
certainly appreciate the response.

------
graton
I was able to get this going, but it took awhile as I use a non-standard
working mode.

All of the docs I have read assume that you are logged in locally on the
system, but if you are not (like me) then things fall apart.

I am running a Windows 10 desktop, and then SSH into my local Linux box from
Windows. Both systems are sitting next to me and I can press the Yubikey
easily.

My local Linux system is running Fedora 32 and I did the following to enable a
user connected via SSH to use the Yubikey.

Created a user group for yubikey users, which in reality only has me in it.

Created a /etc/polkit-1/rules.d/99-pcsc-yubikey.rules file which gives
smartcard access to the 'yubikey' group. Without this then 'ykman list' would
not work.

Created a /etc/udev/rules.d/99-yubikey.rules to give access to the 'yubikey'
group. I used /lib/udev/rules.d/69-yubikey.rules as the starting point for my
file. I had to add my two Yubikeys USB IDs (lsusb to see them) as they weren't
present.

Made sure to log out and back in to have the 'yubikey' group be active for my
user. I vaguely remember a command that would do it, but I forgot it.

After all of that I got it to work :)

------
simias
I've been using my Yubikey in GnuPG smartcard mode for years to do the same
thing, from what I can see from this tutorial FIDO2 seems a bit easier to
setup initially but it also seems much less widely supported at the moment.
Are there other tradeoffs to consider?

~~~
StavrosK
I think those are pretty much the only ones, the Yubikey in GPG smartcard mode
was always too fiddly for me and interfered with my agent in other ways, but
this is trivial to set up and use.

Also, a big draw of the USB SSH key for me is that I can plug it in to other
computers and connect to my servers, which smartcard mode didn't do, so that
was a big drawback for me.

~~~
RL_Quine
You can't do that easily, you still need the public key file on disk.

~~~
StavrosK
Not with resident key mode.

~~~
RL_Quine
Sort of. The keys have limited space on them (20 for some Yubikey models) for
resident keys. You wouldn't want to be always using them for everything.

~~~
StavrosK
That's why I'm excited about SoloKeys v2, they (will) come with space for
thousands of resident keys.

------
dboreham
I've waited 10 years for this.

~~~
knorker
Waited 10 years? Why didn't you just use a yubikey in PIV mode, or the yubikey
with gpg, or a smartcard, or… I've been using a yubikey PIV for, hmm… at least
5 years.

Sure, a FIDO key is cheaper.

~~~
closeparen
OpenSSH doesn't do X.509, how would PIV mode or a smartcard help?

~~~
Xylakant
Yubikeys work in PIV mode with openssh, it just requires the necessary module
and some invocation dance with ssh-agent
[https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PK...](https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html)
or Filippo Valsordas yubikey-agent [https://github.com/FiloSottile/yubikey-
agent](https://github.com/FiloSottile/yubikey-agent)

~~~
knorker
What invocation dance?

This works:

"ssh -oPKCS11Provider=/path/to/opensc-pkcs11.so user@host.com"

Or you place that in your ~/.ssh/config

Host *

    
    
      PKCS11Provider /path/to/opensc-pkcs11.so

------
j88439h84
Is this instead of typing a password to unlock the ssh key?

~~~
Freak_NL
If you wish. You can decide if you want a passphrase on the generated key or
not, just as you can with a classic SSH key. It's probably best to use a
passphrase and have ssh-agent remember it for you until you shut down the
computer. That way you enter your passphrase the first time using SSH with
that key after logging in, and ask you to tap the physical U2F key, and only
ask for the interaction with the physical key afterwards.

When I SSH to a host configured like this, the Yubikey U2F key I am using
blinks to ask me to touch it. It's really quite neat.

------
decentralbanker
been looking forward to seeing FIDO gain traction for more use cases. i
interviewed at a company where there's work being done on a FIDO-enabled
smartphone approach:

[https://www.hypr.com/passwordless-ssh-linux-fido-
login/](https://www.hypr.com/passwordless-ssh-linux-fido-login/)

------
Bedon292
Am I missing something? The commands for resident and non-resident appear to
be identical.

Edit: They fixed it.

~~~
arianvanp
they have the addition of -O resident

from ssh-keygen:

    
    
           resident
                  Indicate that the key should be stored on the FIDO authenticator itself.  Resident keys may be supported  on
                  FIDO2 tokens and typically require that a PIN be set on the token prior to generation.  Resident keys may be
                  loaded off the token using ssh-add(1).
    
    

from ssh-add:

    
    
            -K     Load resident keys from a FIDO authenticator

~~~
StavrosK
I had the same command in both by mistake :/

~~~
notaplumber
You still do?

EDIT: I see it now.

~~~
StavrosK
It just takes a while for the site to deploy.

------
indigodaddy
@StavrosK do you have a writeup on how you put together your blog
infrastructure?

~~~
StavrosK
Not really but it's just Lektor and Netlify, hosted on Gitlab. I also have
Gitlab CI deploying the site on Neocities
([https://neo.stavros.io/](https://neo.stavros.io/)) as a backup/just to see
if I could.

------
RL_Quine
Be aware literally nothing supports this unless it's your own kernel.

Gitlab? No. Github? No. My gateway with a hand built gentoo kernel? Yes.

It seems functional, but you've also got to be aware that `ed25519-sk` and
`ecdsa-sk` have sort of spotty support in the devices too. `ed25519-sk` does
not work on a Yubikey <5, for example.

~~~
StavrosK
> Be aware literally nothing supports this unless it's your own kernel.

Ubuntu 20.04 and later supports this, and, since that's LTS, it means that
quite a few servers and machines will be supporting it already.

Github/Gitlab aren't supporting it yet, but given how great it is for
security, I think they have a big incentive to speed up support.

> ed25519-ek does not work on a Yubikey <5, for example.

That's no problem, since you can just generate an ECDSA key instead, but yes,
not all keys have hardware support for all algorithms.

~~~
Freak_NL
Debian 11 too, out of the box. It took a bit of work to use a newer openssh-
client on my Ubuntu 18.04 laptop, but that was manageable.

