Secure Boot 加固实战:UEFI 安全启动配置

UEFI 安全启动加固(Secure Boot)是服务器与工作站上防止引导链被劫持的最后一道防线。近年多起 Linux 与 ESXi 平台的 bootkit、恶意 EFI 模块事件表明,一旦攻击者在系统盘或 ESP 分区写入未签名的引导程序,传统杀软与文件完整性检查几乎无从发现。本文围绕 UEFI 安全启动加固的落地流程展开,覆盖密钥体系构建、引导链签名、TPM 2.0 度量启动与自动解密,并给出可直接复制执行的命令与验证方法。

适用场景

  • 物理服务器、边缘节点、办公终端在交付前做基线加固,需要阻断 bootkit 与固件层持久化。
  • 等保 2.0、ISO 27001 审计要求「可信启动」与「启动完整性校验」的落地实现。
  • 全盘加密(LUKS)环境希望取消手工输入口令,改为由 TPM 2.0 绑定启动状态自动解封密钥。
  • 需要区分「合规要求」与「营销宣传」:安全启动只保证引导链未被篡改,不负责运行时恶意进程查杀,也不替代 EDR。

前置条件

  • 固件:UEFI 模式启动(非 Legacy/CSM),固件设置中可访问 Secure Boot 与密钥管理项。
  • 操作系统:主流发行版 x86_64,内核启用 CONFIG_EFI 与 CONFIG_MODULE_SIG。
  • 存储:ESP 分区容量 ≥ 512 MB;如需 TPM 自动解密,则准备 LUKS2 加密卷。
  • 硬件:TPM 2.0 芯片(/sys/class/tpm/tpm0 存在)。
  • 工具:sbctl(密钥与签名管理)、mokutil、tpm2-tools、sbsigntool、bootctl。
  • 权限:root 权限,且已备份 ESP 分区与 LUKS 头部(cryptsetup luksHeaderBackup)。

安装工具链:

# Debian / Ubuntu
sudo apt update && sudo apt install -y sbctl mokutil tpm2-tools sbsigntool

# RHEL / Rocky / AlmaLinux
sudo dnf install -y sbctl mokutil tpm2-tools sbsigntool

# Arch Linux
sudo pacman -S sbctl mokutil tpm2-tools sbsigntool

原理说明

UEFI 安全启动依赖四个签名数据库:PK(Platform Key,平台密钥,唯一)、KEK(Key Exchange Key,用于更新 db/dbx)、db(允许签名的证书白名单)、dbx(吊销名单)。固件在加载每个 EFI 可执行文件前校验其签名是否落在 db 且不在 dbx 中,校验失败即拒绝执行。当 PK 被清空时,固件进入 Setup Mode,此时可写入自有密钥;一旦写入 PK,即转为 User Mode。

TPM 2.0 则提供「度量启动」能力:固件、引导程序、内核按顺序把各阶段代码与配置的哈希值写入 PCR(Platform Configuration Register)。其中 PCR 0 记录固件代码,PCR 4/5 记录引导程序,PCR 7 记录安全启动状态。系统可用 tpm2_pcrread 读取这些值,也可将 LUKS 密钥封印(seal)到指定 PCR 组合——只有当启动链未被篡改、PCR 值匹配时才允许解封,从而实现「安全启动 + 全盘加密」的联动。

需要明确边界:Secure Boot 只校验签名与度量,不检测引导后的恶意行为;它与内存保护、SELinux、EDR 属于不同层次,不能互相替代。

操作步骤

步骤一:确认固件与当前状态

# 查看安全启动当前状态
sudo sbctl status

# 查看引导管理器与安全启动详情
bootctl status

# mokutil 侧确认(部分发行版需先加载 efivarfs)
sudo mokutil --sb-state

# 确认 TPM 设备存在
ls -l /sys/class/tpm/tpm0
sudo tpm2_getcap properties-fixed | head -20

若 sbctl status 显示 Setup Mode: Enabled,说明固件尚未登记平台密钥,可直接进入步骤三。若显示 Secure Boot: enabled 且使用厂商密钥,则需先进入固件设置清除 PK,或在 Linux 侧执行 sbctl setup --setup 请求进入 Setup Mode。

步骤二:登记自定义密钥

# 生成 PK/KEK/db 全套密钥(默认存放 /var/lib/sbctl/keys)
sudo sbctl create-keys

# 登记密钥,-m 表示同时保留微软第三方 CA 证书
# 保留微软 CA 是为了兼容部分显卡/网卡 OpROM 与 Windows 双引导,生产环境建议保留
sudo sbctl enroll-keys -m

# 再次确认状态
sudo sbctl status

登记成功后,sbctl status 中 Vendor Keys 会变为 microsoft,Setup Mode 变为 Disabled,表示固件已进入 User Mode。

步骤三:签名引导链

sbctl 需要用 -s(save)把待签名文件路径登记到数据库,这样后续固件更新或内核升级后可批量重新签名。

# 1) 签名系统的 EFI 引导程序(路径按实际发行版调整)
sudo sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI
sudo sbctl sign -s /boot/EFI/systemd/systemd-bootx64.efi

# 2) 签名内核镜像
sudo sbctl sign -s /boot/vmlinuz-linux
sudo sbctl sign -s /boot/initramfs-linux.img    # 若引导方式需要度量 initramfs

# 3) 列出已登记与已签名文件
sudo sbctl list-files

# 4) 校验签名是否通过
sudo sbctl verify

内核升级或引导程序更新后,需要重新执行签名,否则下次启动会被固件拒绝。推荐通过 pacman hook(Arch)或 dracut/kernel-install 的 post hook 自动触发:

# Arch Linux 的 pacman hook 示例:/etc/pacman.d/hooks/99-sbctl-sign.hook
[Trigger]
Operation = Install
Operation = Upgrade
Type = Path
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/systemd/boot/efi/*.efi

[Action]
Description = Signing EFI binaries for Secure Boot...
When = PostTransaction
Exec = /usr/bin/sbctl sign-all

步骤四:启用安全启动并验证

# 重启并进入固件设置,把 Secure Boot 设为 Enabled
sudo systemctl reboot

# 重启后确认内核视角的状态
bootctl status | grep -i "secure boot"
sudo sbctl status | grep -E "Setup Mode|Secure Boot"

# 内核日志中的确认信息(关键行)
sudo dmesg | grep -i "secure boot"
sudo journalctl -k -b | grep -iE "secure boot|lockdown"

正常输出应包含 Secure boot enabled 或 Kernel is locked down,说明引导链已通过校验且内核进入锁定模式(Lockdown)。

步骤五:TPM 2.0 度量启动与自动解密

确认 LUKS2 卷并绑定 TPM,PCR 0(固件)与 PCR 7(安全启动状态)是常用组合:

# 查看当前 PCR 值,记录基线
sudo tpm2_pcrread sha256:0,7

# 把 LUKS 密钥封印到 TPM,绑定 PCR 0 与 7
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/nvme0n1p2

# 查看已登记的解锁方式
sudo systemd-cryptenroll /dev/nvme0n1p2

# 确认 crypttab 中启用了 tpm2 选项
grep -n "tpm2" /etc/crypttab

若引导链或固件发生变化导致 PCR 不匹配,系统会拒绝自动解封,此时需回退到口令解锁并重新执行 enroll。

配置验证

  • sudo sbctl verify:所有登记文件应返回 Signed,无 Not Signed。
  • sudo sbctl status:Secure Boot 为 enabled、Setup Mode 为 Disabled。
  • bootctl status:输出 Secure Boot: enabled (user)。
  • sudo dmesg | grep -i "secure boot":出现 Secure boot enabled。
  • 反向验证:尝试启动未签名内核(如旧版本 vmlinuz),应被固件拒绝并提示安全校验失败,证明策略确实生效。

常见问题(FAQ)

Q1:开启安全启动后第三方驱动(NVIDIA、VirtualBox、DKMS 模块)无法加载怎么办?

这是内核模块签名问题,与 UEFI 引导签名是两条不同的信任链。解决路径有两步:其一,用 MOK(Machine Owner Key)为 DKMS 模块签名并登记;其二,确保内核启用了 CONFIG_MODULE_SIG。

# 生成 MOK 密钥对
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der \
  -nodes -days 3650 -subj "/CN=DKMS Module Signing/"

# 登记 MOK 到固件(会提示设置一次性口令,重启后在 MOK Manager 中确认)
sudo mokutil --import MOK.der

# DKMS 构建时自动签名:把配置写入 /etc/dkms/framework.conf
echo 'mok_signing_key="/root/MOK.priv"'    | sudo tee -a /etc/dkms/framework.conf
echo 'mok_certificate="/root/MOK.der"'     | sudo tee -a /etc/dkms/framework.conf

# 重建并检查签名状态
sudo dkms autoinstall
modinfo nvidia | grep -i signer   # 应显示签署者信息

Q2:内核或引导程序升级后忘记重新签名,导致系统无法启动,如何恢复?

恢复方式取决于引导管理方式。若为 systemd-boot,可在固件设置中临时关闭 Secure Boot 进入系统,执行 sudo sbctl sign-all 后重新启用;若为 GRUB 且保留了可启动的旧内核条目,可在菜单中选择旧内核进入。

# 进入系统后批量重签全部已登记文件
sudo sbctl sign-all

# 校验
sudo sbctl verify

# 排查遗漏:列出所有可执行 EFI 与内核,确认是否都已登记
sudo find /boot /boot/efi -type f \( -name '*.efi' -o -name 'vmlinuz*' \) 2>/dev/null
sudo sbctl list-files

为避免再次发生,应把 sbctl sign-all 固化到包管理器 hook 与内核安装流程中,并在变更后执行 sbctl verify 作为发布前检查项。

Q3:TPM 自动解密突然失败,提示「No matching PCR」怎么办?

常见原因是固件升级、BIOS 设置变更或引导程序更新导致 PCR 0/7 发生变化。此时系统应回退到 LUKS 口令解锁。

# 重新读取 PCR 并与基线比对
sudo tpm2_pcrread sha256:0,7

# 拆除旧的 TPM 绑定并重新封印
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/nvme0n1p2
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/nvme0n1p2

总结

UEFI 安全启动加固的核心是三条链路的闭环:密钥链(PK/KEK/db 自有化,避免依赖厂商默认密钥)、签名链(引导程序与内核全部签名并纳入自动化 hook)、度量链(TPM PCR 绑定,实现启动状态与磁盘解密的联动)。实施顺序建议为:先备份 ESP 与 LUKS 头部,再进入 Setup Mode 登记密钥,随后签名引导链并验证,最后接入 TPM。运维上必须把「重新签名」作为内核升级的固定步骤,否则一次升级就可能让设备无法启动。

安全启动解决的是引导链完整性问题,与运行时防护互补而非替代。完整的服务器加固还应叠加 SELinux 强制模式、内核参数收紧、auditd 审计与文件完整性监控,形成从固件到应用的分层防线。