应用 Yubikey 的N种方法

最近入手了一个Yubikey 5。这个U盘大小的小玩意就像是一把钥匙,一些需要输入传统密码的地方可以用插入Yubikey来代替。

我琢磨出了一些使用Yubikey装逼的方法,在这里做一些总结。

这并不是Yubikey的使用教程,只是一些另类的使用方法的总结。

本文所述内容,除特殊说明外,均使用了Yubikey的U2F(FIDO)功能,需要预先设置好并打开FIDO功能。

配置 PAM 模块

以下操作需要先安装pam-u2f包,并且需要预先配置好Yubikey的U2F(FIDO)功能。

添加密钥

pamu2fcfg工具添加密钥:

mkdir ~/.config/Yubico 
pamu2fcfg -o pam://hostname -i pam://hostname > ~/.config/Yubico/u2f_keys

触摸Yubikey以确认添加。

如果您系统的主机名发生变化,例如由于不同网络中的DHCP,您将无法登录。

为了防止这种情况,建议指定上述选项并替换hostname为实际的主机名。

如果您拥有多个密钥,可使用以下指令附加:

pamu2fcfg -o pam://hostname -i pam://hostname -n >> ~/.config/Yubico/u2f_keys

创建预设

新建文件/etc/pam.d/auth-yubikey并添加如下内容:

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并添加以下内容作为第一行

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密钥

    ssh-keygen -t ecdsa-sk
  • 生成Ed25519密钥

    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功能的主密钥。

创建主密钥
gpg --full-gen-key

GPG默认只能创建RSA、DSA/Elgamal类型的密钥,如果想创建ECC类型的密钥或者创建带A功能的主密钥可以添加--expert参数。

gpg --expert --full-gen-key

此处会提示:

请选择您要使用的密钥类型:
(1) RSA 和 RSA (默认)
(2) DSA 和 Elgamal
(3) DSA(仅用于签名)
(4) RSA(仅用于签名)
(7) DSA(自定义用途)
(8) RSA(自定义用途)
(9) ECC 和 ECC
(10) ECC(仅用于签名)
(11) ECC(自定义用途)
(13) 现存的密钥
(14)卡中现有密钥
您的选择是? 

这里可以选择11,然后直接创建一个带A功能的主密钥。这里我选择9,方便之后演示生成子密钥。

请选择您想要使用的椭圆曲线:
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
您的选择是?

第一个椭圆曲线对应的算法就是熟悉的Ed25519,生成的SSH公钥也是ssh-ed25519格式的,此处我选择这个。当然也可以选择其他的选项,我更喜欢25519,因为它的公钥很短。

接下来填写相关的信息,一个主密钥就创建好了。

如果主密钥需要转移到Yubikey中,它必须设置为永不过期。

创建子密钥

如果已经创建好A功能的主密钥,可以跳过此步骤。

GPG默认只能创建S和E功能的子密钥,如果想自定义子密钥,需要添加--expert参数。注意参数放的位置,必须放在--edit-key前面。

gpg --expert --edit-key <KEY_ID>

进入GPG命令行后输入addkey创建子密钥。反馈如下

请选择您要使用的密钥类型:
(3) DSA(仅用于签名)
(4) RSA(仅用于签名)
(5) ElGamal(仅用于加密)
(6) RSA(仅用于加密)
(7) DSA(自定义用途)
(8) RSA(自定义用途)
(10) ECC(仅用于签名)
(11) ECC(自定义用途)
(12) ECC(仅用于加密)
(13) 现存的密钥
(14)卡中现有密钥
您的选择是?

这里我们要创建一个具有A功能的子密钥,只能选择7、8或11。这里选择11。

ECDSA/EdDSA 密钥的可实现的功能: 签名(Sign) 身份验证(Authenticate) 
目前启用的功能: 签名(Sign) 

(S) 签名功能开关
(A) 身份验证功能开关
(Q) 已完成

您的选择是?

这里我们只需要A功能,建议仅开启A功能,关闭其他功能。如果需要其他的功能,最好创建单独的子密钥。

请选择您想要使用的椭圆曲线:
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
您的选择是?

根据喜好选择,我选择1。然后填写其他信息,带有A功能的子密钥就创建好了。

配置GPG-Agent和SSH验证

OpenSSH本身并不支持OpenPGP物理密钥的验证,需要借助GPG-Agent来进行。这个工具类似SSH-Agent,开启前最好先关闭SSH-Agent。

GPG带有默认启用的systemd用户Socket,会在需要的时候自动调用,并不需要手动启动服务。

打开GPG-Agent的SSH支持。打开~/.gnupg/gpg-agent.conf,添加一行文本:

enable-ssh-support

由于我们关闭了SSH-Agent,本意上是要GPG-Agent接管SSH-Agent的工作。如果配置了SSH-Agent的环境变量记得先删除。

添加环境变量,使默认的SSH-Agent指向GPG-Agent。编辑~/.pam_environment

SSH_AGENT_PID DEFAULT= 
SSH_AUTH_SOCK DEFAULT="${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh"

如果系统中安装了Gnome-Keyring,还需要停用其ssh组件,否则它将覆盖之前的环境变量。

~/.pam_environment中添加

GSM_SKIP_SSH_AGENT_WORKAROUND DEFAULT=1

然后,我们需要将GPG密钥的指纹添加到GPG-Agent中。

执行以下指令

gpg --with-keygrip -K

复制带有A标签的密钥的Keygrip值,将其放入~/.gnupg/sshcontrol文件中。

就好了。

应用GPG-Agent设置

最简单的方法是重启电脑。

获得SSH公钥

使用以下指令

gpg --export-ssh-key <KEY_ID>

即可导出SSH公钥,将其放到目标主机的~/.ssh/authorized_keys中即可,也可以添加到GitHub中。

下面都以GitHub为例来进行测试。

也可以通过GPG-Agent来获得公钥。执行

ssh-add -L

应该会获得和上一条指令一样的输出,这就说明配置成功了。可以连个GitHub测试一下了。

ssh git@github.com

应该会看到这样的提示:

Hi nwntech! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.

说明SSH密钥配置正确了。

转移密钥

转移主密钥到Yubikey中

先备份一下密钥

gpg --export-secret-key --armor <KEY_ID> > <文件名>

--armor参数代表导出ASCII格式的文本,参数的位置也必须要在密钥ID之前。

进入GPG命令行

gpg --edit-key <KEY_ID>

执行

toggle
keytocard

转移完后,本机不再包含真正的密钥,只有一个指示它存储在智能卡上的指针。你依然可以在列表中看到它,但每次操作主密钥时都会要求插入密钥并验证PIN。

转移子密钥到Yubikey中

此时拔除Yubikey,测试SSH的连接,发现还是可以连通的,不过等到转移完子密钥之后,就需要验证密钥和PIN了。

进入GPG命令行

gpg --edit-key <KEY_ID>

提示

gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

私钥可用。

sec  ed25519/97F30xxxxxxAC7DD
创建于:2021-08-22  有效至:永不       可用于:SC  
卡号: 0006 1xxxxxxx
信任度:绝对        有效性:绝对
ssb  cv25519/5346xxxxxx8AFC1
创建于:2021-08-22  有效至:永不       可用于:E   
ssb  ed25519/5553xxxxxxB50825
创建于:2021-08-22  有效至:永不       可用于:A   
[ 绝对 ] (1). xxxxx <x@x>

gpg> 

找到A功能所在子密钥的位置,即前缀为”ssb”的条目中A功能子密钥的顺序。如上,A功能的子密钥序号为2

选择子密钥。注意导入子密钥只能一个一个导入,如果不选择子密钥就会导入主密钥。

key 2

导入

keytocard

就完成了。接下来也可以将其他子密钥导入Yubikey中。

同样的,本机也不再包含子密钥,只有一个指示它存储在智能卡上的指针。

测试连接

此时再连接SSH时就应该要求插入物理密钥了。

小结

这个方法其实和我随插随用的想法有些区别,将其插入其他设备上使用时还是需要进行额外的配置,但是相比其他方法,只输入一次密码还是要比每次都输入密码要方便得多的。

在远程服务器上进行PAM验证

应该不会有人会这么做吧,啊?

参考资料

Universal_2nd_Factor - ArchWiki

Yubikey - ArchWiki

GnuPG#SSH_agent - ArchWiki

Importing keys - dev.Yubico

使用 GPG 密钥进行 SSH 认证 - HydricAcid


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!