Portal:MicroOS/FDE

跳转到:导航搜索

全盘加密 (FDE)

MicroOS 和 Tumbleweed 现在使用基于 systemd 的全盘加密镜像,适用于遵循启动加载程序规范 (BLS[1]) 的启动加载程序。

systemd-boot 启动加载程序以引入此规范而闻名,其中启动加载程序条目不是 EFI 分区 (类型#1) 中的小型配置文件,或者最近的 PE 包,它将 kernelinitrd 和命令行等元数据聚合在一个称为统一内核镜像或 UKI (类型#2) 的单个文件中。

最近,openSUSE 从 Fedora 移植了将在 GRUB2 中启用 BLS 类型#1 条目的补丁。其他启动加载程序,如 zipl (s390x) 或 Petitboot (PowerPC) 也支持 BLS,并且由于 systemd-bootGRUB2arm64 中受支持,因此 BLS 涵盖了我们关心的所有架构。

新的 MicroOS[2] 和 Tumbleweed[3] 镜像提出了一种基于 systemd 的全盘加密架构,该架构依赖于具有遵循 BLS 的启动加载程序。

使用 systemd,我们可以注册传统的密码,以及基于 TPM2FIDO2 密钥的自动解锁。在 TPM2 的情况下,自动解锁仅在系统处于良好已知状态 (健康状态) 时发生,由测量启动[4] 确定,其中包括所有固件、安全启动证书、启动加载程序、kernelinitrd 和命令行。这意味着如果启动链中的某些内容已被更改 (例如,如果 initrd 不是我们在安装期间使用的那个),则 TPM2 将检测到更改并拒绝提供密码以解锁 LUKS2 设备,并会要求输入密码。

安装

这些镜像作为 qcow2 镜像部署给 KVM。它们可以通过 libvirtQEMU 中运行,例如。运行它们的最直接方法是使用 virt-managervirt-install 创建一个具有 UEFITPM2 的机器。

首先,从这里下载 sdboot 镜像 [8]。使用 libvirt 运行镜像的最简单命令如下

 virt-install --name microos-sdboot \
   --ram 2048 \
   --boot uefi \
   --tpm backend.type=emulator,backend.version=2.0,model=tpm-tis \
   --import --file ./openSUSE-MicroOS.x86_64-kvm-and-xen-sdboot.qcow2 \
   --osinfo opensuse-factory

请注意 --boot uefi 选项以启用 UEFI 和 -tpm 标志以启用模拟软件 tpm。虚拟机启动时会自动启动 virt-viewer。在首次启动期间,系统会询问您是否要加密当前设备,并在首次启动结束时,jeos-firstboot 将继续进行注册。

系统生成了一个很长的恢复密钥。需要安全地保存此密钥 (使用手机扫描二维码以快速访问密钥)。此密钥用于访问 LUKS2 rootfs 设备。

菜单将提供注册 TPM2 (如果检测到) 选项,并带有或不带有额外的 PIN。使用 TPM2,如果系统处于良好状态 (请参阅 [4] 中的测量启动),我们可以自动解锁设备。如果注册了 PIN,则在验证系统后会要求输入 PIN。如果两者匹配,则将释放解锁 LUKS2 密钥设备的密钥,并且启动过程将继续。

如果在注册过程中检测到 FIDO2 密钥,菜单将提供注册它的选项。并非所有 FIDO2 密钥都可以注册,它们需要支持 hmac-secret 扩展。要查看我们的密钥是否支持它,一种选择是查看它是否可以列出

 systemd-cryptenroll --fido2-device=auto "$DEV"

可选地,我们可以注册根密码或新密码。

恢复密钥也用作 TPM2systemd-pcrlock (默认选项) 注册时使用的恢复 PIN。不要将恢复 PIN 与为 TPM2 + PIN 注册选项创建的 PIN 混淆。当预测失败并且我们需要注册新的预测时,将使用恢复 PIN,而无需修改所有加密设备的 LUKS2 标头,这需要进行繁琐的重新注册过程。

现在可以正常操作系统了。在更改启动链中某些组件的更新之后,将生成新的 TPM2 预测,因此下一次重新启动仍然会将系统视为健康状态,并且不会要求密码来打开 LUKS2 设备。

您可以通过运行以下命令来检查系统是否正在使用全盘加密 (将 /dev/vda3 替换为您的磁盘,如果不同)

 systemd-cryptenroll /dev/vda3

该命令将显示所有密钥槽,包括 fido2 和 tpm (如果一切正常)。

使用 YaST 安装

如今,YaST2 完全支持使用 BLS 启动加载程序 (Tumbleweed 的默认 grub2-bls 和 MicroOS 的 systemd-boot) 以及通过 systemd 进行用户空间全盘加密的系统安装。

简而言之

对于 openSUSE MicroOS 和 Tumbleweed,安装步骤几乎相同

  • 使用 YaST2 引导或专家分区程序为每个分区选择“基于 systemd 的全盘加密”
  • 设置密码并选择 argon2id 密钥派生函数
  • 可选地,选择 TPM2TPM2+PINFIDO2 密钥作为身份验证
  • 检查是否选择了 grub2-bls 作为启动加载程序
  • 忽略一些投诉 (bsc#1244755)

就这样了,真的。

MicroOS 和 Tumbleweed 的详细说明

由于 YaST2 已经支持这种安装方式,因此整个过程直接而简单。

首先需要下载 MicroOS DVD 镜像[7] 或 Tumbleweed 镜像。请记住,为了正确安装,我们需要一个带有 TPM2 的 UEFI 系统。

启动正常安装,并在 建议 分区中选择 专家分区程序 / 从当前建议开始

建议分区 - 初始

专家分区程序 中,我们可以开始删除两个 GRUB2 子卷:@/boot/grub2/i386-pc@/boot/grub2/x86_64-efi,如果存在 (MicroOS) @/boot/writable。这将在未来自动完成。

专家分区程序 - 初始

在 MicroOS 中,您将看到两个分区,一个包含 rootfs,另一个用于 /var,但在 Tumbleweed 中,您将拥有 rootfs 和 swap。选择一个,按 编辑.. 按钮并启用 加密设备 复选框。

编辑分区 - 初始

在下一个窗口中,选择的 加密方法 应为 基于 systemd 的全盘加密。填写加密密码,并在 基于密码的密钥派生函数 中选择 Argon2id。最后,在 身份验证 框中设置 TPM2TPM2+PIN。后者将使用 TPM2 来验证系统的健康状况,但要解锁设备,需要输入 PIN 或密码。为了简单起见,将设置为解锁 LUKS2 设备的相同密码,但以后可以在终端中更改它。

编辑分区 - TPM2

忽略有关 LUKS2 和 GRUB2 支持的警告。自动选择的启动加载程序将是 grub2-blssystemd-boot,具体取决于发行版,并且此警告不适用。

加密 /varswap 后,专家分区程序 屏幕会将所有分区标记为加密,并且 grub2 子卷将不再存在。

专家分区程序 - 最终

返回 建议分区 屏幕,YaST2 将显示使用 TPM2 的设备,并且最终屏幕 安装设置 将显示 grub2-bls 作为选定的启动加载程序。

建议分区 - 最终
安装设置

安装后,系统将重新启动,并在终端中检查所有分区是否已加密以及是否正在使用 TPM2。例如,在我的 VM 中

 localhost:~ # lsblk
 NAME        MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
 sr0          11:0    1 1024M  0 rom   
 vda         253:0    0   20G  0 disk  
 ├─vda1      253:1    0  512M  0 part  /boot/efi
 ├─vda2      253:2    0  8.2G  0 part  
 │ └─cr_root 254:1    0  8.2G  0 crypt /usr/local
 │                                     /srv
 │                                     /opt
 │                                     /home
 │                                     /.snapshots
 │                                     /root
 │                                     /
 └─vda3      253:3    0 11.3G  0 part  
   └─cr_var  254:0    0 11.3G  0 crypt /var
 localhost:~ # systemd-cryptenroll /dev/vda2
 SLOT TYPE    
    0 password
    1 tpm2
 localhost:~ # systemd-cryptenroll /dev/vda3
 SLOT TYPE    
    0 password
    1 tpm2

不使用 YaST 安装

如果已经有一个带有 LUKS2 设备的系统,我们可以手动注册设备以使用 TPM2

 sdbootutil enroll --ask-pin --method=tpm2

方法也可以是 tpm2+pinfido2。我们可以验证注册。

 localhost:~ # systemd-cryptenroll /dev/vda2
 SLOT TYPE    
    0 password
    1 tpm2
 localhost:~ # systemd-cryptenroll /dev/vda3
 SLOT TYPE    
    0 password
    1 tpm2
 localhost:~ # cat /etc/default/fde-tools
 localhost:~ # ls /boot/efi/EFI/systemd/*.json

如果列出的 json 文件是 pcrlock.json,我们通过 systemd-pcrlock 注册了系统,但由于并非所有 TPM2 都支持 NVIndex 策略,因此我们可能会找到 tpm2-pcr-signature.json。这意味着我们正在使用 pcr-oracle 和签名策略。

从 GRUB2-EFI 迁移

如果我们正在使用传统的 GRUB2 并且想要迁移到 grub2-blssystemd-boot,通常无法做到。主要原因是 ESP 的大小。默认情况下是 256Mb,但我们需要大约 1Gb (MicroOS 的 512Mb 应该可以)。如果您设法在不破坏系统的情况下扩大分区,例如使用 gpartedKDE 分区管理器,那么我们可以进行迁移!

首先检查我们是否可以正确地 ESP 分区,该分区具有 vfat 文件系统,并且足够大。

 # Check that there is a ESP of type "vfat" and is at least 512Mb / 1GB
 findmnt /boot/efi -o fstype -n
 df "$(findmnt /boot/efi -o source -n)"

BLS 启动加载程序将从 /etc/kernel 读取内核 cmdline 和条目令牌。

 mkdir /etc/kernel
 # For Tumbleweed use "opensuse-tumbleweed"
 echo "opensuse-microos" > /etc/kernel/entry-token
 
 # Read the cmdline from "cat /proc/cmdline", and remove "root=" or
 # "BOOT_IMAGE=" entries
 echo "quiet systemd.show_status=yes console=ttyS0,11520 console=tty0 security=selinux selinux=1" > /etc/kernel/cmdline
 # In Tumbleweed we can use other cmdlines like:
 #   "rw quiet systemd.show_status=1 console=ttyS0,11520 console=tty0 security=selinux selinux=1"
 #   "splash=silent quiet security=selinux selinux=1 mitigations=off" > /etc/kernel/cmdline

在安装新的启动加载程序之前,我们需要编辑 /etc/sysconfig/bootloader 并更改 LOADER_TYPE 和其他字段,具体取决于启动加载程序。

 # If we will use grub2-bls:
 #   LOADER_TYPE="grub2-bls"
 # or if we use systemd-boot:
 #   LOADER_TYPE="systemd-boot"
 # If SECURE_BOOT is "yes", be sure that shim is installed
 # Also check that:
 #   UPDATE_NVRAM="no"
 vim /etc/sysconfig/bootloader

现在我们可以删除 GRUB2 并安装新的启动加载程序。

 zypper rm grub2-common
 zypper in sdbootutil grub2-x86_64-efi-bls
 # If we use systemd-boot:
 #   zypper in sdbootutil systemd-boot

我们应该删除旧启动加载程序的一些残留物,并清理 ESP。还有一些子卷可以删除。

 rm -fr /boot/.vmlinuz* /boot/System.map* \
        /boot/config* /boot/initrd* \
        /boot/sysctl.conf* /boot/vmlinuz* \
        /boot/grub2 /boot/efi/*
 rm /etc/default/grub.*
 
 # Remove the GRUB2 subvolumes
 umount /boot/grub2/x86_64-efi
 umount /boot/grub2/i386-pc
 btrfs subvolume list /
 # ...
 # ID 263 gen 301 top level 256 path @/boot/grub2/x86_64-efi
 # ID 264 gen 88 top level 256 path @/boot/grub2/i386-pc
 # ...
 btrfs subvolume delete --subvolid <first id from above> /
 btrfs subvolume delete --subvolid <second id from above> /
 
 # Remove both subvolumes from fstab
 vim /etc/fstab

我们现在准备好将启动加载程序安装到 ESP 中。

 sdbootutil install
 sdbootutil add-all-kernels

最后,我们可以清除旧的 EFI 启动条目。

 efibootmgr
 # ...
 # Boot0004* openSUSE Boot Manager (grub2-bls)     HD(1,GPT,79453660-ce11-4ac9-ab92-ed60cc14e163,0x800,0x1db800)/File(\EFI\opensuse\shim.efi)
 # Boot0005* opensuse-secureboot   HD(1,GPT,79453660-ce11-4ac9-ab92-ed60cc14e163,0x800,0x1db800)/File(\EFI\opensuse\shim.efi)
 # ...
 efibootmgr -B -b 5

并重新启动。

工作原理

关于这内部工作原理的某种详细描述可以在 openSUSE wiki[5] 和初始公告[6] 中找到。

以下是简短版本。

如前所述,测量启动[4] 是一个评估系统健康状况的过程。当机器启动时,在一些初始化之后,固件的组件将检查是否存在 TPM2 设备。这个多方面的设备可以用作某些测量中“信任根”,这些测量可以在系统中进行。

这意味着固件可以计算启动过程的下一个组件的哈希值,并以加密方式扩展 TPM2 内部的内部寄存器,称为 PCR,以注册计算的哈希值。扩展操作只是将计算的哈希值与当前寄存器值附加起来,然后再次计算复合值的哈希值。这将是新寄存器的值。

此操作使得无法将特定值写入其中一个寄存器,并且最终值只能通过复制用于扩展寄存器的值的序列来重现。这意味着如果由于某种原因其中一个值不同,则 PCR 的最终值将与预期值非常不同。

这是测量启动的核心。启动链的每个组件 (UEFI 固件中的多个步骤、shim 启动加载程序、启动加载程序本身、kernelinitrd) 将加载链中的下一步,计算内存块 (或数据) 的哈希值,并按照公共规范扩展其中一个 PCRTPM2 内部寄存器的最终值代表系统的状态。

TPM2 可用于加密 (密封) 一些数据,例如密码,基于期望的寄存器值,以便如果寄存器匹配某些值,则 TPM2 可以解密 (在行话中解封) 数据。这意味着我们可以使用 TPM2 来存储 LUKS2 设备的密钥,并且只有当此系统处于良好状态时,该密钥才会返回给系统。

systemd-cryptsetup 生成器在 initrd 中会非常早地读取 /etc/crypttab,并为每个加密设备生成一个适配的 systemd-cryptsetup 服务。该服务将检查 LUKS2 设备是否可以使用当前策略打开,如果所有匹配都通过,则会执行打开操作。为了避免冒名顶替设备,此服务(如果通过 /etc/crypttab 中的 tpm2-measure-pcr=yes 配置)将读取设备的卷密钥并扩展 PCR 15。要读取此密钥,设备需要先打开,这意味着 TPM2 验证必须先发生,并且 PCR 15 不能参与策略。有一个名为 measure-pcr-validator 的服务,它在 initrd 的 switch root 之前执行,将验证此 PCR 15,如果验证失败,它将停止(冻结)系统,以避免访问错误的设备。可以使用 measure-pcr-validator.ignore=yescmdline 中避免此停止。

这是无需在每次启动后请求密码的 FDE 的基础,并且 systemd 以及 systemd-cryptenroll 和其他工具承诺提供此功能。推荐的 systemd 方法需要通过 dm-verityUKI 为内核使用只读设备。但是,openSUSE MicroOS 和 Tumbleweed 项目创建了一组工具,该架构实例化了此发行版,但适应了当前状态(分离的 kernelinitrd,基于 btrfs 快照的事务等)

使用的新工具是

  • disk-encryption-tool
  • sdbootutil
  • dracut-pcr-signature

disk-encryption-tool 仅存在于基于镜像的发行版中,将在首次启动时负责加密镜像。这样做将保证为每个系统生成一个新的主密钥,并且不会在镜像中存储与某些恢复密码共享的 LUKS2 密钥槽。此工具还将使用 jeos-firstboot 进行初始注册,使用 systemd-cryptenroll 和下一个工具 sdbootutil

传统 GRUB2 中的启动条目管理与遵循 BLS 的引导加载程序使用的启动条目管理非常不同。当生成新的快照时,需要将新的内核从 rootfs 复制到 ESP(UEFI 所需的 FAT32 分区,在 openSUSE 中位于 '/boot/efi')中的特定位置。如果需要,需要重新生成一个新的 initrd,或者重用旧的 initrd。并且需要创建一个启动条目,一个小的配置文件,用于链接 kernelinitrd 和指向正确的 btrfs 子卷的 cmdline。所有这些管理都由 sdbootutil 完成。

此外,需要进行新的预测才能在不使用密码的情况下启动新的快照。此预测是一种考虑不同 PCR 值组合的策略。sdbootutil 也会使用 systemd-pcrlockpcr-oracle 生成这些预测,具体取决于在注册期间自动选择的策略类型。

该预测包括 TPM2 策略以及将测量不同加密磁盘的卷密钥的预期 PCR 15 值。此外,sdbootutil 还在相同的 ESP 中提供验证器服务,该服务将检查此 PCR #15 值。

默认情况下,选择 systemd-pcrlock 生成的 NVIndex 类型的策略,但也可以通过 pcr-oracle 生成签名策略,本文档稍后将解释如何迁移。建议坚持使用 NVIndex 策略,并将策略存储在 TPM2 内部。这种类型的策略需要使用之前提到的恢复 PIN 来生成来自预测错误的系统的新的预测,并使用恢复密码来解锁设备。

最后,dracut-pcr-signature 是一个小的 dracut 模块,用于在 initrd 中,它会将策略和预期的 PCR #15 值从 ESP 复制到内存中的 initrd 中,以便 systemd-cryptsetup 可以使用 TPM2 策略来解封 LUKS2 密码。

所有这些组件协同工作。disk-encryption-tool 将执行磁盘的初始加密以及不同密码和安全设备的注册,以进行自动解密。sdbootutil 将负责日常引导加载程序条目管理,确保为下一次启动生成新的预测,始终在新的组件(如 kernel 等)之前进行测量。最后,通过 dracut-pcr-signature,我们保证 TPM2 策略在 initrd 可以解锁设备之前可用,这样就不需要在磁盘上的 initrd 上进行修改。

Combustion

我们可以通过 Combustion 进行注册,跳过 jeos-firstboot 交互式模块。

该过程分为两个阶段。在第一阶段,我们将配置 disk-encryption-tool-dracut 服务(从 initrd 运行)在 Combustion--prepare 阶段,创建一个未加密的凭据,这将强制磁盘加密而无需等待超时。加密将在 initrd 阶段的非常晚期发生,就在 IgnitionCombustion 完成时。

在第二阶段,我们将创建一个加密的凭据集,以与第二个服务 sdbootutil-enroll 通信,该服务将在首次启动期间执行,并使用 TPM2 进行注册。

#!/bin/bash
# combustion: network prepare
# script generated with https://opensuse.github.io/fuel-ignition/

if [ "${1-}" = "--prepare" ]; then
    # We set disk-encryption-tool-dracut.encryption credential to
    # "force".  This will make disk-encryption-tool-dracut force the
    # encryption, ignoring that Combusion configured the system, and
    # will skip the permission countdown
    #
    # After the encryption the recovery key is registered in the
    # kernel keyring %user:cryptenroll
    mkdir -p /run/credstore
    echo "force" > /run/credstore/disk-encryption-tool-dracut.encrypt
    exit 0
fi

# Redirect output to the console
exec > >(exec tee -a /dev/tty0) 2>&1

# Create a valid machine-id, as this will be required to create later
# the host secret
systemd-machine-id-setup

# We want to persist the host secret key created via systemd-cred
# (/var/lib/systemd/credential.secret)
mount /var

mkdir -p /etc/credstore.encrypted
credential="$(mktemp disk-encryption-tool.XXXXXXXXXX)"

# Enroll recovery key
echo "1" > "$credential"
systemd-creds encrypt --name=sdbootutil-enroll.rk "$credential" \
	      /etc/credstore.encrypted/sdbootutil-enroll.rk

# Enroll extra password
echo "SECRET_PASSWORD" > "$credential"
systemd-creds encrypt --name=sdbootutil-enroll.pw "$credential" \
	      /etc/credstore.encrypted/sdbootutil-enroll.pw

# # Enroll TPM2 with secret PIN
# echo "SECRET_PIN" > "$credential"
# systemd-creds encrypt --name=sdbootutil-enroll.tpm2+pin "$credential" \
# 	      /etc/credstore.encrypted/sdbootutil-enroll.tpm2+pin

# Enroll TPM2
echo "1" > "$credential"
systemd-creds encrypt --name=sdbootutil-enroll.tpm2 "$credential" \
	      /etc/credstore.encrypted/sdbootutil-enroll.tpm2

# # Enroll FIDO2
# echo "1" > "$credential"
# systemd-creds encrypt --name=sdbootutil-enroll.fido2 "$credential" \
# 	      /etc/credstore.encrypted/sdbootutil-enroll.fido2

shred -u "$credential"

# Umount back /var to not confuse tukit later
umount /var

# Leave a marker
echo "Configured with combustion" > /etc/issue.d/combustion

# Close outputs and wait for tee to finish.
exec 1>&- 2>&-; wait;

故障排除

调试跟踪日志记录(可选)

为了帮助诊断问题,sdbootutil 可以将详细的 shell 跟踪输出到一个专用的日志文件。此功能默认关闭,仅当存在特定的日志文件时才激活。

它做了什么

启用后,sdbootutil 脚本执行的每个 shell 命令都会被跟踪到 /var/log/sdbootutil.log。每行都带有时间戳,并带有脚本名称、行号和函数注释,使其易于跟踪执行路径。

如何启用

创建日志文件(以 root 身份),并在运行要调试的操作(例如,注册、更新预测等)之前 确保具有限制性权限,例如使用

touch /var/log/sdbootutil.log
chmod 600 /var/log/sdbootutil.log

如何验证它是否有效

运行操作(例如,sdbootutil update-predictions)并观察日志

tail -f /var/log/sdbootutil.log

您应该看到以 + 开头并包含时间戳、脚本名称、行和函数的行。

如何禁用

删除或重命名文件。仅当文件存在时才激活跟踪。

捕获的内容

sdbootutil 和相关辅助脚本的 shell 跟踪(正在执行的命令)

时间戳和源代码上下文(文件、行、函数)

未捕获的内容

其他程序的正常 stdout/stderr(这些仍然会进入它们通常的目的地)

内核/dracut/systemd 调试输出(例如,rd.debug、systemd.log_level=debug)——单独使用这些来获取 initrd/早期启动的详细信息

安全注意事项

命令参数和环境变量值可能会出现在跟踪中。将 /var/log/sdbootutil.log 视为敏感数据。如果在启用调试的情况下输入密码或卷密钥,它们将被捕获。

仅在需要时启用跟踪,因为它可能会导致长时间启用时产生大型日志文件。

限制

非常早期的 initrd 阶段可能不会在此处记录;更喜欢 dracut/journald 调试。

跟踪仅影响 sdbootutil 脚本。它不会更改其他服务的全局日志记录。

LUKS2 密钥槽

列出系统中注册的密钥槽。

   DEV=/dev/vda3
   systemd-cryptenroll "$DEV"

应该列出至少一个“recovery”和一个“tpm2”密钥。如果根密码也在注册期间注册,则也会显示一个“password”密钥。

要删除未使用的槽,请使用 --wipe-slot=,要注册新的“password”或“recovery”密钥,请使用 --password--recovery-key 参数。任何更改都需要输入其中一个密码。

PCRs

如果使用 systemd-pcrlock,则跟踪的 PCR 位于 /etc/sysconfig/fde-tools 中。

可以使用 systemd-pcrlock 检查这些寄存器的当前值。由于直到 systemd v257 才将其标记为实验性的,因此它位于 /usr/lib/systemd 中。

可以使用相同的工具检查事件日志。对于每个 PCR 扩展,事件日志中都有一条记录,记录 PCR 索引、扩展的值和描述扩展目的的一些元数据。也可以使用相同的 systemd-pcrlock 工具检查此信息。

PCR #15

为了避免一种攻击,该攻击将用具有相同 UUID 或设备名称的恶意设备替换加密设备,如果 /etc/crypttab 中的设备选项中存在 tpm2-measure-pcr=yes,则 systemd-cryptsetup 可以使用设备卷密钥扩展 PCR #15。

此扩展发生在设备打开(并且应用了 TPM2 策略)之后,因为只能使用与其中一个密钥槽关联的密码检索卷密钥。这意味着 PCR #15 不能参与 TPM2 策略。

sdbootutil update-predictions/var/lib/sdbootutilESP 中创建并签名的 PCR #15 预测文件,如果文件丢失或自上次更新以来 /etc/crypttab 已更改。

默认情况下,每次命令 update-predictions 时都不会更新此文件,因为这需要用户交互才能请求恢复设备卷密钥的密码。

如果主密钥已更改,我们可以通过 sdbootutil update-preditions --measure-pcr 强制重新创建文件

measure-pcr-validator 服务将验证预测文件的签名,并将其与当前 PCR #15 值进行比较。这在所有参与 initrd 的加密设备都以固定顺序打开后完成,以保证最终值始终相同。

如果预测失败,系统将在 switch root 发生之前停止(冻结),并且 rogue 设备中的程序没有机会控制。在检查设备安全且存在错误预测后,我们可以通过在 cmdline 中添加 measure-pcr-validator.ignore=yes 来跳过停止,以继续启动过程并修复问题。

策略

当使用 pcrlock 时,会生成一系列 JSON 文件来表示在启动期间测量的不同组件。它们存储在 /var/lib/pcrlock.d 中,并由 sdbootutil 编排。

这些组件可以将多个测量组合到一个文件中,表示隐式 AND 操作。一个组件可以有多个变体,表示 OR 操作。

systemd-pcrlock 将尝试将当前的事件日志与呈现的组件中的不同哈希对齐,如果存在匹配,它将在 systemd-pcrlock 表中的一列中呈现。

请注意,如果事件日志中没有与某个条目关联的组件,则我们无法制定包含此 PCR 的策略。

当创建新策略时,systemd-pcrlock 的输出可能会有些令人困惑。例如,如果无法将 PCR 预测包含在策略中(因为当前的组件哈希与事件日志中的哈希不匹配,因此无法映射),则策略将在此次排除此 PCR 创建。

为了增加详细程度,建议以这种方式执行 sdbootutil

 SYSTEMD_LOG_LEVEL=debug sdbootutil update-predictions

生成的策略(好吧,策略元数据,因为实际的策略位于 TPM2 内部)存在于两个地方。规范的是在 /var/lib/systemd/pcrlock.json 中。一份副本会复制到 ESP 中,即 /boot/efi/EFI/{systemd,opensuse}/pcrlock.json,以便稍后可以被 dracut-pcr-signature 找到。

如果重新启动后要求输入恢复密码(或者如果我们注册了一个额外的密码,则输入第二个密码),我们可以调试不匹配的原因。

使用 systemd-pcrlock 表,我们可以在“component”列中找到没有为我们跟踪的 PCR 分配的行。此表还显示预期的哈希值,因此我们可以通过检查 /var/lib/systemd/pcrlock.d 中注册的组件来找到不匹配的哈希值。

例如,如果看到 PCR#4(boot-loader-code)对于名为“efi-boot-services-application”的最后一个事件缺少组件,并且在描述列中看到内核的路径,我们知道失败的组件是内核,组件文件是“650-kernel-efi-application”。

在进行重新注册(下一节)之前,我们需要了解这种不匹配是预测代码中的错误,我们忘记在手动更改 ESP 中的内核后更新预测,还是有人篡改了内核,从而危及系统!

重新注册

当预测系统失败时,我们需要为新的测量值生成新的策略,并替换存储在 TPM2 中的策略。为此,我们需要与恢复密钥匹配的恢复 PIN(在注册期间生成的用于手动解锁 LUKS2 设备的漫长的密码)

在 Tumbleweed 的 QEMU 下使用 FDE 时,如果 PCR 0 用于密封 LUKs2 密钥(这是默认配置),则 OVMFQEMU 本身的更新可能需要重新注册。这是因为主机更改了用于启动 VM 的固件,现在新的固件在启动后为 PCR 0 生成不同的值。

我们可以通过输入 PIN 强制重新注册

 sdbootutil --ask-pin update-predictions

环境变量 PIN 也可以使用

 PIN=... sdbootutil update-predictions

完全重新注册(丢失恢复 PIN)

如果由于某种原因我们丢失了恢复 PIN,那么我们需要手动进行重新注册。当然,我们需要拥有一些密码或 FIDO2 密钥才能访问系统并解锁设备,否则无能为力!

 # Add a LUKS password
 sdbootutil enroll --ask-pw --method=password
 # alternative: Add or Replace a LUKS recovery key
 sdbootutil enroll --method=recovery-key
 # NOTE: Adding a new access method is only needed if no other access has been configured so far!
 # Remove the current TPM policy and unenroll the devices
 sdbootutil unenroll --method=tpm2
 # Make a new policy and enroll all devices
 PIN=<selected recovery PIN> sdbootutil enroll --method=tpm2

最后两个命令等效于

 # Remove the old policy from the TPM2 and from the system
 /usr/lib/systemd/systemd-pcrlock remove-policy
 # Drop the "tpm2" keyslot from the LUKS2 each device
 systemd-cryptenroll --wipe=tpm2 $DEV
 # Generate predictions and policy, and register it in the TPM2's NVRAM
 PIN=... sdbootutil update-predictions
 # For each device, we update the LUKS2 header creating a new keyslot
 systemd-cryptenroll --tpm2-device=auto $DEV

请注意,重新注册需要为所有设备完成。

“启动选项恢复”蓝屏

如果从新镜像启动,将显示一个带有 5 秒倒计时的蓝屏,然后将正常重新启动。此屏幕来自 fallback.efishim 包),它正在为镜像注册第一个 EFI 启动条目。这可能会令人困惑,但这不是错误。下一次启动将直接进入新的条目,并且不会再次显示该镜像。

更多信息请参见 fallback README

迁移

我们可以使用以下命令从 NVIndexpcrlock)迁移到签名策略(pcr-oracle

 # Remove the policy from the TPM2 and from the system
 /usr/lib/systemd/systemd-pcrlock remove-policy
 # Remove systemd-experimental package (reboot, as sdbootutil should
 # not find systemd-pcrlock)
 transactional-update pkg rm systemd-experimental
 # Remove .pcrlock files with components and variants
 rm -fr /var/lib/pcrlock.d
 # Remove pcrlock.json in /var and ESP
 rm /var/lib/systemd/pcrlock.json
 rm /boot/efi/EFI/systemd/pcrlock.json
 # Generate a RSA key pair
 pcr-oracle \
    --rsa-generate-key \
    --private-key /etc/systemd/tpm2-pcr-private-key.pem \
    --public-key /etc/systemd/tpm2-pcr-public-key.pem \
    store-public-key
 # Drop the "tpm2" keyslot from the LUKS2 device
 systemd-cryptenroll --wipe-slot=tpm2 $DEV
 # Enroll the private key
 systemd-cryptenroll \
   --tpm2-device=auto \
   --tpm2-public-key=/etc/systemd/tpm2-pcr-public-key.pem \
   --tpm2-public-key-pcrs=0,2,4,9 \
   $DEV
 # Generate new predictions with pcr-oracle
 sdbootutil update-predictions

要迁移回来

 # Install systemd-experimental (reboot, as sdbootutil should find
 # systemd-pcrlock)
 transactional-update pkg in systemd-experimental
 # Remove public and private keys fron /etc and public from ESP
 rm /etc/systemd/tpm2-pcr-private-key.pem
 rm /etc/systemd/tpm2-pcr-public-key.pem
 rm /boot/efi/EFI/systemd/tpm2-pcr-public-key.pem
 # Remove signed policy from /etc and ESP
 rm /etc/systemd/tpm2-pcr-signature.json
 rm /boot/efi/EFI/systemd/tpm2-pcr-signature.json
 # Drop the "tpm2" keyslot from the LUKS2 device
 systemd-cryptenroll --wipe-slot=tpm2 $DEV
 # Generate predictions and policy, and register it in the TPM2's NVRAM
 sdbootutil update-predictions
 # Enroll the keyslot in the LUKS2 device.  Will ask for the recovery
 # password
 systemd-cryptenroll \
   --tpm2-device=auto \
   --tpm2-pcrlock=/var/lib/systemd/pcrlock.json \
   $DEV

资源

[1] Boot Loader Specification: https://uapi-group.org/specifications/specs/boot_loader_specification/

[2] MicroOS image: https://build.opensuse.org/package/show/openSUSE:Factory/openSUSE-MicroOS

[3] Tumbleweed image: https://build.opensuse.org/package/show/openSUSE:Factory/kiwi-templates-Minimal

[4] Measured boot: https://en.opensuse.net.cn/Portal:MicroOS/RemoteAttestation#Measured_boot

[5] Systemd FDE: https://en.opensuse.net.cn/Systemd-fde

[6] Systemd-boot and Full Disk Encryption in Tumbleweed and MicroOS: https://news.opensuse.net.cn/2023/12/20/systemd-fde/

[7] MicroOS installer: https://get.opensuse.net.cn/microos/

[8] MicroOS sdboot 镜像:https://download.opensuse.org/tumbleweed/appliances/openSUSE-MicroOS.x86_64-kvm-and-xen-sdboot.qcow2