使用FIDO2的安全密钥来进行SSH登录与Git提交

Arc 发布于 2025-03-24 161 次阅读


起因

突发奇想,花了巨大的价钱在淘宝购买了一个Yubikey Bio-C安全密钥(这个密钥支持使用指纹识别来替换掉PIN码输入的流程),用来作为每次都得输入PIN码的Yubikey 5C-NFC安全密钥的备份。

到货后才发现是FIDO2 Edition,上网查询后发现多协议版本目前还不对公众市场开放。而且一般来说,只支持FIDO2特性的安全密钥会比较便宜。又不太想退货,后来发现其实OpenSSH8.3版本就已经支持了使用FIDO2来进行SSH登录。Yubico已经发布了一个教程来告诉大家怎么操作,不过我现在再用中文阐述一遍这个流程。

一些声明

首先你要对FIDO2有个大致的了解。

我直接从Yubico的官网上复制一些话来进行描述:

FIDO2

以 8.2p1 为起点,OpenSSH 增加了对使用 FIDO2 凭证进行注册和身份验证的支持。OpenSSH 支持可发现凭据和不可发现凭据,允许使用安全密钥和 YubiKey,包括 YubiKey 生物密钥。FIDO2 凭证允许简化 YubiKey 的注册和身份验证体验。

有关使用 FIDO2 和 YubiKey 设置 SSH 的说明,请访问:使用 FIDO2 保护 SSH

FIDO2 的优势

FIDO2 允许在 YubiKey 本身内直接生成密钥,无需 PKI 框架或其他应用程序来生成密钥。OpenSSH 直接与 YubiKey 上的 FIDO2 函数连接,允许它选择密钥算法,以及生成可发现和不可发现的凭据。

由于支持可发现和不可发现的 FIDO2 凭证,FIDO2 允许为本地安全端点提供 SSH 选项,除了凭证应保留在 YubiKey 上的公有开放端点外,还允许使用。

FIDO2 的缺点

截至本页的上次更新,Windows 尚不支持 SSH 上的 FIDO2。(现在已经支持了)

FIDO2 基于去中心化部署,没有中央服务器颁发或撤销密钥。必须单独删除密钥,以便在 YubiKey 丢失或被盗时撤销访问权限。

总结

注意这里面提到的缺点:密钥必须单独删除。这意味着你如果只用FIDO2进行服务器访问,那么密钥一丢就全完了。所以最好保留第二种访问服务器的方式(比如我就用老的Yubikey 5C-NFC和一些其他的方式作为备份)。

顺便说一下我的看法:我觉得使用FIDO2来取代PIV访问性价比更高,因为支持PIV的多协议版本一般来说价格更贵。

通用流程

升级SSH版本

首先得确保SSH版本足够高(大于等于8.3版本),从而支持这个FIDO2的特性。至于版本我们可以用以下命令进行检查:

ssh -V

否则我们需要升级。一般来说,在Ubuntu上我们直接用以下命令升级:

apt-get upgrade ssh

生成FIDO2的公钥和私钥

执行命令

ssh-keygen -t ed25519-sk -O resident -O application=ssh:Arc_servers -O verify-required
-C "root@Arc_servers"

这里application的服务你可以自己起个名字,我这里就叫ssh:Arc_servers代表这个可以登录我所有的服务器。Comment是密钥的注释,方便你在一堆密钥中分清哪个是哪个。

随后他会在你指定的文件夹(对于Windows来说,一般是C:\Users\username/.ssh/里,这里的username就是你的用户名,有时候只有前几位)生成默认文件名id_ed25519_sk的私钥和id_ed25519_sk.pub的公钥。这个私钥文件就不需要进行特别的保护了,因为其只是一个接口,直接对应到了安全密钥上(所以我就没有设置Passphrase),从而不用担心文件本身被破解(如果是老方法直接在本机生成密钥,一个很弱的PIN码或者没有PIN码会让人觉得很不安全)。

在登录服务器时使用FIDO2

添加公钥

一般来说,我们直接在root用户里面添加公钥就行了。具体来说,就是打开/root/.ssh/authorized_keys这个文件,在里面新加一行,内容是上面提到的id_ed25519_sk.pub的内容就可以了。

修改本地config文件

为了登录方便,我们直接在本地的SSH Config里面添加好对应的信息。这个文件一般在C:\Users\username\.ssh\config这个位置。

文件内容大致如下:

Host Your_Server_Name
    HostName ip1.ip2.ip3.ip4
    Port 22
    User root
    IdentityFile C:\Users\username\.ssh\id_ed25519_sk

这样就可以直接用VSCode登录连接了。其实这后半段流程和正常加个普通的安全密钥基本上是一回事。

在Git里使用FIDO2

虽然我们已经在服务器登录中使用了FIDO2来实现无密码登录(我这个安全密钥是扫指纹的),当然也可以在Git中使用安全密钥来进行登录。Yubico给了一个教程,不过我们一样稍微简单的叙述一下流程。

考虑到其实大家一般用Git都是连接到Github,所以我们就针对这个来进行简单讲解。

添加SSH认证密钥与签名密钥

打开网址,我们把通用流程中生成的FIDO2 私钥保存到里面。我们可以选择类型,不过为了方便之后步骤我们把这个私钥既设置成签名,又设置成认证(也就是存两遍)。

随后我们直接测试是否可以登录:

$ ssh -T [email protected]

Confirm user presence for key ED25519-SK SHA256:e6a//1234567
User presence confirmed
Hi username! You've successfully authenticated, but GitHub does not provide shell access.

一般来说这样就至少设置成功了。为什么在这里我们不需要进行额外的其他设置呢?这应该也算是一个FIDO2的特性(也就是类似SSH里添加多个密钥进行交互,其实就是一个个比对)。所以直接就设置好了。

修改本地Git设置来支持本地Git签名

考虑到另一个需求:我们要支持类似PGP的签名方式,所以还是得操作一下。

以下操作是在Ubuntu上实现的。

首先升级Git到版本2.34,升级方法和之前说的SSH一样,查询版本用命令git --version

修改全局Git签名格式到SSH(一般来说就改全局,方便):

git config --system gpg.format ssh

随后把之前的FIDO2 私钥复制到/root/.ssh/id_ed25519_sk里面。

设置正确的密钥权限:

chmod 0600 /root/.ssh/id_ed25519_sk

设置全局Git签名密钥位置:

git config --system user.signingKey ~/.ssh/id_ed25519_sk

添加可信验证者文件,简单来说类似下面的操作:

$ cat ~/.ssh/allowed_signers

[email protected] [email protected] AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJhUXYvdwz3Dx45bWNmxHs1R21mlUm0o63+s4iCzRoFeAAAACnNzaDpnaXRodWI= user@host

保存该文件为全局验证者设置:

git config --system gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

使用ssh-agent进行远程服务器签名(可选)

我们假设本机是Server A插着安全密钥,我们通过SSH连接到了机器Server B上,现在我们要在Server B上写代码并使用安全密钥签名提交到Github上面。那么我们就需要一些额外的配置来完成这些。

首先仍然需要上一步的设置。

Server A(假设是Windows操作系统)上自动启动ssh-agent同时添加合适的密钥:

管理员启动PowerShell

# 自动启动设置
Set-Service ssh-agent -StartupType Automatic
# 启动服务
Start-Service ssh-agent
# 添加安全密钥
ssh-add $env:USERPROFILE\.ssh\id_ed25519_sk
# 验证加没加上去
ssh-add -L

Server A(假设是Linux系统)上设置ssh-agent同时加入到~/.zshrc之类的rc文件或者profile文件实现登录自动启动:

if [ -z "$SSH_AUTH_SOCK" ]; then
   eval $(ssh-agent -s)
   ssh-add -l >/dev/null || ssh-add ~/.ssh/id_ed25519_sk
fi

加完之后可以使用类似source ~/.profile来执行。

Server A上设置自动同步ssh-agent

~/.ssh/config中,在开头添加如下语句:

Host *
    ForwardAgent yes

这样就不需要所谓的ssh -A了,默认添加上去。

Server B上默认开启ssh-agent转发:

打开/etc/ssh/sshd_config,找到这句话并取消注释(没有的话直接加上去)

AllowAgentForwarding yes

然后我们使用VSCode连接没有打开ssh-agentServer B,观测效果:

$ ssh-add -L

[email protected]  1234567 root@Arc_servers

这里就验证了ssh-agent被成功转发了上去。

设置Git自动签名(可选)

VSCode里,Git签名默认不是自动打开的。当然在命令行中使用Git也不是。我们来开启这些功能来更加自动化。

命令行:

git config --system commit.gpgsign true

VSCode

使用Ctrl+,打开设置界面,搜索gpg并打开如下功能:

Git: Enable Commit Signing
Enables commit signing with GPG, X.509, or SSH.

这样就实现自动化签名了。

验证是否可以签名

考虑到用一堆命令行把事情变得很复杂,我们直接用VSCode来尝试进行测试。

首先在Github建个空的仓库,然后拉下来。 随便写点东西提交,看看是否签名完成就行。

一些彩蛋

可以考虑在VSCode中关闭以下功能:

Git: Autofetch
When set to true, commits will automatically be fetched from the default remote of the current Git repository. Setting to all will fetch from all remotes.

这个功能会经常让你去碰你的安全密钥,我觉得没必要打开。等到要操作的时候手工拉一下就行。

It is my final heart.
最后更新于 2025-03-24