Yubikey Passthrough on WSL2 With Full FIDO2 Support
I recently starting using Yubikeys both to store passkeys which allow me to do passwordless logins to websites like GitHub, and to SSH into remote servers with FIDO2.
I have a number of machines at home, but I spend the majority of my time using
a Windows 11 desktop computer running NixOS on WSL2 (in the past I’ve described
Windows 11 + my tiling window manager
komorebi
as the “desktop environment”
on top of my NixOS WSL2 shell).
Rarely if ever do I make SSH connections from Windows 11 directly; if I am making an SSH connection it is almost always from NixOS. This posed a problem for my adoption of FIDO2 SSH with my Yubikeys, because the process is not quite as simple as just passing through the USB Yubikey to the WSL2 VM.
Below I’ll outline the steps to get USB Yubikey passthrough to a NixOS WSL2 VM working with full FIDO2 support.
Although these steps specifically target NixOS, the underlying information can be used to produce the same result on the Linux distribution of your choice.
Prerequisites on Windows 11⌗
Get started by installing Yubikey Manager on Windows and making sure your Yubikey(s) are being recognized:
winget install -e --id Yubico.YubikeyManager
# Unfortunately, WinGet _still_ isn't able to place installed binaries in the $PATH reliably 🤦
❯ & "C:\Program Files\Yubico\YubiKey Manager\ykman.exe" info
Device type: YubiKey 5C
Serial number: XXXXXXXXXX
Firmware version: 5.4.3
Form factor: Keychain (USB-C)
Enabled USB interfaces: OTP, FIDO, CCID
Applications
OTP Enabled
FIDO U2F Enabled
FIDO2 Enabled
OATH Enabled
PIV Enabled
OpenPGP Enabled
YubiHSM Auth Enabled
Next, install usbipd-win
, which is
what we’ll use to do the USB passthrough to the WSL2 VM.
# For whatever reason, this one does seem to be added to the $PATH correctly 🤷
winget install usbipd
In an Administrator PowerShell Terminal, run usbipd list
and take note of the
BUSID
for your Yubikey, which we will need later:
❯ usbipd list
Connected:
BUSID VID:PID DEVICE STATE
9-4 1050:0407 USB Input Device, Microsoft Usbccid Smartcard Reader (WUDF) Not shared
Building and Loading a Custom WSL2 Linux Kernel⌗
Now comes the fun part.
In order to enable full USB passthrough with FIDO2 support, we need to compile a custom WSL2 Linux kernel, because the kernels released by Microsoft are for whatever reason missing a few key options that we need.
If you’ve already compiled your own WSL2 Linux Kernels before and are
comfortable with this process, you just need to go and enable HIDDEV
and
HIDRAW
and then recompile.
Otherwise, you can navigate to my
custom-wsl2-linux-kernel
project and download the latest release. This project pulls the latest version
of the offical
WSL2-Linux-Kernel released by
Microsoft, enables the required configuration options, builds the kernel on
GitHub Actions, and finally makes the resulting vmlinux
file available to
download.
If you are interested in building WSL2 Linux kernels with different options
enabled, you
can fork the project and commit your own edits to the config-wsl
file. GitHub
Actions will then start building the kernel for you and store the resulting
vmlinux
file as a build artifact for you to download.
Anyway, once you have your vmlinux
file, place it in a convenient directory
(I keep mine in $HOME
🤷) and then edit (or create) your ~/.wslconfig
file
on Windows 11 to instruct WSL2 to use this specific kernel when booting VMs:
[wsl2]
kernel=C:\\Users\\LGUG2Z\\vmlinux
Now make sure you’ve saved any work you’re doing in any WSL2 VMs and run wsl --shutdown
in a PowerShell prompt. Then wait for at least 10 seconds after
that command terminates before starting up your NixOS WSL2 VM again.
Configuring NixOS to Automatically Attach Your Yubikey⌗
Thankfully, now that we are back in NixOS land the rest of this tutorial is fairly declarative.
I must give a huge thank you to everyone who has been contributing to the
various threads about this on the
NixOS-WSL repo, in particular the
user terlar
who has put together a pending
PR which the Nix code
below is largely based on.
Start by creating a usbip.nix
file and storing it wherever feels best in your
flake repo:
{
config,
lib,
pkgs,
...
}:
with lib; let
usbipd-win-auto-attach = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/dorssel/usbipd-win/v3.1.0/Usbipd/wsl-scripts/auto-attach.sh";
hash = "sha256-KJ0tEuY+hDJbBQtJj8nSNk17FHqdpDWTpy9/DLqUFaM=";
};
cfg = config.wsl.usbip;
in {
options.wsl.usbip = with types; {
enable = mkEnableOption "USB/IP integration";
autoAttach = mkOption {
type = listOf str;
default = [];
example = ["4-1"];
description = "Auto attach devices with provided Bus IDs.";
};
};
config = mkIf (config.wsl.enable && cfg.enable) {
environment.systemPackages = [
pkgs.linuxPackages.usbip
pkgs.yubikey-manager
pkgs.libfido2
];
services.pcscd.enable = true;
services.udev = {
enable = true;
packages = [pkgs.yubikey-personalization];
extraRules = ''
SUBSYSTEM=="usb", MODE="0666"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", TAG+="uaccess", MODE="0666"
'';
};
systemd = {
services."usbip-auto-attach@" = {
description = "Auto attach device having busid %i with usbip";
after = ["network.target"];
scriptArgs = "%i";
path = [pkgs.linuxPackages.usbip];
script = ''
busid="$1"
ip="$(grep nameserver /etc/resolv.conf | cut -d' ' -f2)"
echo "Starting auto attach for busid $busid on $ip."
source ${usbipd-win-auto-attach} "$ip" "$busid"
'';
};
targets.multi-user.wants = map (busid: "usbip-auto-attach@${busid}.service") cfg.autoAttach;
};
};
}
Then, in the part of your flake that deals with your WSL configuration, import
the file and set the appropriate BUSID
for your Yubikey which we determined
earlier:
{
imports = [
# Your import path will probably be different
./usbip.nix
];
wsl = {
usbip = {
enable = true;
# Replace this with the BUSID for your Yubikey
autoAttach = ["9-4"];
};
};
}
Go ahead and rebuild your system with your updated NixOS configuration, and now whenever your Yubikey is detected in the port that you have identified, it will be passed through automatically to your NixOS VM. 🎉
We can test this by running ykman fido credentials list
inside of our WSL2 VM:
❯ ykman fido credentials list
Enter your PIN: ************
Credential ID RP ID Username Display name
abcdefgh... github.com LGUG2Z جاد
Great! You can now use your existing FIDO2 SSH keys from inside your WSL2 VM, or generate a new key by following the Linux setup instructions for “Securing SSH with FIDO2” on the Yubico website.
A few things to keep in mind:
- You’re probably going to want to keep two Yubikeys connected to your Windows machine simultaneously in order to use one in Windows and one in WSL2
- If you have both Yubikeys connected simultaneously, and you have set up Yubico Login for Windows, you’ll have to remove one of them when you are logging into the computer after a restart otherwise the login will fail
- Outside of this specific edge case, I haven’t encountered any other issues with keeping two Yubikeys connected simultaneously for daily use
- In order to use FIDO2 functionality, you need to explicitly set up a FIDO2 PIN on your Yubikey as one is not set by default
I’m Not Sure I Can Replicate This on Ubuntu…⌗
The main things to note if you are trying to adapt the NixOS configuration snippets above to work on other Linux distrubtions are:
- The packages being added to the system environment
- The
auto-attach.sh
script being pulled from theusbipd-win
project - The
udev
rules being set forusb
andhidraw*
- The
systemd
service which calls theauto-attach.sh
script and attaches the device on the givenBUSID
to the VM
Look, I’m not gonna lie, I probably couldn’t replicate this with any confidence on Ubuntu either.
If you’d like to try running NixOS on WSL2, please take a look at my
nixos-wsl-starter
template
which should have you up and running with a useable terminal-powered
development environment in less than 10 minutes.
There is even a video you can follow along with step by step.
If you have any questions you can reach out to me on Twitter and Mastodon.
If you’re interested in what I read to come up with solutions like this one, you can subscribe to my Software Development RSS feed.
If you’d like to watch me writing code while explaining what I’m doing, you can also subscribe to my YouTube channel.