加密方案介绍

我的系统盘文件系统是 Btrfs 。这是个很棒的文件系统,功能强大,但不具备加密功能。

ArchWiki1 介绍了两种加密分区的思路。在现有的文件系统上加密和使用 LUKS 。

LUKS 的花样2繁多,大致有这几种:

LUKS on Partition

最常用的方法。在新分区上创建 LUKS 加密空间,解密后再创建文件系统,即先加密后分区。

不明白分区和文件系统区别的小伙伴建议先百度了解!

优点是配置方便,可以自由添加和更换加密密钥。

缺点是不灵活,必须预先分配要加密的磁盘空间。 可以但很麻烦,而且需求很少。

Plain dm-crypt

纯加密,没有 LUKS 加密头,对 SSD 最友好,但局限很大。

使用这种方式需要记住所有加密参数,并保管好密钥。因为一旦加密完成,你就不能更改密钥了。

其他方式

比如 LUKS on RAID 的软 RAID 加密,LUKS on LVM 和 LVM on LUKS 的套娃加密。普通用户需求较少,就不介绍了。

这里选择 LUKS on Partition 作为加密方案。

准备阶段

驱动器安全擦除

建立加密分区前需要对硬盘进行安全擦除。常规方法是建立分区前擦除3,也可以建立加密后擦除4。

这里采用一种适用于 NVMe 驱动器的方法。

  1. 删除所有的分区

    用什么都行。

  2. 擦除驱动器。

    擦除 NVMe 驱动器的方法有两种,format 和 sanitize 。后者是更完全的擦除,对硬盘寿命会有损耗。

    这里选择前者。假设硬盘块文件为 /dev/nvme0n1 ,执行

    nvme format /dev/nvme0 -ses 1 -n 1
    

nvme0 表示是 NVMe 字符设备 NVMe character device 。

nvme0n1 表示是 NVMe 命名空间块设备 namespace block device ,输入错误系统会报错并给出提示。

后面的 -n 1 参数指定了 NVMe 命名空间为 1 。

分区

常规的分区操作。UEFI 启动需要划分至少两个分区(系统分区和 ESP)。

过程不赘述。

建立加密头

cryptsetup luksFormat /dev/nvme0n1p2

/dev/nvme0n1p2 即准备加密的分区的块路径。

也可以设置其他参数5。比如指定 LUKS 版本:

cryptsetup --type luks1 luksFormat /dev/nvme0n1p2

默认参数

如果不指定参数会采用默认参数,相当于:

cryptsetup --type luks2 --cipher aes-xts-plain64 --hash sha256 --iter-time 2000 --key-size 256 --pbkdf argon2i --sector-size 512 --use-urandom --verify-passphrase luksFormat device

多数情况下默认参数完全够用,除非使用 LUKS1 或者需要更强的安全性能。

密钥

  • 密码

创建时如果没有指定其他参数,系统会要求输入密码来加密分区。

密码可以后期修改,而且这个密码(包括密钥文件)可以设置多个,并不唯一。

  • 密钥文件

如果不想使用密码加密,可以使用密钥文件6加密分区。需要使用这样的指令:

cryptsetup luksFormat /dev/nvme0n1p2 /path/to/keyfile

创建时不会提示输入密码。

一个创建随机密钥文件的方法,需要用 ROOT 权限执行:

dd bs=512 count=4 if=/dev/random of=/path/to/keyfile iflag=fullblock
chmod 000 /path/to/keyfile

妥善保管好自己的密钥,做好备份!

安装系统

解密分区

安装系统前需要解锁分区

cryptsetup open /dev/nvme0n1p2 dm_name

解密后的分区会被映射到 /dev/mapper/dm_name 。dm_name 可以修改为其他名称。

如果使用了密钥文件加密,解密时需要指定密钥路径

cryptsetup open /dev/nvme0n1p2 dm_name --key-file /etc/mykeyfile

然后就可以在解密后的分区上创建文件系统了。以 btrfs 为例,假设设定的dm_name为 Root_

mkfs.btrfs /dev/mapper/Root_
mount /dev/mapper/Root_ /mnt

接下来就可以正常安装操作系统了。

如果要加密 BOOT 分区请参考 BOOT 分区加密的方法安装 GRUB 引导。(不推荐)

BOOT 分区加密

不推荐加密 BOOT 分区。

GRUB 仅支持解密 LUKS1 分区,对 LUKS2 分区的支持尚未完全。

这并不影响系统盘使用 LUKS2 加密。

打开 /etc/default/grub ,修改这一项:

GRUB_ENABLE_CRYPTODISK=y

修改后重新安装 GRUB 引导:

grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB --recheck

--recheck 用于防止重复添加启动项。

如果没有修改 GRUB 配置文件就安装引导,系统会报错并提示你修改。

配置开机解密

配置 initramfs HOOK

  1. 修改 /etc/mkinitcpio.conf ,添加 LUKS 需要的 HOOK

    Arch Linux 提供了两套方案,可以使用 udev 或者 systemd 的 HOOK。

    推荐后者,因为支持更多的功能,开机更快。

    添加 systemd HOOK:

    HOOKS=(base **systemd** autodetect keyboard **sd-vconsole** modconf block **sd-encrypt** filesystems fsck)
    

    需要在对应位置添加星号标记的条目。

  2. 添加密钥文件路径,不需要引号

    FILES=(/path/to/keyfile)
    

    使用 ROOT 执行 mkinitcpio -p 内核名称 生成内核文件,如果创建失败请检查是否安装了 btrfs-progs 包。

配置 Bootloader 内核参数

此项与下条二选一。

修改 Bootloader 的内核参数。在已有的参数前添加参数:

rd.luks.name=device-UUID=cryptroot 指定加密盘的位置。这里使用 UUID[^Persistent_block_device_naming_(简体中文) - ArchWiki] 定位分区。UUID 可以使用指令查看 lsblk -o name,uuid 。注意需填入被加密物理分区的 UUID ,而不是解密后映射分区的 UUID 。

cryptroot 是解密后映射分区的名称,注意要与下方设置的 dm_name 一致。

rd.luks.options 设置解密参数,主要是 LUKS 加密等参数,一般不需要额外设置。如果是 SSD ,可添加 discard 参数启用 TRIM 。

rd.luks.key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=/path/to/keyfile 指定加密文件的位置,需要填入被加密物理分区的 UUID 以及文件路径(文件包含在 initramfs 中)。

root=/dev/mapper/cryptroot 映射的系统盘位置。

以 GRUB 为例:

GRUB_CMDLINE_LINUX_DEFAULT="rd.luks.name=6120c3ba-7811-4692-a9c0-b0319a8effa4=root_ rd.luks.options=timeout=10s,discard,cipher=aes-xts-plain64:sha256,size=512 rd.luks.key=6120c3ba-7817-4692-a9c0-b0314a679ea4=/etc/cryptluks.key root=/dev/mapper/root_ loglevel=3 quiet"

执行生成 grub.cfg :

grub-mkconfig -o /boot/grub/grub.cfg

配置 crypttab

此项与上条二选一。

新建 /etc/crypttab.initramfs 。这个文件会在 mkinitcpio 时自动添加入 initramfs 中。每次修改完密钥文件都需要执行 mkinitcpio 操作。

格式与 /etc/crypttab 相同。

在文件后添加如下条目:

block_  UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  -  discard,cipher=aes-xts-plain64:sha256,size=512

条目一共分为四列:

  • 第一列:映射分区的名称,即 rd.luks.name=device-UUID=cryptroot 中的 cryptroot 。
  • 第二列:被加密物理分区的位置,即 rd.luks.name=device-UUID=cryptroot 的 device-UUID 。需要加上前缀 UUID= 。
  • 第三列:密钥文件的路径。没有的话填 none 或 - 。
  • 第四列:解密参数,即 rd.luks.options 的内容。

保存后执行 mkinitcpio 操作。

别忘了给 Bootloader 添加内核参数,只要添加一项 root=/dev/mapper/cryptroot 。

使用 Yubikey 开机解密

最近入手了一个 Yubikey 5,想着能不能用它来开机解密,实现真正的安全启动。答案是可以的7。

systemd 提供了 FIDO 硬件密钥解密的支持。

需要配置 systemd 的 HOOK ,并提前配置好 Yubikey 的 FIDO 功能。

方法

  1. 插入 Yubikey ,使用 ROOT 执行指令

指令中的块文件路径请按自己的加密盘实际路径修改。

这里需要的是加密盘物理分区的块文件,而不是解密后的映射分区块文件。

systemd-cyptenroll /dev/nvme0n1p2 --fido2-device=auto

有以下可选参数:

--fido2-with-client-pin=BOOL 解锁时是否需要输入 PIN ,默认为 yes 。建议添加并设置为 no ,否则每次开机不仅要插入密钥还要输入 PIN 。

--fido2-with-user-presence=BOOL 解锁时是否需要用户确认(即触摸密钥),默认为 yes ,需要 FIDO 设备支持 "up" 特性。如果使用 Yubikey 5 就不用设置这个了,即使关闭了也会被强制打开(有提示信息)。

--fido2-with-user-verification=BOOL 解锁时是否需要用户确认,默认为 no ,需要 FIDO 设备支持 "uv" 特性。不知道和上条有什么区别,不用管即可。

执行过程中需要输入 Yubikey 的 FIDO PIN ,并且需要输入加密盘的密码。

此处不能使用密钥文件解密。

建议提前添加一个密码,可以完成操作后移除。

  1. 修改 Bootloader 的解密参数。删除文件解密,否则硬件密钥不会生效

  2. 在内核参数中指定 FIDO 启动方式

    在解密参数中添加 fido2-device=auto 。

开机

开机时需要插入 Yubikey ,等待指示灯闪烁后触摸。如果设置了输入 PIN 解密,需要先输入 PIN 再触摸。

不要求通电前插入密钥,在超时前插入并完成触摸即可。

设置 Yubikey 解密后,可以设置自动登陆,以实现“一键开机”的功能。

参考资料

  1. https://wiki.archlinux.org/index.php/Btrfs_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#%E5%8A%A0%E5%AF%86 ↩

  2. https://wiki.archlinux.org/index.php/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition ↩

  3. https://wiki.archlinux.org/index.php/Solid_state_drive/Memory_cell_clearing ↩

  4. https://wiki.archlinux.org/index.php/Dm-crypt/Drive_preparation ↩

  5. https://wiki.archlinux.org/index.php/Dm-crypt/Device_encryption#Encryption_options_for_LUKS_mode ↩

  6. https://wiki.archlinux.org/index.php/Dm-crypt/Device_encryption#Keyfiles ↩

  7. https://wiki.archlinux.org/title/Universal_2nd_Factor#Data-at-rest_encryption_with_LUKS ↩