ArchLinux LUKS 全盘加密教程手稿

archlinux, LUKS2, Unified Kernel Image, Secure Boot (sbctl), YubiKey, btrfs (with subvolume), UEFI, Snapper, FIDO2

前言

自从发现自己随时可能被中华人民共和国人民警察们搜查以来,我的加密偏执就复发了。我决定用 LUKS2 和 YubiKey 武装我的电脑。可能的话,我还要把这枚 YubiKey 改造得易于摧毁。

不过,配置环境起码要两天左右,我手头上的事太多了,中间还有一个半月在医院度过,因此迟迟没有动手。4 个月后,我终于有时间了。于是开干。

准备工作

我阅读了几篇文章:

结合各方优劣,得出了自己的方案。事先在虚拟机里尝试了两遍,最终得到了比较满意的结果。

方案介绍

本方案会使用 LUKS2 加密 EFI 分区以外的整个硬盘,使用 Unified Kernel Image 引导。引导+系统启动只需要输入一次密码(使用 YubiKey 则可配置为不需要密码,也可配置为 2FA)。EFI 分区中会存在一个 main.efi,里面包含了 initramfsmicrocodekernel。会使用 sbctl 对它们进行签名,从而支持 Secure Boot,最后,配置 YubiKey 用于解密 LUKS2

本方案会使用 linux-zen 而非 linux 内核。

本方案不会使用 TPMTPM 配置不当可能导致你的加密功亏一篑。[1]如确有需要,可参考“准备工作”一节中的第二篇文章。

尽管我会加入很多可能不必要的细节,但这只是因为我的神经多样性,本文的性质仍然是笔记 / 手稿,不是操作说明。本文中的命令仅作示意,实际操作过程中可能会有所不同。

本文作于 2024 年 2 月 10 日。你阅读这篇文章时,内容可能已经过时。我并不具备足够的安全知识,本文的操作无法确保安全。

安装系统

此时你的 Secure Boot 应该在关闭状态。我觉得不会有谁想在安装系统时开启 Secure Boot。

首先,遵循安装指南,至安装指南#建立硬盘分区为止。

lsblk
cfdisk /dev/nvme0n1

第一个分区会作为 EFI 分区,分 1G 或者 0.5G 即可,后续 UKI 会占用 ~100MB。

剩下的空间全分给第二个分区,这将是你的加密分区。不再分一个 Swap 分区的原因是当前 btrfs 已经支持 swapfile,并且我 RAM 充足。

mkfs.vfat -n EFI /dev/nvme0n1p1 # 假设这是第一个分区

创建、挂载、格式化 LUKS2 容器,默认设置已经基本满足需求:

cryptsetup luksFormat /dev/nvme0n1p2 # 假设这是第二个分区
cryptsetup open /dev/nvme0n1p2 system
mkfs.btrfs --label system /dev/mapper/system

cryptsetup luksFormat /dev/nvme0n1p2 时,会提示你输入密码。这时可以设置一个安装时临时使用的密码,配置完 YubiKey 后再改。

临时挂载 btrfs 根,创建 subvolume(子卷):

mount /dev/mapper/system /mnt
btrfs subvolume create /mnt/@root
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@var

具体建多少子卷,建哪些,见仁见智。

然后,卸载根、挂载子卷和 ESP 分区:

umount -R /mnt
mount --mkdir -o noatime,subvol=@root /dev/mapper/system /mnt
mount --mkdir -o noatime,subvol=@home /dev/mapper/system /mnt/home
mount --mkdir -o noatime,subvol=@var /dev/mapper/system /mnt/var
mount --mkdir /dev/nvme0n1p1 /mnt/efi

设置镜像站,安装系统并 chroot:

vim /etc/pacman.d/mirrorlist
# 此处 -K 的含义是复制 keyring
pacstrap -K /mnt base linux-zen linux-firmware intel-ucode btrfs-progs

此后,遵循安装指南#配置系统,对系统进行配置。配置好以后,别急着 umount 和重启。你需要配置引导。

# 如果遵循前述步骤,此时你应该在 arch-chroot 环境中
pacman -Syu networkmanager base-devel vim sbctl efibootmgr
vim /etc/mkinitcpio.conf

将它的 HOOKS 一栏配置为如下形式:

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

先不加入 plymouth,等引导成功后再加入,便于观察输出

创建 /etc/kernel/cmdline,写入:

fbcon=nodefer rw rd.luks.allow-discards cryptdevice=/dev/nvme0n1p2:system bgrt_disable root=LABEL=system rootflags=subvol=@root,rw splash vt.global_cursor_default=0

这将是你的内核参数,以后需要配置内核参数的话就改这里。sbctl 会自动配置好 microcode。安装 intel-ucodeamd-ucode 即可,无需额外在参数里加上。

创建 /etc/crypttab.initramfs,写入:

system /dev/nvme0n1p2 none timeout=180,fido2-device=auto

这一步是让 initramfs 启动时解密位于 /dev/nvme0n1p2 的 LUKS2 容器,并挂载到 /dev/mapper/system

创建或编辑 /etc/vconsole.conf

KEYMAP=us
FONT=lat2-16

这将能解决 mkinitcpio 时的警告。

创建密钥并生成 Unified Kernel Image,加入引导:

sbctl create-keys
# 因为用的是 linux-zen 而非 linux,所以需要指定内核和 initramfs 文件名
sbctl bundle -k /boot/vmlinuz-linux-zen -i /boot/intel-ucode.img -f /boot/initramfs-linux-zen.img -s /efi/main.efi
# 想打 AMD 微码用这行,区别在 -i 和 -a 参数
sbctl bundle -k /boot/vmlinuz-linux-zen -a /boot/amd-ucode.img -f /boot/initramfs-linux-zen.img -s /efi/main.efi
mkinitcpio -P
efibootmgr --disk /dev/nvme0n1 --part 1 --create --label "Arch Linux" --loader /efi/main.efi
exit # 退出 chroot
umount -R /mnt
reboot

这次应该能启动进入系统。启动过程中会要求你输入此前 cryptsetup luksFormat 时设置的密码。

进入系统后,可以遵循如下步骤连接到无线网络:

systemctl enable --now NetworkManager
nmtui

设置 NTP:

timedatectl set-ntp true

配置 YubiKey

按照建议阅读,配置好多用户与桌面。

使用如下命令确保你的 YubiKey 具备 FIDO2 功能:

sudo systemd-cryptenroll --fido2-device=list

你将能得到类似下面的输出:

PATH         MANUFACTURER PRODUCT
/dev/hidraw0 Yubico       YubiKey FIDO+CCID

使用如下命令将 YubiKey 添加到 LUKS2 容器的解密设备列表:

sudo systemd-cryptenroll /dev/nvme1n1p2 --fido2-device=auto --fido2-with-client-pin=no --fido2-credential-algorithm=eddsa

参数意义如下:

参数 说明
/dev/nvme1n1p2 设备路径
--fido2-device 设备,可用 auto,或前面的 /dev/hidraw0
--fido2-with-client-pin 默认是 yes,此处置 no,从而开机时只需要触摸 YubiKey 而不需输入 PIN,可自行调整为需要 PIN
--fido2-credential-algorithm 算法,此处选择 eddsa

可参考 systemd-cryptenroll#FIDO2_tokens

sudo mkinitcpio -P 后重启系统,确认 YubiKey 配置成功。

确定 YubiKey 可以运行后,记得通过 cryptsetup luksChangeKey /dev/nvme0n1p2 来修改此前临时设置的密码。改个强一点、最好你自己也记不住的,然后写在纸上(最好用遇水能洇开的墨水),放在安全但又随手可及的地方。

可以用如下命令生成:

openssl rand -base64 16

配置 Plymouth

想看 Plymouth 的话,此时也可以配置好。我的 Plymouth 在启动时会出现方块,原因是 initramfs 里没有字体,解决方法为在 mkinitcpio 时加入。照着下面的例子编辑 /etc/mkinitcpio.conf

# 你可以改成别的字体
FILES=(/usr/share/fonts/noto/NotoSans-Regular.ttf)

配置 Snapper

Snapper 可以对你的 btrfs 进行定期快照与恢复。我使用图形化的 Btrfs Assistant 配置,因此这里没什么可以说的。

配置 Secure Boot

此步操作不当可能导致你的数据丢失。

把这放到最后一步是因为它最麻烦,并且开了 Secure Boot 以后,从系统维护盘启动进行修正会变得困难。

不使用 TPM 的前提下,配置 Secure Boot 的用处并不大,反而会带来很多麻烦。但如果使用了 TPM(把密钥写入了 TPM),那么不配置 Secure Boot 就会导致安全性下降。[1:1]我也是因为 Windows 那边开了 Bitlocker(加密系统盘时,会把密钥写入 TPM)才想着配置 Secure Boot。

如果你的电脑有 Windows 系统,并且 Windows 系统盘开启了 Bitlocker,那么请备份并保管好恢复密钥,下次启动 Windows 时很可能会用到。

在 BIOS 界面,开启 Secure Boot,并重置为 Setup Mode。重启进入 Linux。使用如下命令将密钥安装进 Secure Boot:

sbctl enroll-keys -mcft

如果你的 Secure Boot 没被重置为 Setup Mode,那么这一步会报错。请勿强行写入

各参数说明:

参数 说明
-m Microsoft,添加微软密钥,从而使 Windows 能够启动
-c custom,添加自定义密钥,从而使你的 Linux 能够启动
-f firmware-builtin,添加固件内置密钥
-t tpm-eventlog,添加 TPM 事件日志

sbctl 在此前 sbctl bundle ... -s /efi/main.efi 时,会将构建信息保存(-s 参数),并在每次 mkinitcpio 时自动更新。因此,你不需要手动更新。你也可以参考 sbctl 的手册页来获得更多用法。例如,使用 sbctlrefindsystemd-boot 进行签名。

当那一天来临

如果需要紧急销毁密钥,正确的步骤是:

  • 找到前述写有密码的纸条,将其销毁。
  • 销毁 YubiKey,确保其芯片(内部的硅片)破碎。如果你此前配置的是 --fido2-with-client-pin=no,那么这步应当先做。 YubiKey 比较坚固,你可能要用些工具,或在平时就将它改造得易于摧毁。

这以后,就可以微笑着迎接破门而入的军警宪特了。祝你不要有这一天。



  1. 可以参考这篇文章备份链接)和这篇文章备份链接)。如果不想 TPM 变成突破口,就不要把密钥丢 TPM 里。 ↩︎ ↩︎

2 Likes

链接超过十个了,删了一些链接

提了一下tl,现在可以发10个了

2 Likes

好歹带个消防斧什么的迎接破门而入的……吧(x

咦?稀客!

or just remove the keyslot on LUKS beforehand (
不過維護會麻烦一点 因为cryptsetup好像没法用key解锁 得livecd手动加内核参数

1 Like

我这里cryptsetup可以用key解锁

how, 我读了一整遍man cryptsetup也沒找到哪裏可以用fido

我 cryptsetup open /dev/nvme0n1p2 system 的时候它就会问我要 pin、摸密钥了

我是通过 systemd-cryptenroll 来把 fido 写入 luks 的

1 Like