4、Bootloader签名与验证:GRUB2配置、shim引导加载器、内核签名与验证流程
安全启动走到Bootloader这一层,其实才是真正开始「较真」的地方。UEFI固件验完签名后,会把控制权交给引导加载器。如果这个环节被篡改,前面所有努力都白费了。
我个人习惯把Bootloader签名验证拆成三个核心模块:GRUB2、shim、内核。它们各自承担不同的信任链角色。下面我们一个一个说清楚。
4.1 整体信任链:谁签谁,谁验谁?
先看一张整体流程图,把关系理清。
说白了,这就是一条链:UEFI验shim,shim验GRUB2,GRUB2验内核。每一环都只信任上一环签过名的东西。我在项目中遇到过有人跳过shim直接签GRUB2,结果换了一台笔记本就启动不了——因为UEFI固件不认识你的证书。
4.2 shim引导加载器:微软帮你铺的路
shim是什么?它是个很小的EFI程序,唯一使命就是验证并加载GRUB2。为什么需要它?因为要让GRUB2通过UEFI Secure Boot,你得让GRUB2的签名被UEFI固件信任。但GRUB2更新频繁,每次更新都找微软签名不现实。
shim的巧妙之处在于:它本身由微软签名(所以UEFI固件信任它),但它内部包含一个机器所有者密钥(MOK)数据库。你可以用自己的密钥签GRUB2,然后把公钥加到MOK里。shim启动时会用MOK验GRUB2。
4.3 GRUB2签名配置实战
GRUB2的签名,我建议分两步走:先签核心文件,再签模块。别偷懒只签一个,否则启动时加载未签名模块照样报错。
4.3.1 准备签名工具和密钥
# 安装签名工具
sudo apt install sbsigntool
# 生成自己的签名密钥(如果还没有)
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key \
-out MOK.crt -days 3650 -nodes -subj "/CN=My Secure Boot Key/"
# 将公钥转换为EFI签名格式
cert-to-efi-sig-list -g "$(uuidgen)" MOK.crt MOK.esl
sign-efi-sig-list -k MOK.key -c MOK.crt MOK MOK.esl MOK.auth
嗯,这里要注意:uuidgen生成一个随机GUID,每个密钥对应该有一个唯一GUID。我见过有人所有机器用同一个GUID,虽然也能用,但不符合最佳实践。
4.3.2 签名GRUB2核心文件
# 找到GRUB2的EFI文件
sudo find /boot -name "grub*.efi"
# 签名
sudo sbsign --key MOK.key --cert MOK.crt \
/boot/efi/EFI/ubuntu/grubx64.efi \
--output /boot/efi/EFI/ubuntu/grubx64.efi.signed
# 替换原文件
sudo mv /boot/efi/EFI/ubuntu/grubx64.efi.signed \
/boot/efi/EFI/ubuntu/grubx64.efi
4.3.3 签名GRUB2模块
GRUB2有很多模块(如ext2.mod、lvm.mod),它们默认不签名。如果你启用了Secure Boot,GRUB2加载未签名模块会直接拒绝。
# 批量签名所有模块
for mod in /boot/grub/x86_64-efi/*.mod; do
sudo sbsign --key MOK.key --cert MOK.crt "$mod" --output "$mod"
done
# 别忘了签名grub.cfg的校验文件(如果有)
sudo sbsign --key MOK.key --cert MOK.crt \
/boot/grub/grub.cfg.sig --output /boot/grub/grub.cfg.sig
4.4 内核签名与验证流程
内核签名是最后一道关卡。GRUB2加载内核前,会检查内核文件的签名。如果签名无效或缺失,Secure Boot会阻止启动。
4.4.1 签名内核镜像
# 使用sbsign签名vmlinuz
sudo sbsign --key MOK.key --cert MOK.crt \
/boot/vmlinuz-$(uname -r) \
--output /boot/vmlinuz-$(uname -r).signed
# 替换原文件
sudo mv /boot/vmlinuz-$(uname -r).signed /boot/vmlinuz-$(uname -r)
4.4.2 签名内核模块
内核模块也需要签名,否则加载时会报Required key not available。我建议用sign-file工具,它是内核源码自带的。
# 安装内核头文件(包含sign-file)
sudo apt install linux-headers-$(uname -r)
# 签名单个模块
sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 MOK.key MOK.crt \
/lib/modules/$(uname -r)/kernel/drivers/xxx/xxx.ko
# 批量签名所有模块
for mod in $(find /lib/modules/$(uname -r) -name "*.ko"); do
sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 MOK.key MOK.crt "$mod"
done
4.4.3 验证签名是否生效
# 检查内核签名
sbverify --cert MOK.crt /boot/vmlinuz-$(uname -r)
# 检查模块签名
modinfo -F signer /lib/modules/$(uname -r)/kernel/drivers/xxx/xxx.ko
如果输出显示签名者信息,说明签名成功。如果报错,检查密钥路径和文件权限。
4.5 将MOK密钥注册到shim
签名完所有文件后,最后一步是把你的公钥注册到shim的MOK数据库里。这样shim启动时才会信任你的签名。
# 使用mokutil注册MOK
sudo mokutil --import MOK.crt
# 设置一个密码(重启后需要输入)
# 重启后会自动进入MOK管理界面,选择Enroll MOK并输入密码
# 查看已注册的MOK
mokutil --list-enrolled
4.6 常见问题与排查思路
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
启动时显示Verification failed: (0x1A) Security Violation |
GRUB2或内核未签名,或签名无效 | 重新签名对应文件,检查密钥是否匹配 |
| 启动后进入grub rescue | GRUB2模块未签名或签名过期 | 重新签名所有.mod文件 |
加载内核模块报Required key not available |
内核模块未签名 | 使用sign-file签名模块 |
| MOK注册后重启没有提示 | shim版本不支持,或MOK文件格式错误 | 检查.auth文件格式,尝试重新生成 |
说实话,大部分问题都出在「签名链断了一环」。你想想看,UEFI验shim没问题,shim验GRUB2没问题,但GRUB2加载内核时报错——那八成是内核签名有问题。逐级排查,用sbverify和mokutil验证每一环,很快就能定位。
核心要点回顾:
- shim是微软签名的「桥接层」,负责验GRUB2
- GRUB2和内核使用MOK密钥签名
- 签名要覆盖核心文件、模块、内核镜像
- MOK注册是最后一步,别忘了重启后确认
- 每次更新内核或GRUB2后,重新签名