DM-verity

为何使用 DM-Verity?
您可能想要延续 RoT (信任根 [1]) - 从硬件(硬件 TPM 作为信任根,它确保只有硬件中使用 TPM 中正确的密钥才能解密磁盘/文件加密)-> 安全启动(安全启动仅运行经过批准的签名 EFIStub/签名引导加载程序)-> 到现在 DM-Verity rootfs(它确保您的 root 镜像/root 分区没有被篡改)-> 强制访问控制 (AppArmor 或 SELinux - 顺便说一句,SELinux 在默认 linux-lts 上被禁用) -> 沙箱化/虚拟机化应用程序。
我仅仅将根分区设置为只读(基本上只是一个不可变的根分区)与此方法有什么区别?
DM-Verity 完全禁止修改根分区/root 镜像中的任何文件,甚至禁止根镜像/root 分区的一部分(根镜像/root 分区需要完整无缺,并与 DM-Verity 在不同分区上验证的签名密码学相匹配),考虑到这一点,您可以使用 ERO-FS 或 SquashFS 生成只读根分区/文件镜像。如果您将 EXT4 文件系统设置为可写,并且 DM-Verity 要使用它,它将被视为“已损坏”并且不再启动,因为即使对根镜像/分区进行一个微小的数据更改也会使其“损坏”。这是 Android 和 Chrome 操作系统设备中使用的相同技术。也与 A/B root 的使用相关,Fedora 也使用它。
- 以下指南将使用 dracut 来制作签名安全启动的 EFIStub。
- 如果您还没有,您可以按照 Alpine Linux 磁盘加密设置 [2],使用 DM-Crypt LUKS 和 LVM
- 当使用 Dracut 模块生成时,您可以选择使用 TPM 解密镜像,使用
add_dracutdrivers+=" ... tpm2-tss ... "
和 Clevis [3] - 使用 Alpine Linux AppArmor Wiki 应用 AppArmor(Docker 已经使用了 AppArmor,但您可以对任何不应访问特定目录的应用程序应用 AppArmor 配置文件(AppArmor 是一个基于 PATH 的 MAC))[4]
- 在服务器上使用 GVisor [5] 和 Docker 进行沙箱化(Docker 不是沙箱 [6](因为与 Gvisor 使用自己的内核重新实现相比,docker 使用原生内核))
什么是 ERO-FS?
谷歌曾考虑在 Android 13 中实现 ERO-FS 和 DM-Verity [7]。据报道,谷歌在 Android 13 中使用了它,ERO-FS 是一种出色且快速的压缩只读文件系统。
以下是关于 [Arch Linux Wiki] [8] 上 mkfs.erofs
的摘录
mkfs.erofs(1) 为根分区上的 ext4 或 squashfs 提供了一个有吸引力的替代方案。与 squashfs 类似,EROFS 在设计上不允许写入,并且在许多情况下,在闪存和固态介质上的性能优于类似的文件系统。它默认使用 lz4 压缩,由华为为 Android 手机设计,华为广泛使用 dm-verity。
安装
使用 ERO-FS

/etc/apk/repositories
中使用 Alpine Linux Edge Testing Repository)
首先,一些注意事项,您可以使用严格的只读文件系统,例如 SquashFS 或 ERO-FS,此 wiki 将使用 ERO-FS(如果您使用自定义内核,请确保它支持诸如 ERO-FS 之类的文件系统,默认的 linux-lts 支持)。[ERO-FS Github] [9] 包含有关如何压缩和制作您自己的只读 root 镜像的一些信息。
$ apk install erofs-utils
$ mkdir image && cd image
$ wget https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-minirootfs-3.21.0-x86_64.tar.gz
$ tar -xvf alpine-minirootfs-3.21.0-x86_64.tar.gz && rm alpine-minirootfs-3.21.0-x86_64.tar.gz
以下命令将生成一个使用 lz4 压缩的 ERO-FS 镜像,名为 ./erofs.img (erofs.img 将是 dracut 使用的 root 分区文件)
$ cd .. && mkfs.erofs -zlz4hc erofs.img ./image
在文件或分区之间选择

您可以通过比较哈希表来验证 erofs/squashfs 镜像的完整性。哈希表可以是文件或分区。
- 分区的大小应至少为最终 root 镜像 squashfs/erofs 大小的 10%。
- 在制作哈希表时,运行 veritysetup 也应该提供 root 哈希值。保留此 root 哈希值以供稍后在通过内核参数/在 initramfs 中解锁设备时使用。
使用 mkinitfs/kernel-hooks/secureboot-hooks

使用 mkinitfs,可以在 initramfs 生成中强制使用 veritysetup。建议制作一个 chroot 或 docker,这样您就不会弄乱您的系统文件,并通过添加这些文件

apk add cryptsetup
编辑 /sbin/mkinitfs(在 "# copy modloop signature" 之前放置 "# Copy custom init")
内容为 /etc/mkinitfs
在 "if [ "$SINGLEMODE" = "yes" ]; then" 之前放置 "# Call custom script"
内容为 /initramfs/init
内容为 /etc/mkinitfs/features.d/my-custom-script.files
内容为 /etc/mkinitfs/features.d/veritysetup.files
内容为 /etc/mkinitfs/features.d/veritysetup.modules
内容为 /etc/mkinitfs/features.d/erofs.modules
内容为 /etc/mkinitfs/mkinitfs.conf
内容为 /etc/scripts/my-custom-script.sh
然后只需执行
apk add secureboot-hook gummiboot gummiboot-efistub efibootmgr kernel-hooks secureboot-hook
mkinitfs -c /etc/mkinitfs/mkinitfs.conf -b / $(uname -r)
解压缩
mkdir /tmp/initramfs cd /tmp/initramfs zcat /boot/initramfs-$KERNEL | cpio -idmv
通过执行 "init" 并查看其运行方式来测试它是否工作
./init
使用 Dracut
请记住,将您的 $ROOT_PARTITION
以只读方式挂载(假设您使用 EXT4,如果使用 ERO-FS 或 SquashFS,则忽略此步骤),因为任何更改都会导致 DM-Verity 检测到它为“已损坏”

安装基本组件
apk add dracut dracut-core cryptsetup gummiboot gummiboot-efistub tpm2-tools clevis tpm2-tss tpm2-tss-tcti-device
使用 Clevis 进行 TPM
clevis luks bind -d /$ENCRYPTED_ROOT_PARTITION tpm2 '{"pcr_ids":"1,7"}'
输入 LUKS 密码,请记住,是的,现在它有 tpm,但如果可能,请删除旧的 luks 密码并使用密钥文件或不同的密码来解锁,因为 tpm 更好,这样如果您的计算机坏了,您也不会被锁定在数据之外!
为您的 DM-Verity 创建新分区
将 /dev/sdX 替换为您的实际设备名称。
sudo fdisk /dev/sdX
进入 fdisk 界面后
- 按 n 创建新分区
- 选择 p 作为主分区
- 选择分区号(例如,4)
- 对于起始扇区,按 Enter 使用默认值
- 对于结束扇区,计算您的根分区大小的 10%,并使用 +SIZE 格式。例如,如果您的根分区为 100GB,则输入 +10G
- 按 t 更改分区类型
- 输入您刚刚创建的分区号
- 键入 8e 表示 Linux LVM 类型(最接近 verity)
- 按 w 写入更改并退出
为全盘解密添加自定义行
编辑 "/usr/lib/dracut/modules.d/90crypt/cryptroot-ask.sh
" 并在 "exit 0
" 之前添加以下行(添加您的 $ROOT_PARTITION
目录和 $VERITY_PARTITION
目录)
内容为 /usr/lib/dracut/modules.d/90crypt/cryptroot-ask.sh
使用 Dracut + DM-Verity 生成 EFIStub
要设置 DM-Verity(借用自 Arch Linux Wiki)
veritysetup format /dev/$ROOT_PARTITION /dev/$VERITY_PARTITION
/etc/dracut.conf (将您的引导参数放入 "kernel_cmdline
" 中)
内容为 /etc/dracut.conf
最后,使用 dracut 生成 UKI(将 6.X.X-X-lts
替换为您的内核 $VERSION
)(还要将 /boot/efi/EFI/Linux/alpine-linux.efi
替换为您的挂载 efi 分区)
export DRACUT_KMODDIR_OVERRIDE=1
dracut --include /usr/lib/dracut/roothash.txt /usr/lib/dracut/roothash.txt --host-only --kernel-image /boot/vmlinuz-lts --kmoddir /lib/modules/6.X.X-X-lts --kver 6.X.X-X-lts --uefi --uefi-stub /usr/lib/gummiboot/linuxx64.efi.stub --force --compress lz4 /boot/efi/EFI/Linux/alpine-linux.efi /boot/efi/EFI/Linux/alpine-linux.efi
efibootmgr --create --disk /dev/$EFI_PARTITION --part 1 --label alpine-linux --loader EFI/Linux/alpine-linux.efi
不幸的是,使用默认的 Alpine Linux 内核 (linux-lts
) 无法工作,因此请下载另一个发行版的根系统(如 debian 或 devuan),并将 /lib/modules/6.X.X-X-lts
和 /boot/vmlinuz-lts
替换为另一个发行版的内核模块和内核镜像(或者考虑编译您自己的 linux-hardened 内核 [11])。
$ROOT_PARTITION
的文件,它应该显示“已损坏”,并且不会继续启动过程(仅当您使用 EXT4 时)。外部链接
信任根/信任链
使用 LVM 和 LUKS 的全盘加密 (AlpineWiki):
- LVM_on_LUKS [2]
Clevis Github
AppArmor Wiki (AlpineWiki)
- AppArmor [4]
GVisor
Gvisor Github 声明 docker 不是沙箱
Android-13 使用 ERO-FS 的可能性
DM-Verity (Arch Wiki):
ERO-FS Github
Dracut (Arch Wiki):
Linux-Hardened Kernel (AlpineWiki):
- Hardened_linux [11]