module.sig_enforce=1 导致 PVM 宿主内核启动掉进 initramfs
问题现象
按照 PVM 部署文档安装 kernel-release-* 发布的 PVM 宿主内核并执行 deploy/pvm/grub/host_grub_config.sh 后,重启系统直接掉进 initramfs 救援 shell:
Gave up waiting for root file system device. Common problems:
- Boot args (cat /proc/cmdline)
- Check rootdelay= (did the system wait long enough?)
- Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/mapper/openstack--dev--vg-root does not exist. Dropping to a shell
BusyBox v1.37.0 (Debian 1:1.37.0-6+b9) built-in shell (ash)
(initramfs)通过 GRUB「Advanced options」子菜单仍可正常启动发行版自带内核。
环境信息
- Cube Sandbox 版本:v0.7.x(撰写时的部署脚本)
- 部署模式:PVM(无
/dev/kvm,由 PVM 宿主内核提供 KVM 能力) - 宿主机 OS / 内核:VMware 上的 Debian 13 (trixie) —— 见下方实测案例; ESXi 上也有一致症状 (#1949)
- 相关组件:
deploy/pvm/grub/host_grub_config.sh
根因分析
两个事实叠加导致故障:
- 社区发布内核的模块没有签名。pvm_host 内核配置里
CONFIG_MODULE_SIG=y,但CONFIG_MODULE_SIG_ALL未启用、CONFIG_SYSTEM_TRUSTED_KEYS="",构建产物中的.ko文件全部没有签名 (modinfo -F signer对dm-mod.ko、kvm.ko、scsi_mod.ko、mptspi.ko、vmw_pvscsi.ko等均返回空)。 host_grub_config.sh按设计在内核启动参数中启用module.sig_enforce=1。签名强制开启后,内核拒绝加载所有未签名 模块,存储驱动(如 VMware 上的mptspi、LVM 的dm-mod)无法加载, 根分区设备自然找不到。
实测案例:VMware 上的 Debian 13 (trixie)
宿主机:Debian 13 (trixie) VMware 虚拟机,未开启嵌套虚拟化,磁盘挂载在 LSI 53c1030 Fusion-MPT 控制器(mptspi)上,根分区位于 LVM (/dev/mapper/openstack--dev--vg-root)。
| 内核 | 来源 | 模块签名情况 |
|---|---|---|
6.12.113+deb13-amd64(发行版) | Debian | 已签名 —— signer='Build time autogenerated kernel key',压缩格式 .ko.xz |
6.6.117-opencloudos9.cubesandbox.pvm.host-gbc75224fca7e | kernel-release-261012-1 deb | 未签名 —— 所有模块 modinfo -F signer 为空 |
发布 PVM 内核的相关配置(取自 /boot/config-<kver> 与 /proc/config.gz):
CONFIG_MODULE_SIG=y
# CONFIG_MODULE_SIG_FORCE is not set
# CONFIG_MODULE_SIG_ALL is not set
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_MODULE_SIG_HASH="sha512"
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""安装 deb 后的模块签名检查:
$ modinfo -F signer /lib/modules/6.6.117-...pvm.host/kernel/drivers/md/dm-mod.ko
(空)
$ modinfo -F signer /lib/modules/6.12.113+deb13-amd64/kernel/drivers/md/dm-mod.ko.xz
Build time autogenerated kernel key恢复过程(实际执行的步骤):
重启 → GRUB →
Advanced options for Debian GNU/Linux→ 启动6.12.113+deb13-amd64。去掉该参数并重新生成 grub 配置:
bashsed -i 's/ module\.sig_enforce=1//' /etc/default/grub update-grub reboot重启进入 PVM 内核后:
kvm_pvm正常加载、/dev/kvm出现,Cube Sandbox 安装和运行一切正常。
提示:该平台上磁盘顺序(sda/sdb)在重启之间可能变化。除 LVM 根分区外,
/etc/fstab中的挂载项建议使用UUID=。
实测案例:VMware 上的 openEuler 24.03 LTS-SP4
宿主机:openEuler 24.03 LTS-SP4 VMware 虚拟机,未开启嵌套虚拟化,100 GiB 空白数据盘用于 /data/cubelet,根分区为 LVM(ext4)。
| 内核 | 来源 | 模块签名情况 |
|---|---|---|
6.6.0-159.4.3.154.oe2403sp4.x86_64(发行版) | openEuler | 已签名 —— signer='openEuler kernel ICA 1',压缩格式 .ko.xz |
6.6.117-opencloudos9.cubesandbox.pvm.host-gbc75224fca7e | kernel-release-261012-1 rpm | 未签名 —— 与 Debian 案例是同一份构建产物 |
openEuler 自带内核把自家证书内置进了内核,这正是 PVM 内核构建需要效仿的 模式:
# openEuler 自带内核
CONFIG_MODULE_SIG=y
# CONFIG_MODULE_SIG_FORCE is not set
CONFIG_MODULE_SIG_KEY=""
CONFIG_SYSTEM_TRUSTED_KEYS="certs/openeuler-cert.pem"
# PVM 发布内核
CONFIG_MODULE_SIG=y
# CONFIG_MODULE_SIG_ALL is not set
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_SYSTEM_TRUSTED_KEYS=""模块签名检查:
$ modinfo -F signer /lib/modules/6.6.0-159.../kernel/drivers/md/dm-mod.ko.xz
openEuler kernel ICA 1
$ modinfo -F signer /lib/modules/6.6.117-...pvm.host/kernel/drivers/md/dm-mod.ko
(空)结论:PVM 内核构建产物与宿主发行版无关——未签名模块和 module.sig_enforce=1 的风险在 RPM 系发行版(openEuler、CentOS 等)上完全 相同,恢复和规避方法也一样。
解决方案
1. 恢复主机
参照上方实测案例中的恢复步骤:从 GRUB 启动发行版旧内核,然后从 /etc/default/grub 中移除 module.sig_enforce=1 并重新生成 grub 配置。
2. 在内核构建侧修复,grub 脚本保持启用
PR #2053 不会弱化 host_grub_config.sh:它继续无条件追加 module.sig_enforce=1。根因改为在 内核构建侧修复——在 deploy/pvm/configs/pvm_host 中开启模块签名:
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_ALL=y # modules_install 时为所有内核内置模块签名
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" # 私钥 + 匹配的 X.509 证书
CONFIG_SYSTEM_TRUSTED_KEYS="" # 签名密钥的公钥证书会自动嵌入内核启用 CONFIG_MODULE_SIG_ALL=y 后,make modules_install 会用 CONFIG_MODULE_SIG_KEY 为所有内核内置模块签名;构建过程还会提取匹配的 公钥证书(certs/signing_key.x509)并嵌入内核的 trusted keyring,因此 内核只信任它自己发布的模块——这正是让无条件的 module.sig_enforce=1 变得安全的原因。
用该配置重新构建并发布 kernel-release-* 产物。仍在运行旧(未签名)发布 内核的主机,必须先安装重新构建的签名内核,再重新执行 host_grub_config.sh,否则强制签名仍会拒绝其模块。该方向已用自建内核 验证:用更新后配置构建的 Debian 13 deb 与 openEuler 24.03 rpm 均可在 module.sig_enforce=1 下启动,kvm_pvm//dev/kvm 正常,未签名模块被拒、 用构建密钥签名的模块可加载。
上线时还有两个注意点:
- 安装新内核后,移除旧的未签名 PVM 内核包(或至少不要选择其 GRUB 入口)。
module.sig_enforce=1是全局生效的,选中旧入口仍会掉进 initramfs。 - 用新的
kernel-release-*tag 发布重建后的产物,并提升包版本。该配置 改动不会改变内核 release 字符串,所以相同版本的 RPM 不会被自动升级; 给LOCALVERSION加后缀或显式提升包版本,才能让签名内核成为真正的升级。
3. 任意主机上的快速风险检查
要检查所有已安装的 PVM 内核,而不只是当前运行的内核:恢复路径会启动 发行版内核,此时 uname -r 不一定就是 grub 脚本要配置的内核。
for kdir in /lib/modules/*pvm.host*; do
[ -d "$kdir" ] || continue
kver=$(basename "$kdir")
echo "== $kver"
grep -qw "module.sig_enforce=1" /proc/cmdline && echo " enforcement: ON"
grep -E "^CONFIG_MODULE_SIG_FORCE=" "/boot/config-$kver" 2>/dev/null \
|| zgrep -E "^CONFIG_MODULE_SIG_FORCE=" /proc/config.gz 2>/dev/null
# 遇到第一个没有签名的模块就停止:签名强制会拒绝加载它。
find "$kdir" \( -type f -o -type l \) -name '*.ko*' 2>/dev/null |
while IFS= read -r m; do
[ -n "$(modinfo -F signer "$m" 2>/dev/null)" ] || { echo " unsigned: $m"; break; }
done
done高危状态 = 强制签名已开启(cmdline、grub 配置或 CONFIG_MODULE_SIG_FORCE=y),而任一已安装 PVM 内核中任意模块的 signer 为空或 modinfo 无法读取。此时下一次启动就会再次掉进 initramfs。 注意 modinfo -F signer 只能证明存在签名字段,不能证明内核信任该签名 密钥:用内核不信任的密钥签名的模块在强制模式下同样会被拒绝,而这种情况 无法在启动前从用户态检出。
4. 树外与 DKMS 模块
主机上必须加载的任何树外/DKMS 模块,必须用内核信任的同一密钥签名 (scripts/sign-file),否则会被强制模式拒绝。无法用发布签名密钥签名的 模块,应改为编译进内核。
注意:本次修改之前发布的
kernel-release-*产物模块仍未签名。必须先 用更新后的deploy/pvm/configs/pvm_host重新构建,才能在运行这些旧内核 的主机上安全使用无条件的module.sig_enforce=1。
参考
- 相关 PR:#2053
- 可能相关的 issue:#1949
- 内核文档:Module Signing