加密方案介绍

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

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

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

LUKS on Partition

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

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

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

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

Plain dm-crypt

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

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

其他方式

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

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

准备阶段

驱动器安全擦除

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

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

  1. 删除所有的分区

    用什么都行。

  2. 擦除驱动器。

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

    这里选择前者。假设硬盘块文件为 /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_namedm_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=cryptrootdevice-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