在Debian系统上折腾Secure Boot,最常见的问题不是它有多难,而是网上很多教程会让你直接关掉它。关掉确实省事,但如果你需要双系统、或者处于对底层安全有要求的生产环境,正确配置Secure Boot并给内核签名才是正道。问题的核心在于,Debian默认内核虽然支持Secure Boot,但当你自己编译内核、安装第三方驱动模块或者使用DKMS时,系统会因签名缺失而拒绝加载。下面直接讲具体的配置方法和签名流程,不绕弯子。
理解当前的签名机制链动手之前,需要先搞清楚Debian的信任链是怎么走的。固件信任微软的KEK,微软给Debian的Shim引导器签名,Shim内嵌了Debian的CA证书,然后Shim去验证由Debian私钥签名的GRUB和内核。我们后续要做的,就是生成自己的机器所有者密钥,也就是MOK,把它注册到Shim的信任列表里,然后用这个MOK给我们自己编译的内核或模块签名。这样既不破坏安全启动的链条,又能让自定义组件通过验证。
生成机器所有者密钥首先用openssl生成一对用于签名的证书。这一步在任意目录执行即可,建议放在一个安全且不容易误删的位置,比如/etc/ssl/mok。执行以下命令创建目录并生成密钥对:
sudo mkdir -p /etc/ssl/mok cd /etc/ssl/mok sudo openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -days 36500 -subj "/CN=My Custom Kernel Key/" -nodes
上面的命令生成一个有效期100年的自签名证书。MOK.priv是私钥,必须严格保护权限,MOK.der是公钥证书,后续要注册到固件里。执行完后立刻把私钥权限收紧,避免被普通用户读取。
sudo chmod 600 MOK.priv将MOK注册到Shim
有了密钥对,接下来要把公钥导入到系统信任链。使用mokutil工具发起注册请求,系统会要求你设置一个一次性密码,这个密码在重启后的MokManager界面里输入,不是日常登录密码,可以设简单些,用完即弃。
sudo mokutil --import MOK.der
执行后会提示输入密码并确认。完成后重启系统。重启过程中,Shim会检测到有待处理的MOK请求,自动进入蓝色的MokManager界面。选择Enroll MOK,然后Continue,再选择Yes确认,输入刚才设置的密码。找到你的证书并确认注册,最后选择Reboot重启。系统再次启动后,新注册的MOK就生效了。可以用以下命令验证证书是否已被系统识别:
sudo mokutil --list-enrolled为官方预编译内核签名
如果你只是需要让Debian官方仓库的内核在Secure Boot开启的状态下正常工作,通常不需要额外签名,因为Debian内核已经由官方密钥签名过了。但如果你更新了某些带内核模块的驱动,或者安装了VirtualBox、NVIDIA闭源驱动这类DKMS模块,情况就不同了。这些模块编译后存放在/lib/modules/$(uname -r)/updates/dkms/目录下,没有被签名,内核会拒绝加载。这时需要手动对这些内核模块签名。
签名用的脚本在Debian里通常由内核头文件包提供,位于/usr/src/linux-headers-$(uname -r)/scripts/sign-file。如果提示找不到,需要确认linux-headers包已安装。签名的命令格式如下,以签名vboxdrv模块为例:
sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 /etc/ssl/mok/MOK.priv /etc/ssl/mok/MOK.der /lib/modules/$(uname -r)/misc/vboxdrv.ko
但一个一个模块手动签名效率太低,更合理的做法是写一个脚本,在DKMS自动编译模块后触发签名。DKMS本身支持POST_BUILD钩子,可以在/etc/dkms/framework.conf里配置,或者直接在每次内核更新后统一对updates目录下的所有ko文件签名。下面是一个批量签名的脚本示例,保存为/etc/kernel/postinst.d/zz-sign-modules并赋予执行权限:
#!/bin/bash
cd /lib/modules/$(uname -r)
for ko in $(find updates -name '*.ko'); do
/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 /etc/ssl/mok/MOK.priv /etc/ssl/mok/MOK.der "$ko"
done
sudo chmod +x /etc/kernel/postinst.d/zz-sign-modules
这样每次安装新内核或者DKMS模块重新编译时,签名操作会自动执行,省去重复劳动。
编译自定义内核并签名对于需要自定义内核的场景,比如精简驱动或者开启特定优化参数,编译完成后必须对整个内核镜像进行签名。Debian内核编译通常生成vmlinuz文件,这个文件本质上是经过压缩的内核可执行文件,签名时需要用到sbsign工具。先安装sbsigntool包:
sudo apt install sbsigntool
假设编译好的内核位于/boot/vmlinuz-6.6.0-custom,签名的命令如下:
sudo sbsign --key /etc/ssl/mok/MOK.priv --cert /etc/ssl/mok/MOK.der /boot/vmlinuz-6.6.0-custom --output /boot/vmlinuz-6.6.0-custom.signed
签名成功后,需要更新引导加载器配置,让它指向签名后的内核。如果使用GRUB,编辑/etc/default/grub,修改GRUB_CMDLINE_LINUX相关参数,然后执行update-grub。GRUB会自动识别签名过的内核并生成对应的菜单项。如果手动维护引导项,需要确保加载的是带签名的内核文件。
处理initramfs的签名问题很多人忽略的一点是,仅仅签名内核还不够。initramfs镜像包含了大量内核模块,这些模块在启动初期就会被加载。如果initramfs里的模块没有签名,系统照样会卡住。Debian的update-initramfs工具在生成initramfs时,默认不会对里面的模块签名,但可以通过配置让它在生成过程中自动签名。具体做法是在/etc/initramfs-tools/hooks/目录下创建一个钩子脚本,在模块复制完成后调用sign-file签名。
创建一个文件/etc/initramfs-tools/hooks/sign-modules,内容如下:
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
for module in $(find ${DESTDIR}/lib/modules -name '*.ko'); do
/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 /etc/ssl/mok/MOK.priv /etc/ssl/mok/MOK.der "$module"
done
赋予执行权限后,每次执行update-initramfs -u时,钩子会自动对initramfs内的模块签名。这个步骤能避免启动时出现“required key not available”的错误。
排查签名失败和常见陷阱即使按照流程操作,仍可能遇到一些细节问题。如果注册MOK后重启发现签名仍然无效,先用dmesg | grep -i secureboot确认Secure Boot确实处于开启状态。如果输出显示Secure Boot disabled,说明固件层面就没有开启,需要进BIOS设置。另一个常见问题是密钥权限,如果私钥文件权限不是600,某些工具会拒绝读取,直接导致签名失败。
模块加载失败时,用modprobe -v 模块名观察详细输出,再配合dmesg查看内核日志。常见错误信息包括“PKCS#7 signature not signed with a trusted key”,这表示模块签名未被信任,要么是MOK没注册成功,要么是用错了密钥。还有一种情况是内核配置里CONFIG_MODULE_SIG_FORCE被设为y,强制所有模块必须签名,这时候漏掉任何一个ko文件都会导致加载失败。对于Debian官方内核,这个选项默认是开启的,所以一定要确保DKMS模块全部签名。
如果遇到Shim版本过旧导致MokManager界面不出现,可以考虑从Debian backports仓库更新shim-signed和shim-helpers包。更新后重新执行mokutil --import操作,通常能解决兼容性问题。
维护密钥和长期管理MOK私钥一旦丢失,所有用该密钥签名的内核和模块都需要重新签名并重新注册新密钥,所以务必妥善备份MOK.priv文件。建议将私钥离线保存在加密存储介质中,同时记录下当初注册MOK时使用的证书信息。如果系统重装或者更换主板,需要重新注册MOK,但私钥和证书可以复用,签名过的内核和模块不需要重新签名。
对于管理多台机器的场景,可以在一台安全机器上生成统一的MOK,然后分发公钥证书到各台机器注册,私钥集中保管。这样所有机器信任同一个签名源,模块和内核可以通用,降低维护复杂度。
定期检查签名状态也是个好习惯,可以用下面的一行命令快速查看当前内核里所有未签名或签名无效的模块:
for mod in $(lsmod | tail -n +2 | awk '{print $1}'); do modinfo $mod | grep -q "signer" || echo "$mod 未签名或签名无效"; done
通过这套完整的签名和注册流程,Debian系统在保持Secure Boot开启的状态下,既能运行官方内核,也能加载自定义内核和第三方内核模块,安全性和灵活性兼得。整个过程不依赖任何外部网络服务,完全在本地完成,符合离线环境和高安全需求场景的要求。
