Alex Edwards

Securing SSH with FIDO2

Over the past year, I’ve seen more attacks attempting to escalate privileges, exfiltrate credentials, compromise environments, and poison publishing workflows.

Password managers such as Proton Pass or Bitwarden have been discussed for years, and everyone should be using one. But I see far less discussion about securing other developer credentials, especially SSH keys.

Traditionally, SSH keys live on disk. They can be protected with a passphrase, but they are still files that malware can find, copy, and attempt to use later.

So I wanted something better: SSH keys that are useful to me, but much harder to steal or reuse.

TL;DR: Skip to Conclusion

Securing SSH Keys

While researching this, I came across OpenSSH’s security key support using either ecdsa-sk or ed25519-sk.

With *-sk keys, OpenSSH stores a key handle on disk, but the security-sensitive operation requires a hardware FIDO authenticator, such as a YubiKey or SoloKey. The file by itself is not enough.

This is the same general family of technology behind WebAuthn and passkeys, which immediately made it feel like the right direction, I’m already a big advocate for using such devices for MFA and PassKeys.

FIDO2-backed SSH keys feel simpler for day-to-day developer use:

  • They are supported directly by modern OpenSSH.
  • They work nicely with the same security-key model used by WebAuthn/passkeys.
  • They can require physical presence.
  • They can optionally require user verification with a PIN.
  • They avoid leaving a normal reusable private key sitting on disk.

What I wanted to achieve was to require presence, just like MFA, when the key was to be used, and the private key safe on the security key.

Touching a USB dongle, or entering a pin is a small enough compromise to prevent malware or agents running off with my credentials and having a bad day for it.

Security Keys Aren’t Replacing Everything

I still have access credentials that need to live on headless VMs, in CI/CD, in automated systems. These deployments don’t have access to a physical device that a human can interact with.

Creating a Secured Key

First, you’ll need the hardware: I’ll be using a YubiKey for this, so any direct interaction with the key will be based on that. If you’re using a Solokey or anything else, refer to the vendor’s documentation.

It’s also important that you buy two: one as your primary key, and another as a backup that you keep somewhere safe. After I verify both work and are set up correctly, I can simply gain access using my backup and revoke only the lost one.

To create a FIDO-hosted SSH Key, you’ll need:

  • openssh 8.3+
  • libfido2
  • ykman (for a YubiKey)

Setting a FIDO PIN

Yubikeys comes with a factory-default PIN that should be changed to something memorable:

ykman fido access change-pin

After that, the key is ready to use. Just don’t forget the pin.

Creating an SSH Key

To create a simple ‘touch to authenticate’ SSH key, the command is as straightforward as creating any other key:

ssh-keygen -t ed25519-sk -C "your_email@example.com"

The options start getting more interesting when you want a higher level of authentication by requiring a PIN as well:

ssh-keygen -t ed25519-sk -O verify-required -C "your_email@example.com"

Or creating a key that can be transported between machines:

ssh-keygen -t ed25519-sk -O resident -C "your_email@example.com"

The above can be combined, along with other key/value variables that simply help to identify SSH keys further down the line.

For my own keys, I want to use resident keys and a default of simply needing touch, keeping the touch+pin option for accounts or access that require a heightened security.

Resident keys and portability

Resident keys are one of the nicest parts of this setup. They allow me to carry my SSH credentials between devices and run:

ssh-keygen -K

This downloads the resident SSH key handles from the YubiKey and recreates the local files. They still need the YubiKey to work, and they still have the same level of interaction required.

Conclusion

If you are on a modern OS and have a FIDO2-capable security key, securing SSH with a YubiKey is surprisingly approachable.

For interactive developer access, ed25519-sk gives you a much better security model than a normal private key sitting on disk. Add resident if you want portability between machines. Add verify-required if you want PIN + touch authentication.

The most important operational advice is simple: have a backup key. Create separate credentials for each device, register both public keys, and test them before you rely on them.

I also put together a small script that helped me while migrating my SSH keys on macOS.