Bitwarden 桌面客户端可以充当 SSH Agent,让 SSH 私钥保存在 Bitwarden 保险库中,而不是直接存放在 ~/.ssh 目录。

在 Windows 中启用 Bitwarden SSH Agent 后,PowerShell 可以直接使用其中的密钥。但 WSL2 中的 Linux 程序无法直接访问 Windows 的命名管道,因此需要借助 npiperelay 和 socat 建立一座桥梁。

本文介绍如何在 WSL2 中使用 Windows 上的 Bitwarden SSH Agent,同时解决新版 WSL 中运行 ss -a 时出现的:

RTNETLINK answers: Invalid argument

工作原理

整个链路如下:

WSL2 中的 ssh/git
        │
        ▼
~/.ssh/agent.sock
        │
      socat
        │
   npiperelay.exe
        │
        ▼
\\.\pipe\openssh-ssh-agent
        │
        ▼
Windows Bitwarden SSH Agent

WSL2 中的 SSH 客户端连接 Unix Socket,socat 和 npiperelay 再把请求转发到 Bitwarden 提供的 Windows OpenSSH Agent 命名管道。

私钥仍由 Bitwarden 管理,WSL2 只是把签名请求转发给 Bitwarden。

一、配置 Bitwarden SSH Agent

打开 Bitwarden 桌面客户端,在设置中启用 SSH Agent:

Settings → SSH agent → Enable SSH agent

确保保险库中已经保存了至少一把 SSH 密钥,并保持 Bitwarden 处于解锁状态。

如果 Windows 的 OpenSSH Authentication Agent 服务正在运行,还需要将其停止并禁用,避免和 Bitwarden 使用相同的 Agent 接口。

以管理员身份打开 PowerShell:

Stop-Service ssh-agent
Set-Service ssh-agent -StartupType Disabled

然后在普通 PowerShell 中测试:

ssh-add -L

如果能够看到 Bitwarden 中保存的公钥,说明 Windows 侧已经配置成功。具体设置也可以参考 Bitwarden SSH Agent 官方文档。

如果这里无法列出密钥,应该先检查 Bitwarden 设置,而不是继续排查 WSL2。

二、安装 npiperelay

npiperelay 用于让 WSL2 程序访问 Windows 命名管道。

在 PowerShell 中使用 Chocolatey 安装:

choco install npiperelay

默认安装路径通常为:

C:\ProgramData\chocolatey\lib\npiperelay\tools\npiperelay.exe

在 WSL2 中对应的路径是:

/mnt/c/ProgramData/chocolatey/lib/npiperelay/tools/npiperelay.exe

也可以使用 Winget:

winget install albertony.npiperelay

不过 Winget 的安装路径会包含用户名和包版本,需要根据实际位置修改后面的脚本。

三、安装 socat

进入 WSL2,安装 socat:

sudo apt update
sudo apt install socat

socat 将负责创建 Linux Unix Socket,并把 Socket 中的数据交给 npiperelay。

在 WSL 2.7.8 中运行:

ss -a

可能会看到:

RTNETLINK answers: Invalid argument

这不是 Bitwarden、SSH Agent 或网络路由配置错误,而是新版 WSL 内核与 Ubuntu 自带旧版 iproute2 之间的兼容问题。

相关环境通常是:

WSL:2.7.8
内核:6.18.33.1-microsoft-standard-WSL2
iproute2:6.1.0

Linux 6.16 已经移除了 DCCP Socket 实现,而旧版 ss 在执行广泛的 Socket 查询时仍然会探测 DCCP。新内核对该请求返回 EINVAL,旧版 ss 随后将其显示为 RTNETLINK 错误。

根据 microsoft/WSL#40774 中的说明,上游 iproute2 7.0.0 已经能够正确处理这种情况。

截至本文编写时,上游最新稳定版本是 7.1.0,因此直接编译安装 7.1.0。kernel.org iproute2 发布目录

安装编译工具

sudo apt install build-essential pkg-config bison flex

编译 iproute2 7.1.0

cd /tmp

curl -LO https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-7.1.0.tar.xz
tar -xf iproute2-7.1.0.tar.xz
cd iproute2-7.1.0

./configure
make -j"$(nproc)"

这里不需要替换 Ubuntu 软件包中的全部程序,只安装新版本的 ss:

sudo install -m 0755 misc/ss /usr/local/sbin/ss
hash -r

检查当前使用的程序和版本:

type -a ss
ss -V

预期可以看到:

ss utility, iproute2-7.1.0

最后测试:

ss -a >/dev/null

正常情况下不应再出现:

RTNETLINK answers: Invalid argument

如果不想手动编译,也可以暂时忽略这个警告,或者等待 Ubuntu 软件源更新 iproute2。

五、创建 SSH Agent 转发脚本

创建脚本目录:

mkdir -p ~/scripts
mkdir -p ~/.ssh
chmod 700 ~/.ssh

创建文件:

nano ~/scripts/agent-bridge.sh

写入以下内容:

#!/usr/bin/env bash

export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"

if ! ss -a | grep -Fq "$SSH_AUTH_SOCK"; then
    rm -f "$SSH_AUTH_SOCK"

    (
        setsid socat \
            UNIX-LISTEN:"$SSH_AUTH_SOCK",fork \
            EXEC:"/mnt/c/ProgramData/chocolatey/lib/npiperelay/tools/npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork &
    ) >/dev/null 2>&1
fi

该脚本会:

  1. 将 SSH_AUTH_SOCK 指向 WSL2 中的 Unix Socket。
  2. 检查 Socket 是否已经存在。
  3. 删除失效的 Socket 文件。
  4. 通过 socat 创建新的 Unix Socket。
  5. 使用 npiperelay 连接 Bitwarden 的 Windows SSH Agent。

如果 npiperelay.exe 不在 Chocolatey 默认目录,需要将脚本中的路径替换为实际安装路径。

这一转发方案参考自 Bitwarden ssh-agent on WSL2。

为脚本添加执行权限:

chmod +x ~/scripts/agent-bridge.sh

六、随 Shell 自动启动

如果使用 Bash,在 ~/.bashrc 末尾添加:

source ~/scripts/agent-bridge.sh

如果使用 Zsh,则添加到 ~/.zshrc:

source ~/scripts/agent-bridge.sh

重新打开终端,或者立即加载配置:

source ~/.bashrc

七、测试 SSH Agent

在 WSL2 中运行:

ssh-add -l

如果配置成功,应该能够看到 Bitwarden 中保存的 SSH 密钥指纹。

查看完整公钥:

ssh-add -L

也可以测试 GitHub SSH 认证:

ssh -T git@github.com

第一次连接时可能需要确认 GitHub 的主机指纹。随后 Bitwarden 会根据授权设置弹出确认窗口。

配置成功后,下面这些操作都可以直接使用 Bitwarden 中的 SSH 密钥:

git clone git@github.com:username/repository.git
git pull
git push
ssh user@example.com

常见问题

PowerShell 中也无法执行 ssh-add -L

先检查:

Windows 侧无法使用时,WSL2 转发也不会成功。

WSL2 中提示无法连接 Agent

检查环境变量:

echo "$SSH_AUTH_SOCK"

预期结果类似:

/home/username/.ssh/agent.sock

检查 Socket:

ls -l "$SSH_AUTH_SOCK"

检查 socat 进程:

pgrep -af socat

找不到 npiperelay.exe

检查脚本中的路径:

ls -l /mnt/c/ProgramData/chocolatey/lib/npiperelay/tools/npiperelay.exe

如果通过 Winget 安装,可以在 Windows PowerShell 中查找:

Get-Command npiperelay.exe

然后将 Windows 路径转换为 /mnt/c/... 形式。

确认实际调用的是新版本:

type -a ss
command -v ss
ss -V

/usr/local/sbin/ss 应该排在 /usr/bin/ss 前面。

如果 Shell 仍然缓存旧路径,可以执行:

hash -r

然后再次运行:

ss -a

总结

通过 socat 和 npiperelay,可以把 WSL2 中的 Unix Socket 转发到 Windows Bitwarden SSH Agent。这样,WSL2 中的 SSH 和 Git 可以直接使用 Bitwarden 管理的密钥,无需在 Windows 与 Linux 之间复制私钥。

对于 WSL 2.7.8 中出现的 RTNETLINK answers: Invalid argument,真正的原因是新内核与旧版 ss 不兼容。安装 iproute2 7.0.0 或更高版本即可解决;本文使用当前最新稳定版 7.1.0。