1622 字
8 分钟
MacOS YubiKey SSH

MacOS使用 YubiKey (远程)验证 SSH#

  • Yubikey 5C NFC

背景 + 叠甲#

首先这大概率是重复造轮子的教程,网上到处都是Yubikey的使用教程。 但是部分只会告诉你答案,不会告诉你每一步在干嘛,受限于MacOS版本不同等原因,对于我这样的彩笔来说跑不通+完全没法debug。

目的#

  • 用于一般性验证SSH连接

  • Yubikey插在在本地机器,本地机器SSH远程连接到一台Host A时,在Host A上不加载(复制)公私钥的情况下验证SSH链接(其实大部分是Git验证)

选择#

FIDO2 + ed25519-sk

理由就是够用 简单 支持多个密钥 以及 通用性高

MacOS环境安装#

首先你需要安装brew版本的openssh 苹果版本自带的openssh编译时没加入支持硬件密钥的选项

Terminal window
brew install openssh
# yubikey的cli管理工具,可选安装
brew install ykman

如果使用系统自带的你会得到类似下面的报错

Terminal window
~ /usr/bin/ssh-keygen -t ed25519-sk -O resident -O application=ssh:yk5c-main -C "your_email@example.com" -f ~/.ssh/id_yk5c
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
No FIDO SecurityKeyProvider specified
Key enrollment failed: invalid format

此时意味着你有2个openssh,我们需要使用的是/opt/homebrew/bin/ssh

Terminal window
~ type -a ssh
ssh is /opt/homebrew/bin/ssh
ssh is /usr/bin/ssh

你可以使用下面命令来确认是否正确

Terminal window
which ssh

!!非常关键!! 你所有包含ssh的 ssh-* 的命令都需要使用注意一定是HomeBrew的版本,而不是苹果的默认/usr/bin/ssh* 下的任何二进制 所以你遇到任何问题,第一步先检查ssh命令执行路径

配置SSH公私钥#

Yubikey官网文档包含了大部分参数,建议自己阅读一遍,下面只是重复造轮子

我们需要 ed25519-sk 的密钥,这是固定的

有4个可选项 :

  • -O resident 让SSH注册到Yubikey内,你可以在FIDO2页面或者命令行ykman fido credentials list查看到,可以在任何机器上插入Yubikey,执行 ssh-add -K并输入PIN就能加载密钥

  • -O verify-required 每次使用都需要输入 PIN 码和进行实体触摸。

  • -O no-touch-required 无需要求触摸

  • -O application=ssh:$name 当搭配resident时,在FIDO页面显示的名字

下面是我的搭配,首先resident基本是必须,同时搭配application备注名字,但是我想要验证PIN,每次输入PIN码其实挺麻烦,需要多好多操作,下面的情况只需要触碰Yubikey进行一下确认即可

Terminal window
$(brew --prefix)/bin/ssh-keygen -t ed25519-sk -O resident -O application=ssh:yk5c-main -C "你的注释" -f ~/.ssh/id_yk5c

完成后就能看见你的SSH公私钥生成了,但是如果没有yubikey,这个私钥是无法用于验证的,因为这个密钥仅包含公钥和“密钥句柄”,真实密钥是被yubikey上出厂写入的AES密钥操作密钥句柄生成反馈的。参考链接

但是此时你使用这个公钥就能搭配Yubikey尝试登陆了。

SSH-AGENT 远程验证#

这是另外一个核心需求,我的部分编译工具以及服务都不运行在本地,都在其他Linux机器,当他们需要进行拉取Git以及作为跳板再链接其他机器调试时,把Yubikey物理插到那些机器上,不方便也小丑,所以需要ssh-agent,他会帮你代理转发并转发ssh key到远程,可以在远程再次验证。

核心参数/环境变量

  • SSH_AUTH_SOCK/SSH_AGENT_PID (环境变量) 用于ssh-agent的代理在ssh 远程时转发

你需要先禁用自带的服务

Terminal window
launchctl disable user/$UID/com.openssh.ssh-agent

当你使用ssh-agent -s他就会给出环境变量让你用于加载

Terminal window
~ $(brew --prefix)/bin/ssh-agent -s
SSH_AUTH_SOCK=/var/folders/7f/md23q0_j6xxxxxxycgc1111111gn/T//ssh-r2221dsadacxvO/agent.23603; export SSH_AUTH_SOCK;
SSH_AGENT_PID=23604; export SSH_AGENT_PID;
echo Agent pid 23604;

你可以手动复制上面ssh-agent -s输出的信息直接粘贴到命令行生效,或者用eval直接加载。同时你还用ssh-add需要导入你的私钥给ssh-agent

Terminal window
eval "$(ssh-agent -s)"
$(brew --prefix)/bin/ssh-add ~/.ssh/id_yk5c

同时使用ssh-add -L验证

Terminal window
~ $(brew --prefix)/bin/ssh-add -L
sk-ssh-ed25519@openssh.com AAxxxxxnNxxxxxWxxxxbg== your_email@example.com

此时你就可以远程连接其他Host了,但是你需要添加一个参数-A

Terminal window
$(brew --prefix)/bin/ssh -A root@xxxxxx

或者为你的~/.ssh/config的host下添加ForwardAgent yes

Host xxxxxxxx
User root
HostName 1.1.1.1
ForwardAgent yes

当进入远程Host时,同样ssh-add -L 你发现你以及能和上一面一样发现你的密钥成功转发到了远程机器

Debug: 如果你没有看到密钥并出现The agent has no identities.大概率是SSH_AUTH_SOCK/SSH_AGENT_PID变量加载有问题 你需要在MacOS上查看SSH_AUTH_SOCK/SSH_AGENT_PID变量是否为 /var/folders/xxx开头,这才是homebrew ssh-agent生成的,同时你可以看一下ssh-agent的进程路径对不对

ps aux | grep ssh-agent

确认加载密钥后当你远程使用yubikey私钥验证其他时会发现出现类似下面报错

signing failed: agent refused operation

原因出在另外一个关键要素/环境变量

  • SSH_ASKPASS

因为SSH(agent)需要去调用Yubikey验证,但是他并不知道应该去哪里调用,尤其是在远程主机时,所以你需要一个工具去帮你拉取MacOS的验证给Yubikey

这里的工具有好多,我选择是 https://github.com/theseal/ssh-askpass

Terminal window
brew install theseal/ssh-askpass/ssh-askpass

那么这时候你需要在上面配置SSH_AUTH_SOCK/SSH_AGENT_PID的同时配置SSH_ASKPASS的环境变量

Terminal window
export SSH_ASKPASS=$(brew --prefix)/bin/ssh-askpass
export DISPLAY=":0"

此时你再去尝试连接,就会出现下面的弹窗,我不用输入PIN,所以直接回车,此时你的Yubikey会闪烁,触碰就可以完成验证。 ![[../images/others/askkey.png]]

保持服务#

其实到上面已经完成需求,但是你加载的环境变量只能在当前终端有效,那么你需要加入到你的~/.zshrc让他能保持服务,简单脚本(未验证)如下,记得source ~/.zshrc

Terminal window
# Homebrew 安装路径
BREW_PREFIX="$(brew --prefix)"
# 使用 Homebrew 版本的 ssh-agent 启动(如果没启动)
if [ -z "$SSH_AUTH_SOCK" ] || ! pgrep -u "$USER" ssh-agent > /dev/null; then
eval "$($BREW_PREFIX/bin/ssh-agent -s)" > /dev/null
fi
# 自动加载 YubiKey 密钥
SSH_KEY="$HOME/.ssh/id_yk5c"
if [ -f "$SSH_KEY" ]; then
"$BREW_PREFIX/bin/ssh-add" "$SSH_KEY" 2>/dev/null
fi
export SSH_ASKPASS="$BREW_PREFIX/bin/ssh-askpass"
export DISPLAY=":0"

我自己的话是使用Oh-my-zsh里面的ssh-agent插件,你看了他的插件脚本就会发现本质上区别不大,但是你需要注意的是修改他的脚本,制定ssh-agent路径,不然默认似乎只会加载/usr/bin下的

Terminal window
plugins=(..... ssh-agent)
zstyle :omz:plugins:ssh-agent identities ~/.ssh/id_yk5c
zstyle :omz:plugins:ssh-agent helper ssh-askpass

另外他里面的zstyle :omz:plugins:ssh-agent agent-forwarding yes开启似乎会有问题,没空研究,加一嘴

参考#

https://blog.nathanhigley.com/posts/hardwaresshkeysonmacos/ https://swjm.blog/the-complete-guide-to-ssh-with-fido2-security-keys-841063a04252

MacOS YubiKey SSH
https://www.homelabproject.cc/posts/macos/macos-yubikey-ssh/
作者
Channing He
发布于
2025-10-13
许可协议
CC BY-NC-SA 4.0