在 WSL2 下使用 YubiKey

Mar 16 · 5min

WSL2 运行在虚拟机中,无法直接访问宿主机的 USB 设备。要在 WSL2 里使用 YubiKey,需要借助 usbipd-win 将 USB 设备从 Windows 转发到 WSL2,再在 WSL2 中安装相应的工具来与 YubiKey 交互。

安装 usbipd-win

在 Windows 端安装 usbipd-win,用于将 USB 设备共享到 WSL2:

winget install usbipd

安装 WSL2 端依赖

WSL2 端需要安装 USB/IP 客户端工具和智能卡相关的包(这里以 Arch Linux 为例):

paru -S usbutils pcsclite ccid opensc yubikey-manager

启用 pcscd 服务:

sudo systemctl enable --now pcscd.socket

需要在 /etc/wsl.conf 中设置 [boot] systemd=true 以使用 systemd。

配置 polkit 授权

pcscd 默认只允许 root 用户访问智能卡设备。需要添加 polkit 规则允许 wheel 组的用户访问:

// /etc/polkit-1/rules.d/99-pcscd.rules
polkit.addRule(function(action, subject) {
    if ((action.id == "org.debian.pcsc-lite.access_pcsc" ||
         action.id == "org.debian.pcsc-lite.access_card") &&
        subject.isInGroup("wheel")) {
        return polkit.Result.YES;
    }
});

如果你的用户不在 wheel 组中,将 "wheel" 替换为你所在的组。

转发 YubiKey 到 WSL2

在 Windows 端(需要管理员权限)列出所有 USB 设备:

usbipd list

输出类似如下:

Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-1    1050:0407  USB Input Device, Microsoft Usbccid Smartcard Reader (WUDF)   Not shared
2-1    046d:c52b  Logitech USB Input Device, USB Input Device                   Not shared
...

其中 VID:PID 列的 VID(Vendor ID)是 USB 厂商标识,1050 是 Yubico 的 VID。找到对应的 Bus ID 后,绑定并附加到 WSL2:

usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>

但 Bus ID 会随插入的 USB 口变化。更好的方式是用脚本自动识别 YubiKey 并附加,创建 ~\.local\scripts\attach-yubikey.ps1:

# 创建目录
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.local\scripts"
# ~\.local\scripts\attach-yubikey.ps1
$device = usbipd list | Select-String "1050:" | ForEach-Object {
    if ($_ -match "^(\S+)") { $matches[1] }
}

if ($device) {
    usbipd bind --busid $device 2>$null
    usbipd attach --wsl --busid $device --auto-attach
} else {
    Write-Host "YubiKey not found"
}

--auto-attach 会持续监听设备插拔并自动重新附加。可以通过 Windows 计划任务实现开机自启:

$scriptPath = "$env:USERPROFILE\.local\scripts\attach-yubikey.ps1"
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-WindowStyle Hidden -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "Auto Attach YubiKey to WSL" `
    -Action $action -Trigger $trigger -RunLevel Highest

在 WSL2 中验证设备是否可见:

$ lsusb | grep -i yubi
Bus 001 Device 002: ID 1050:0407 Yubico.com Yubikey 4/5 OTP+U2F+CCID

配置 GPG 智能卡

如果你的 YubiKey 配置了 OpenPGP,可以通过 GPG 使用它。

在 WSL2 中,scdaemon 默认通过 CCID 直连 USB 设备,但这条路径在 USB 转发场景下通常无法工作。需要让它改走 pcscd:

# ~/.gnupg/scdaemon.conf
disable-ccid

重启 GPG 相关服务:

gpg-connect-agent "scd killscd" /bye
gpg-connect-agent reloadagent /bye

然后验证:

gpg --card-status

如果能正确显示卡片信息,说明 YubiKey 已经可以使用。

将 GPG 用作 SSH Agent

YubiKey 上的 Authentication 子密钥可以用作 SSH 密钥。启用 GPG Agent 的 SSH 支持:

# ~/.config/environment.d/20-gpg-agent.conf
SSH_AUTH_SOCK=${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh

在 GPG Agent 配置中启用 SSH 支持:

# ~/.gnupg/gpg-agent.conf
enable-ssh-support

重启 GPG Agent:

gpg-connect-agent reloadagent /bye

验证 SSH 密钥是否可用:

ssh-add -L

常见问题

GPG 提示 "No such device"

scdaemon 默认使用 CCID 直连,在 WSL2 的 USB 转发场景下不可用。确认 ~/.gnupg/scdaemon.conf 中已添加 disable-ccid,然后重启服务:

gpg-connect-agent "scd killscd" /bye
gpg-connect-agent reloadagent /bye

GPG 提示 "Service is not running"

pcscd 服务未启动,或者通过 socket 激活未能成功拉起。手动启动服务:

sudo systemctl start pcscd.service

如果 pcscd 已在运行但仍然报错,说明是 polkit 授权问题。确认 /etc/polkit-1/rules.d/99-pcscd.rules 中同时包含了 access_pcsc 和 access_card 两个 action 的授权,参考上文配置 polkit 授权章节。

pcscd 日志提示 "LIBUSB_ERROR_BUSY"

pcscd 无法打开设备,因为其他进程(通常是之前 sudo gpg 残留的 root scdaemon)占用了 USB 接口。先释放设备再重启:

# 杀掉所有 scdaemon
sudo gpg-connect-agent "scd killscd" /bye
gpg-connect-agent "scd killscd" /bye

# 重启 pcscd
sudo systemctl restart pcscd.service

pcscd 无法识别 YubiKey

确认 ccid 驱动已安装,检查 USB 设备是否已正确附加:

lsusb | grep -i yubi

如果设备不可见,回到 Windows 端重新执行 usbipd attach。

> comment on mastodon / twitter
>