因为我的电脑和服务器都很多,管理多台设备的 SSH 配置一直是个痛点。

以前的做法是手动复制私钥,然后还得维护一份差异巨大的 ~/.ssh/config,不安全又麻烦。

最近我重构了整套方案,达成了以下效果:

  1. 零私钥落地:私钥只存在于 Bitwarden 内存,硬盘上无私钥文件。
  2. 配置云同步:公钥和 Config 通过 OneDrive 同步。
  3. 路径大一统:利用 Symlink 抹平 Windows 和 macOS 的路径差异。

需要提前启用 Bitwarden 的 SSH Agent 功能,参考官方文档:Bitwarden SSH Agent

目录结构策略

私钥交给 Bitwarden 托管,我们只需要同步公钥和通用配置。

在 OneDrive 中建立如下结构:

OneDrive/
└── .../
└── ssh/
├── common_config # 配置文件
└── ssh-keys/
└── github.pub # 只有公钥

抹平路径差异

这是最关键的一步。

SSH 配置文件不支持环境变量(如 %OneDrive%),且不同电脑的云同步路径各异(D 盘、C 盘、/Users/...)。

解决方案是在每台电脑的 ~/.ssh/ 下创建一个名为 cloud_ssh 的软链接,指向真实的云同步目录。

Windows

cd ~/.ssh
New-Item -ItemType Junction -Path "cloud_ssh" -Target "...\ssh"

macOS / Linux

cd ~/.ssh
ln -s ".../ssh" cloud_ssh

现在,无论在哪个系统,~/.ssh/cloud_ssh 都是通用的路径。

SSH Config 配置

在本地的 ~/.ssh/config 中,只需要一行 Include:

Include ~/.ssh/cloud_ssh/common_config

然后在 OneDrive 里的 common_config 编写配置:

# example
Host github.com
User git
IdentityFile ~/.ssh/cloud_ssh/ssh-keys/github.pub
IdentitiesOnly yes

Host dev-server
HostName ip
User root
IdentityFile ~/.ssh/cloud_ssh/ssh-keys/dev.pub
IdentitiesOnly yes
ForwardAgent yes

这里 IdentityFile 指向的是 .pub 文件。在 SSH Agent 模式下,SSH 客户端会读取这个公钥的指纹,然后去 Bitwarden Agent 里请求对应的私钥。

ForwardAgent yes 允许你在远程服务器上直接使用本地 Bitwarden 托管的 SSH 密钥,而无需在服务器上存放任何私钥。

  • 场景:当你 SSH 到 A 服务器后,需要从 A 服务器 git clone 私有仓库或 SSH 到 B 服务器。

  • 安全性注意:仅在你信任的服务器上开启。如果服务器管理员具有 Root 权限,理论上他们可以利用已建立的 Socket 连接访问你的本地 Agent。

使用 SSH 密钥进行 Git 提交签名

既然已经实现了 SSH 密钥的统一管理,我们还可以更进一步:用 SSH 密钥代替传统的 GPG 密钥进行 Git 提交签名。

在终端执行以下命令,告诉 Git 使用 SSH 格式进行签名,并指向我们云端同步的公钥:

# 1. 设置签名格式为 ssh
git config --global gpg.format ssh

# 2. 指定用于签名的公钥路径
git config --global user.signingkey "~/.ssh/cloud_ssh/ssh-keys/github.pub"

# 3. (可选) 开启全局自动签名
git config --global commit.gpgsign true

为了让 GitHub 识别你的签名,需要将公钥再次上传,但类型不同:

  1. 打开 GitHub Settings -> SSH and GPG keys

  2. 点击 New SSH Key

  3. Key type 务必选择 Signing Key

  4. 粘贴 github.pub 中的内容并保存

你可以创建一个空提交来测试整个流程:

git commit --allow-empty -S -m "test: verify ssh signing"


# 然后查看提交详情,确认签名状态
git log --show-signature -1

现在可能会输出 No signature,这个问题是因为你已经成功签名了,但 Git 在本地尝试验证签名时,不知道该信任哪些公钥。

Git 需要一个名为 allowed_signers 的文件来充当白名单。

在 OneDrive 的 ssh 文件夹(也就是 cloud_ssh 软链接指向的源目录)里,新建一个文本文件,命名为 allowed_signers(注意不要有 .txt 后缀)。

在该文件中添加一行,格式为:<你的邮箱> <公钥内容>

me@exusiai.top ssh-ed25519 AAAAC3NzaC1lZDI1...

回到终端,执行以下命令告诉 Git 这个文件的位置:

git config --global gpg.ssh.allowedSignersFile "~/.ssh/cloud_ssh/allowed_signers"

现在重新运行查看命令:

git log --show-signature -1

你应该会看到类似这样的输出:

Good "git" signature for me@exusiai.top with ED25519 key SHA256:...

疑难杂症

Windows Git 的坑

在 Windows 上配置好后,ssh -T git@github.com 通了,但 git clone 可能会报 error in libcryptoPermission denied

这是因为 Git for Windows 默认使用内置的 MinGW SSH,它找不到 Bitwarden 的 Agent,也不支持指向 .pub 的配置。

强制 Git 使用 Windows 原生 OpenSSH 即可。

git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

core.sshCommand 类似,Windows 上的 Git 签名程序也需要强制指向系统原生的 ssh-keygen,否则会因为找不到 Bitwarden 的 Agent 管道而报错:

git config --global gpg.ssh.program "C:/Windows/System32/OpenSSH/ssh-keygen.exe"