使用 YubiKey 解密 LUKS 分区
年前入手了两枚 YubiKey 5C NFC,一开始只是用于一些网站的双因素认证,后来发现 YubiKey 还可以用于解密 LUKS 加密的分区。这样就可以实现在启动时需要插入 YubiKey 才能解锁硬盘,在虚拟机了试了一下,发现效果很好!今天来记录一下如何使用 YubiKey 解密 LUKS 加密的分区。
首先准备材料:
- 一个安装好的 Arch Linux 系统
- 一枚支持 FIDO2 的 YubiKey
理论上任何 Linux 发行版都可以,但一些包名可能不一样。而且由于是基于 Linux 内核,是可以实现加密整个根分区的。
如果你只使用密码解密 LUKS 加密的分区,也可以不需要 YubiKey。这篇文章也包含了使用密码解密 LUKS 加密的分区的方法。
此外,这种方法不仅仅可以加密块设备,也可以使用在文件上,将一个文件作为块设备加密,就像 swapfile 一样。
基本介绍
按照惯例,首先介绍一下 LUKS 和 dm-crypt。
根据 Arch Linux Wiki - dm-crypt 的介绍,LUKS 是 Linux Unified Key Setup ( Linux 统一密钥设置) 的缩写。LUKS 是一个磁盘加密规范,它使用 dm-crypt 作为后端。LUKS 通过在磁盘上存储加密密钥的加密形式来实现磁盘加密。LUKS 通过使用一个或多个密钥槽来存储密钥,这些密钥槽可以使用不同的密钥进行填充。密钥槽的数量和密钥的类型是固定的,但是密钥的数量是可变的。
再来看看 dm-crypt,dm-crypt 是 Linux 内核中的透明块设备加密子系统,它被实现为设备映射器目标,并且可以堆叠在其他设备映射器转换之上。因此,它可以加密整个磁盘(包括可移动介质)、分区、软件 RAID 卷、逻辑卷以及文件。它显示为块设备,可用于架设文件系统、交换分区或 LVM 物理卷。
也就是说,用户不需要关心加密的细节,一旦解密,只需要像普通块设备一样使用即可。引用一段维基百科的内容:
dm-crypt is a transparent block device encryption subsystem in Linux kernel versions 2.6 and later and in DragonFly BSD. It is part of the device mapper (dm) infrastructure, and uses cryptographic routines from the kernel's Crypto API. Unlike its predecessor cryptoloop, dm-crypt was designed to support advanced modes of operation, such as XTS, LRW and ESSIV, in order to avoid watermarking attacks.[1] In addition to that, dm-crypt addresses some reliability problems of cryptoloop.
例如,我将 /dev/sda
这个储存设备作为 LUKS 加密的分区,解密后映射到 /dev/mapper/luks
,然后就可以将其视为普通的块设备,进行 mkfs
、mount
等操作。
安装依赖
dm-crypt 是 Linux 内核的一部分,不需要按照什么。这里的依赖主要是关于 fido2 和 yubikey 的。
pacman -S libpido2 # FIDO2 的库
pacman -S yubikey-manager # YubiKey 的管理工具, 提供 ykman 命令
pacman -S pcsc-tools pcsclite # pcsc 驱动
systemctl enable --now pcscd # 启动 pcscd 服务
然后将 YubiKey 插入电脑,运行 ykman list
,可以看到 YubiKey 的信息。此外还可以使用下面的命令检查 YubiKey 是否支持 FIDO2:
systemd-cryptenroll --fido2-device=list
# 输出如下:
# PATH MANUFACTURER PRODUCT
# /dev/hidraw3 Yubico YubiKey OTP+FIDO+CCID
创建 LUKS 加密分区
与 luks 相关的操作使用 cryptsetup
工具的 luks*
子命令。下面的例子我是用一个 U盘,设备名是 /dev/sda
。
首先,使用 cryptsetup
创建一个 LUKS 加密的分区:
cryptsetup luksFormat /dev/sda
有如下常用参数,全部参数列表请自行参考 man cryptsetup
:
参数名称 | 含义 | 推荐值 | 备注 |
---|---|---|---|
--cipher | 加密方式 | aes-xts-plain64 | AES 加密算法搭配 XTS 模式 |
--key-size | 密钥长度 | 512 | XTS 模式需要两对密钥,每个的长度是256 |
--hash | 散列算法 | sha512 | N/A |
--iter-time | 迭代时间 | 最好大于10000 | 单位毫秒。该值越大,暴力破解越难。 |
过程中会要求输入密码,这个密码会用来解密 LUKS 加密的分区。输入两次密码后,会提示是否真的要格式化,输入 YES
即可(一定要大写的,看看提示的那条英文)。密码后面是可以修改的,可以先设一个简单的,后面使用 YubiKey 验证,再把密码删了。
这样,/dev/sda
就被格式化为 LUKS 加密的分区了。接下来,我们需要解密这个分区,然后映射到 /dev/mapper/luks
,当然,这个名字可以自己定义。
cryptsetup open /dev/sda luks
这样,/dev/sda
就被解密并映射到 /dev/mapper/luks
了。接下来就可以像普通块设备一样使用了。比如,我这次使用的是一个
U盘,准备使用 ext4
文件系统。
mkfs.ext4 /dev/mapper/luks
mount /dev/mapper/luks /mnt
现在对 /mnt
目录的任何操作都会被加密并储存到 /dev/sda
中。
取消映射前要先 umount
,然后 cryptsetup close luks
:
umount /mnt
cryptsetup close luks
总结一下命令:
cryptsetup luksFormat /dev/sda
:格式化设备为 LUKS 加密的分区cryptsetup open /dev/sda luks
:解密设备并映射到/dev/mapper/luks
cryptsetup close luks
:取消映射
配置 YubiKey 作为密钥
一个 LUKS 加密的分区可以有 8 个密钥槽 (slot),每个密钥槽可以存储一个密钥。密钥槽的编号是从 0 开始的,上面的操作我们已经使用了一个密钥槽,存储了一个密码。接下来我们要使用 YubiKey 的 FIDO2 功能,将 YubiKey 作为密钥存储到密钥槽 1 中。
首先确定 YubiKey 的设备文件名:
systemd-cryptenroll --fido2-device=list
我这里是 /dev/hidraw3
。然后使用 cryptsetup
将 YubiKey 的公钥存储到密钥槽 1 中:
systemd-cryptenroll /dev/sda --fido2-device=/dev/hidraw3 \
--fido2-with-client-pin=no --fido2-credential-algorithm=eddsa
命令的参数:
参数 | 说明 |
---|---|
/dev/nvme1n1p2 | 设备路径 |
--fido2-device | 设备,可用 auto,或前面的 /dev/hidraw3 |
--fido2-with-client-pin | 默认是 yes ,若为 no ,从而开机时只需要触摸 YubiKey 而不需输入 PIN |
--fido2-credential-algorithm | 算法,此处选择 eddsa |
然后我们尝试使用 YubiKey 解密 LUKS 加密的分区:
cryptsetup open --token-only /dev/sda luks
使用 --token-only
参数强制使用 YubiKey 解密,提示输入 YubiKey 的 PIN,然后触摸 YubiKey 即可解密。效果和密码解密一样。
cryptsetup
常用命令
cryptsetup luksDump /dev/sda
:查看 LUKS 加密分区的信息cryptsetup luksAddKey /dev/sda
:添加密钥cryptsetup luksRemoveKey /dev/sda
:删除密钥cryptsetup luksChangeKey /dev/sda
:修改密钥cryptsetup status luks
:查看映射状态