Skip to content

Instantly share code, notes, and snippets.

@buswedg
Last active January 20, 2025 20:37
Show Gist options
  • Save buswedg/270e7594a68ff99f2af659a97661eb5e to your computer and use it in GitHub Desktop.
Save buswedg/270e7594a68ff99f2af659a97661eb5e to your computer and use it in GitHub Desktop.

YubiKey for FIDO2 Auth on WSL2 (Ubuntu)

What?

I got my hands on a new YubiKey 5 over the weekend. For those unaware, this is a pretty well-known security key which provides options for hardware authentication using a variety of different protocols (e.g. FIDO2, OpenPGP etc.). I was keen to try this out for authenticating with GitHub and also handling SSH credentials when accessing servers I'm running on my local network.

The problem however, is that I'm a pretty heavy WSL (Ubuntu) user and needed something which plays well in that environment. I didn't think about it too much when I initially ordered the YubiKey, but worked out over the weekend that getting a USB device (particularly a security key) to play nicely with WSL, isn't exactly straight forward. So, I did a write-up of the steps I ended up taking for others (or myself) to refer back to.

References

Steps

1. Setting up the YubiKey

Yubico offer a YubiKey Manager to help set up keys. They've got downloads for various platforms including Linux, but I decided to go with the Windows-based manager so I could set up/ manage the key independently of using it within my WSL Ubuntu environment.

Once YubiKey Manager is installed, I went in the 'Applications' menu and added a FIDO2 key. Setup involves providing a PIN which you'll want to take note of, as it'll be requested on use over each session. Outside of that, I also quickly ensured that the FIDO2 interface was set to enabled in the 'Interfaces' menu.

2. Passing the USB key to WSL2

Microsoft fortunately have a good write-up of how to connect a USB device to a WSL2 environment. The steps involve installing an open-source USB/IP project called usbipd-win as well as binding & attaching the USB device to WSL using its bus ID. I recommend going through these steps manually in the first instance and ensuring everything works by running lsusb within WSL Ubnutu.

lsusb
#Bus 001 Device 009: ID xxxx:xxxx Yubico.com Yubikey 4/5 OTP+U2F+CCID

All good thus far. But I did find out pretty quickly that the device gets dropped each time you unplug the Yubikey or restart windows. So, clearly a Windows task was needed to automate the above steps.

3. Creating a Powershell script to bind the key

First thing I did was write up a simple Powershell script to bind the USB key. Note that you'll need to replace the key ID (xxxx:xxxx) with the ID you obtained from running lsusb above.

$yubiKeyBusId = (usbipd list | Select-String "xxxx:xxxx" | ForEach-Object { $_.Line.Split()[0] })

if ($yubiKeyBusId) {
    usbipd attach --wsl --busid $yubiKeyBusId
    Write-Host "YubiKey attached to WSL with busid $yubiKeyBusId."
} else {
    Write-Host "YubiKey not found."
}

I then saved this off as 'YubiKeyAttachToWSL.ps1' for the Windows task.

4. Creating a task to auto-attach the USB key

Next, I had to find an appropriate trigger event for when the USB key was inserted, as the Windows task obviously needs to know when to run the above script.

After some testing using Windows Event Viewer, I managed to find a System logged event from 'WudfUsbccidDrv' with an event ID of 104. This seems to be an event to register the device driver and gets fired consistently when the USB is inserted. So after noting that down I went into Windows Task Scheduler and created the following custom task:

Name: YubiKey-USBConnectedOrLogOn-AttachToWSL

  • When running the task, use the following user account: [local admin account]
  • Run only when user is logged on: selected
  • Triggers:
    1. 'At log on' - Of [local admin account], with delay of task for 5 seconds
    2. 'On an event' - Log: System, Source: WudfUsbccidDrv, Event ID: 104, with delay of task for 5 seconds
  • Actions:
    1. 'Start a program' - Program: powershell.exe, Arguments: -ExecutionPolicy Bypass -File "C:\YubiKeyAttachToWSL.ps1"
  • Settings:
    • Allow task to be run on demand: selected
    • Run task as soon as possible after a scheduled start is missed: selected
    • Stop the task if it runs longer than: 30 seconds
    • If the running task does not end when requested, force it to stop: selected

I then saved the task off and tested it. First by manually running the task. Then by pulling and inserting the USB key. If everything works, you should be able to see the key disappear/reappear in the list of devices reported out by lsusb within your WSL environment.

5. Allowing non-root user to access the key

The job wasn't done there. For some reason, I could see the key was being correctly recognized in my WSL environment, but I couldn't actually use the key (e.g. by running ssh-keygen with a ed25519-sk key) unless running as the root user. To narrow down on the issue, I installed the 'yubikey-manager' and 'fido2-tools' packages on my Ubuntu environment and ran the below command (you shouldn't need to do this):

FIDO_DEBUG=1 fido2-token -L
#fido_hid_unix_open: open /dev/hidraw0: Permission denied
#fido_hid_unix_open: open /dev/hidraw1: Permission denied

So, it seems that while the device was being registered correct, it wasn't actually having permissions applied to the dev endpoints needed for my non-root user. So, as a quick test, I ran the below and rechecked the debug output:

sudo chmod a+rw /dev/hidraw0 /dev/hidraw1

FIDO_DEBUG=1 fido2-token -L
#/dev/hidraw1: vendor=0x1050, product=0x0407 (Yubico YubiKey OTP+FIDO+CCID)

Much better. But its not like I want to run this every time I start up WSL Ubuntu. So, I also created the below udev rule entry (using the correct key ID xxxx:xxxx), so that permissions could be correctly set:

sudo vi /etc/udev/rules.d/99-yubikey.rules

99-yubikey.rules

KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0666", TAG+="uaccess", GROUP="plugdev", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="xxxx"

At this point, everything seemed to be working well. But to make sure, I did another test by pulling the key, inserting, and checking it was registered and available for use by my non-root user.

6. Creating and using a hardware key via FIDO2 for SSH

So I finally got to the point of creating and using my key. Fortunately, Yubico provide a short guide of how to generate SSH credentials which allow authentication using FIDO2. With the focus being on use of ssh-keygen:

ssh-keygen -t ed25519-sk -O resident -O verify-required -C "Your Comment"

One thing that wasn't immediately obvious, was how to set up a key without requiring the Yubikey button to be touched. It may not be a common use-case, but I'd rather be able to pull/insert my Yubikey when I want to use it and have user presence implied by its connection state rather than needing to manually touch the key on each use.

Fortunately, I found that ssh-keygen does indeed offer a way to generate a key without needing user interaction on the device itself:

ssh-keygen -t ed25519-sk -O resident -O no-touch-required -C "Your Comment"

So, at this point I was able to run the above, generate my private and public keys, add my public key to the list of authorized keys on my local servers, and get successfully connected.

@tterrace-tau
Copy link

Thanks for this, it worked like a charm! I had to add another task trigger for Hyper-V-Worker:18500 because if WSL isn't automatically started on boot the script will run into:

usbipd: error: There is no WSL 2 distribution running; keep a command prompt to a WSL 2 distribution open to leave it running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment