Skip to content

module.sig_enforce=1 导致 PVM 宿主内核启动掉进 initramfs ​

问题现象 ​

按照 PVM 部署文档安装 kernel-release-* 发布的 PVM 宿主内核并执行 deploy/pvm/grub/host_grub_config.sh 后,重启系统直接掉进 initramfs 救援 shell:

text
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

根因分析 ​

两个事实叠加导致故障:

  1. 社区发布内核的模块没有签名。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 等均返回空)。
  2. 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-gbc75224fca7ekernel-release-261012-1 deb未签名 —— 所有模块 modinfo -F signer 为空

发布 PVM 内核的相关配置(取自 /boot/config-<kver> 与 /proc/config.gz):

text
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 后的模块签名检查:

console
$ 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

恢复过程(实际执行的步骤):

  1. 重启 → GRUB → Advanced options for Debian GNU/Linux → 启动 6.12.113+deb13-amd64。

  2. 去掉该参数并重新生成 grub 配置:

    bash
    sed -i 's/ module\.sig_enforce=1//' /etc/default/grub
    update-grub
    reboot
  3. 重启进入 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-gbc75224fca7ekernel-release-261012-1 rpm未签名 —— 与 Debian 案例是同一份构建产物

openEuler 自带内核把自家证书内置进了内核,这正是 PVM 内核构建需要效仿的 模式:

text
# 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=""

模块签名检查:

console
$ 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 中开启模块签名:

text
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 脚本要配置的内核。

bash
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。

参考 ​