最近入手了一个Yubikey 5。这个U盘大小的小玩意就像是一把钥匙,一些需要输入传统密码的地方可以用插入Yubikey来代替。
我琢磨出了一些使用Yubikey装逼的方法,在这里做一些总结。
这并不是Yubikey的使用教程,只是一些另类的使用方法的总结。
本文所述内容,除特殊说明外,均使用了Yubikey的U2F(FIDO)功能,需要预先设置好并打开FIDO功能。
配置 PAM 模块
以下操作需要先安装pam-u2f
包,并且需要预先配置好Yubikey的U2F(FIDO)功能。
添加密钥
用pamu2fcfg
工具添加密钥:
1 | mkdir ~/.config/Yubico |
触摸Yubikey以确认添加。
如果您系统的主机名发生变化,例如由于不同网络中的DHCP,您将无法登录。
为了防止这种情况,建议指定上述选项并替换hostname
为实际的主机名。
如果您拥有多个密钥,可使用以下指令附加: 我试过了,会有问题
1 | pamu2fcfg -o pam://hostname -i pam://hostname -n >> ~/.config/Yubico/u2f_keys |
创建预设
新建文件/etc/pam.d/auth-yubikey
并添加如下内容:
1 | auth sufficient pam_u2f.so nouserok origin=pam://hostname appid=pam://hostname |
这里的文件名可以随意修改,但必须在/etc/pam.d/
目录中。
请务必将其中的hostname
修改为自己的主机名!
ArchWiki中将上述条目添加到各个PAM配置文件的第一行。这里我们直接进行一个懒的偷,将这条比较长的文本写入一个单独的配置再进行include。
sudo 验证
在对您的配置进行任何更改之前,请使用ROOT权限(例如sudo -s
)启动一个单独的shell。
这样,如果出现问题,您可以还原任何更改。
设置
打开/etc/pam.d/sudo
并添加以下内容作为第一行
1 | auth include auth-yubikey # 注意这里需要填之前创建的文件名 |
使用
插入Yubikey并执行sudo
,Yubikey的指示灯会闪烁,屏幕上不会有提示信息。此时会有一个超时,触摸后命令被执行,若未触摸而超时,会继续要求输入密码。
如果没有此时没有插入设备,会要求输入密码。
这项设置也适用于其他调用sudo
操作的程序,比如yay
。
Polkit 验证
打开/etc/pam.d/polkit-1
,添加如上条目。
当弹出Polkit授权窗口时,Yubikey指示灯会闪烁,直接触摸可完成授权。
有一个不算BUG的BUG
闪烁时如果想输入密码,输入完毕后窗口会变灰等待Yubikey响应。
即一旦插入了Yubikey,就必须使用Yubikey授权。
解决方法很简单,拔掉密钥即可。
SDDM 登陆
即KDE的开机登陆界面。
打开/etc/pam.d/sddm
,添加如上条目。
与Polkit
不一样,这里有密码框但不会立刻触发Yubikey验证,需要敲击回车才能触发。可输入任意字符。
敲击回车后触摸可完成验证。
KDE5 锁屏验证
和SDDM长的一样但是它由不同的模块负责。
打开/etc/pam.d/kde
,添加相同条目。
效果与SDDM一致。
TTY 登陆
打开/etc/pam.d/system-login
,添加相同条目。
登陆TTY时输入完用户名不会要求输入密码,此处会触发Yubikey验证,触摸即可进入Shell。
LUKS 开机解密
参考我的另一篇博客。
SSH 密钥
SSH中Yubikey有多种用途,您想找的是:
- 为SSH密钥添加双因子认证
- 作为密钥进行SSH验证
- 在远程服务器上进行PAM验证
作为SSH密钥的双因子认证
这个简单。
需要客户端和服务端都支持ecdsa-sk/ed25519-sk
密钥类型,OpenSSH 8.2之后的版本都可以。
GitHub也支持ecdsa-sk/ed25519-sk
类型的公钥。
客户端需要安装libfido2
包。
生成密钥对
插入Yubikey,并执行下面的指令。
生成ECDSA密钥
1
ssh-keygen -t ecdsa-sk
生成Ed25519密钥
1
ssh-keygen -t ed25519-sk
Ed25519-sk密钥类型需要固件版本5.2.3
以上。
和正常生成密钥对一样,只是需要输入PIN并触摸。
使用私钥登陆
登陆时,私钥、私钥密码字段(如果有的话)、Yubikey缺一不可,即在传统的SSH密钥登陆上多了一个Yubikey验证的步骤,提高了安全性。
作为密钥进行SSH验证
Yubikey可以用作基于OpenPGP或PIV的SSH硬件密钥。即将硬件作为私钥载体,随插随用。
配置前需要打开Yubikey的PIV功能。
PIV
有两种方式:
- 一种是在Yubikey中生成自签证书,并使用这个证书进行证书登陆。这个方法涉及证书、CA以及SSH的证书验证方式,与传统的密钥验证有一定差别。关键的是,这种方法生成的公钥并不能用来登陆GitHub,暂时先不做研究。
- 另一种是使用PKCS#11进行密钥验证。这种证书生成方便,且兼容GitHub。美中不足的是,每次登陆都需要输入PIN。当然可以搭配SSH-Agent来实现只输入一次PIN就能多次使用的效果,但是和我的预想还是相去甚远。
OpenPGP密钥
需要使用具有A功能的密钥对,设置很方便,可以兼容传统的密钥登陆,而且可以即插即用,免密登陆(只需要验证第一次)。虽然我个人并不是很喜欢GPG,但是其他的方案都不是很符合我的要求,也只能妥协了。
那么,接下来介绍一下OpenPGP密钥的设置方法。
准备密钥
验证SSH连接需要一个具有A功能的密钥对。如果已经创建了主密钥,可以创建一个A功能的子密钥。如果还没有创建主密钥,可以直接生成一个带有A功能的主密钥。
创建主密钥
1 | gpg --full-gen-key |
GPG默认只能创建RSA、DSA/Elgamal类型的密钥,如果想创建ECC类型的密钥或者创建带A功能的主密钥可以添加--expert
参数。
1 | gpg --expert --full-gen-key |
此处会提示:
1 | 请选择您要使用的密钥类型: |
这里可以选择11,然后直接创建一个带A功能的主密钥。这里我选择9,方便之后演示生成子密钥。
1 | 请选择您想要使用的椭圆曲线: |
第一个椭圆曲线对应的算法就是熟悉的Ed25519,生成的SSH公钥也是ssh-ed25519
格式的,此处我选择这个。当然也可以选择其他的选项,我更喜欢25519,因为它的公钥很短。
接下来填写相关的信息,一个主密钥就创建好了。
如果主密钥需要转移到Yubikey中,它必须设置为永不过期。
创建子密钥
如果已经创建好A功能的主密钥,可以跳过此步骤。
GPG默认只能创建S和E功能的子密钥,如果想自定义子密钥,需要添加--expert
参数。注意参数放的位置,必须放在--edit-key
前面。
1 | gpg --expert --edit-key <KEY_ID> |
进入GPG命令行后输入addkey
创建子密钥。反馈如下
1 | 请选择您要使用的密钥类型: |
这里我们要创建一个具有A功能的子密钥,只能选择7、8或11。这里选择11。
1 | ECDSA/EdDSA 密钥的可实现的功能: 签名(Sign) 身份验证(Authenticate) |
这里我们只需要A功能,建议仅开启A功能,关闭其他功能。如果需要其他的功能,最好创建单独的子密钥。因为Yubikey的不同GPG插槽只具备一个功能,将密钥导入某个特定功能的插槽后,该密钥携带的其他功能将不可用。
1 | 请选择您想要使用的椭圆曲线: |
根据喜好选择,我选择1。然后填写其他信息,带有A功能的子密钥就创建好了。
配置GPG-Agent和SSH验证
OpenSSH本身并不支持OpenPGP物理密钥的验证,需要借助GPG-Agent来进行。这个工具类似SSH-Agent,开启前最好先关闭SSH-Agent。
GPG带有默认启用的systemd用户Socket,会在需要的时候自动调用,并不需要手动启动服务。
打开GPG-Agent的SSH支持。打开~/.gnupg/gpg-agent.conf
,添加一行文本:
1 | enable-ssh-support |
由于我们关闭了SSH-Agent,本意上是要GPG-Agent接管SSH-Agent的工作。如果配置了SSH-Agent的环境变量记得先删除。
添加环境变量,使默认的SSH-Agent指向GPG-Agent。编辑~/.pam_environment
。
1 | SSH_AGENT_PID DEFAULT= |
如果系统中安装了Gnome-Keyring,还需要停用其ssh组件,否则它将覆盖之前的环境变量。
在~/.pam_environment
中添加
1 | GSM_SKIP_SSH_AGENT_WORKAROUND DEFAULT=1 |
然后,我们需要将GPG密钥的指纹添加到GPG-Agent中。
添加指纹非必要步骤。添加后如未插入Yubikey,系统会提示插入。不添加则不会。
执行以下指令
1 | gpg --with-keygrip -K |
复制带有A标签的密钥的Keygrip值,将其放入~/.gnupg/sshcontrol
文件中。
就好了。
应用GPG-Agent设置
最简单的方法是重启电脑。
获得SSH公钥
使用以下指令
1 | gpg --export-ssh-key <KEY_ID> |
即可导出SSH公钥,将其放到目标主机的~/.ssh/authorized_keys
中即可,也可以添加到GitHub中。
下面都以GitHub为例来进行测试。
也可以通过GPG-Agent来获得公钥。执行
1 | ssh-add -L |
应该会获得和上一条指令一样的输出,这就说明配置成功了。可以连个GitHub测试一下了。
1 | ssh [email protected] |
应该会看到这样的提示:
1 | Hi nwntech! You've successfully authenticated, but GitHub does not provide shell access. |
说明SSH密钥配置正确了。
转移密钥
转移主密钥到Yubikey中
先备份一下密钥
1 | gpg --export-secret-key --armor <KEY_ID> > <文件名> |
--armor
参数代表导出ASCII格式的文本,参数的位置也必须要在密钥ID之前。
进入GPG命令行
1 | gpg --edit-key <KEY_ID> |
执行
1 | toggle |
转移完后,本机不再包含真正的密钥,只有一个指示它存储在智能卡上的指针。你依然可以在列表中看到它,但每次操作主密钥时都会要求插入密钥并验证PIN。
转移子密钥到Yubikey中
此时拔除Yubikey,测试SSH的连接,发现还是可以连通的,不过等到转移完子密钥之后,就需要验证密钥和PIN了。
进入GPG命令行
1 | gpg --edit-key <KEY_ID> |
提示
1 | gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc. |
找到A功能所在子密钥的位置,即前缀为"ssb"的条目中A功能子密钥的顺序。如上,A功能的子密钥序号为2
选择子密钥。注意导入子密钥只能一个一个导入,如果不选择子密钥就会导入主密钥。
1 | key 2 |
导入
1 | keytocard |
就完成了。接下来也可以将其他子密钥导入Yubikey中。
同样的,本机也不再包含子密钥,只有一个指示它存储在智能卡上的指针。
测试连接
此时再连接SSH时就应该要求插入物理密钥了。
小结
这个方法其实和我随插随用的想法有些区别,将其插入其他设备上使用时还是需要进行额外的配置,但是相比其他方法,只输入一次密码还是要比每次都输入密码要方便得多的。
在远程服务器上进行PAM验证
应该不会有人会这么做吧,啊?