DM-verity

来自 Alpine Linux
待办事项: 添加一个非 initramfs dm-verity 实现,它应该使用内核进行 dm-verity 检查,而不是 initramfs,这对嵌入式设备更好,启动速度更快(但由于内核定制化而难以实现,可能需要重新编译内核。)


为何使用 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 也使用它。

提示: 为了保持合理的安全性,您最好处理整个 RoT,否则任何其他人都可以攻击非 root 分区/root 镜像的未验证启动过程,而 DM-Verity 强制执行了 root 分区/root 镜像的验证。
  • 以下指南将使用 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

警告: 此 Wiki 将使用 Alpine Linux Edge 版本。(Alpine Linux 非 Edge 版本可能不包含此 Wiki 页面其余部分中的这些软件包)(也在 /etc/apk/repositories 中使用 Alpine Linux Edge Testing Repository)


注意: 为了不混淆,ERO-FS 生成一个“root 镜像”,但您可以将“root 分区”与“root 镜像”互换使用。

首先,一些注意事项,您可以使用严格的只读文件系统,例如 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,这样您就不会弄乱您的系统文件,并通过添加这些文件

待办事项: init 脚本太大了,无法放入 wiki,我只是从 initramfs 生成和解压缩中获取了它(阅读下面的提示)


注意: APK 包 "cryptsetup" 附带 veritysetup

apk add cryptsetup

注意: 确保 chroot 具有 linux-kernel

编辑 /sbin/mkinitfs(在 "# copy modloop signature" 之前放置 "# Copy custom init")

内容为 /etc/mkinitfs

... # Copy custom init cp /initramfs/init "$tmpdir"/init # copy modloop signature ...

在 "if [ "$SINGLEMODE" = "yes" ]; then" 之前放置 "# Call custom script"

内容为 /initramfs/init

... # Call custom script /etc/scripts/my-custom-script.sh if [ "$SINGLEMODE" = "yes" ]; then ...

内容为 /etc/mkinitfs/features.d/my-custom-script.files

/etc/scripts/my-custom-script.sh

内容为 /etc/mkinitfs/features.d/veritysetup.files

/sbin/veritysetup

内容为 /etc/mkinitfs/features.d/veritysetup.modules

kernel/drivers/md/dm-verity.ko*

内容为 /etc/mkinitfs/features.d/erofs.modules

kernel/fs/erofs

内容为 /etc/mkinitfs/mkinitfs.conf

... features="... erofs my-custom-script veritysetup" MODULES="veritysetup erofs" ...

内容为 /etc/scripts/my-custom-script.sh

#!/bin/sh mount /dev/mapper/root /root mount /root/var/root/erofs.img /sysroot

然后只需执行

apk add secureboot-hook gummiboot gummiboot-efistub efibootmgr kernel-hooks secureboot-hook

提示:为了确保 initramfs 具有所需的脚本,请执行

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 检测到它为“已损坏”

警告: 以下指南是在全盘加密的情况下完成的,因此可能无法在未加密的磁盘设置上完美运行,也是在使用 gummiboot (现在称为 systemd-boot - 是的,systemd-boot 是一个独立于 systemd init 系统的项目) EFIStub 和生成 UKI (Unified Kernel Image) 的 UEFI 上完成的


安装基本组件

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 界面后

  1. 按 n 创建新分区
  2. 选择 p 作为主分区
  3. 选择分区号(例如,4)
  4. 对于起始扇区,按 Enter 使用默认值
  5. 对于结束扇区,计算您的根分区大小的 10%,并使用 +SIZE 格式。例如,如果您的根分区为 100GB,则输入 +10G
  6. 按 t 更改分区类型
  7. 输入您刚刚创建的分区号
  8. 键入 8e 表示 Linux LVM 类型(最接近 verity)
  9. 按 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

... ### DM-Verity mount /dev/mapper/$ROOT_PARTITION /var/tmp cryptsetup open --key-file /var/tmp/$KEYFILE_DIR /dev/$VERITY_PARTITION $VERITY_DM_NAME umount /var/tmp veritysetup open /dev/$ROOT_PARTITION /dev/$VERITY_PARTITION $(cat /usr/lib/dracut/roothash.txt) ### ...
使用 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

... kernel_cmdline="" add_dracutdrivers+=" busybox crypt crypt-gpg dm rootfs-block kernel-modules kernel-modules-extra tpm2-tss " /etc/dracut.conf.d/secureboot.conf # 如果您有自己的 secureboot 密钥,或者想要使用 secureboot,则执行以下 3 行: uefi_secureboot_cert="/$SECUREBOOT_KEYS_DIR/db.crt" uefi_secureboot_key="/$SECUREBOOT_KEYS_DIR/db.key" /etc/dracut.conf.d/files.conf # 这是将 veritysetup 二进制文件导入 dracut initramfs 的内容 install_items+=" /sbin/veritysetup " ...

最后,使用 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])。

提示: 如果您想测试 DM-Verity 是否完全工作,请更改 $ROOT_PARTITION 的文件,它应该显示“已损坏”,并且不会继续启动过程(仅当您使用 EXT4 时)。

外部链接

信任根/信任链

使用 LVM 和 LUKS 的全盘加密 (AlpineWiki):

Clevis Github

AppArmor Wiki (AlpineWiki)

GVisor

Gvisor Github 声明 docker 不是沙箱

Android-13 使用 ERO-FS 的可能性

DM-Verity (Arch Wiki):

ERO-FS Github

Dracut (Arch Wiki):

Linux-Hardened Kernel (AlpineWiki):

我关于它是如何工作的旧帖子