Ubuntu的UEFI安全启动(Secure Boot)本质上是一套硬件层面的信任链验证机制,它要求从固件到操作系统内核、再到引导加载程序的每一个环节都必须持有合法的数字签名,否则系统将拒绝加载。在Ubuntu系统中,这套机制默认由Microsoft的密钥和Ubuntu自己的签名密钥共同驱动。如果你想在Ubuntu上启用安全启动并确保引导签名验证正常工作,核心操作就是:确认UEFI固件已开启Secure Boot选项,确认shim引导加载程序已正确安装并签名,确认内核模块签名链完整,以及掌握如何用MOK(Machine Owner Key)管理自定义签名密钥。下面我会把每一步拆开讲透。
一、UEFI安全启动到底在验证什么
传统BIOS时代,系统启动时固件会直接读取硬盘第一个扇区的引导代码并执行,没有任何验证环节,这意味着恶意程序可以轻松替换引导程序。UEFI安全启动改变了这个逻辑:固件内部维护一个密钥数据库(db),里面存放着受信任的证书和签名哈希。启动时,固件会逐级验证:首先验证引导加载程序(如shim或grubx64.efi)的签名是否在db中;然后引导加载程序再验证内核镜像(vmlinuz)和初始ramdisk(initrd)的签名。任何一个环节签名不匹配,启动就会被终止。这就是所谓的"引导签名验证"。
在Ubuntu中,这个链条具体是这样的:UEFI固件验证shimx64.efi(由Canonical签名)→ shim验证grubx64.efi(由Canonical签名)→ grub验证vmlinuz和initrd(由Canonical签名)。如果你安装了第三方内核模块(比如NVIDIA驱动),这些模块也必须签名,否则内核加载时会拒绝它们。
二、如何确认Ubuntu当前的安全启动状态
在终端中执行以下命令即可查看当前状态:
mokutil --sb-state
如果输出"SecureBoot enabled",说明UEFI安全启动已开启。如果显示"SecureBoot disabled",你需要重启进入UEFI固件设置(通常按F2、F12、Del或Esc键,具体取决于主板品牌),在Boot或Security选项卡中找到Secure Boot并设为Enabled。注意:部分老主板可能不支持安全启动,或者开启后会导致某些硬件驱动无法加载。
另外还可以用这个命令查看详细信息:
mokutil --sb-state --verbose
它会告诉你当前启用的是哪个模式(User Mode还是Setup Mode)、是否有自定义密钥(MOK)等关键信息。
三、shim引导加载程序的作用与安装
shim是UEFI安全启动在Linux环境下的关键桥梁。因为微软只签名了微软自己的引导加载程序,Linux发行版无法直接让自己的grub获得微软签名。解决方案就是:Canonical用一个被微软签名的shim来引导,shim本身又内置了Ubuntu的公钥,从而可以验证后续的grub和内核。这就是"链式信任"的设计。
在大多数Ubuntu安装中,shim会自动安装。如果你手动安装或重建引导,需要确保以下包已安装:
sudo apt install shim-signed grub-efi-amd64-signed
安装完成后,检查EFI分区下是否存在shim文件:
ls /boot/efi/EFI/ubuntu/shimx64.efi
如果文件存在且大小正常(通常几百KB),说明shim已就位。如果缺失,说明引导环境有问题,需要重新安装grub-efi。
四、内核模块签名:第三方驱动的痛点
这是很多Ubuntu用户遇到安全启动问题的核心原因。当你启用安全启动后,内核会进入"锁定模式"(lockdown mode),拒绝加载未签名的内核模块。而NVIDIA闭源驱动、某些无线网卡驱动、VirtualBox等第三方模块通常没有经过Ubuntu签名,加载时会被内核直接拒绝,报错类似"module verification failed: signature and/or required key missing"。
解决方案有三种:
第一种,使用Ubuntu官方提供的签名版本。比如NVIDIA驱动,Ubuntu在其仓库中提供了经过签名的.deb包,安装时会自动完成签名注册:
sudo ubuntu-drivers install
或者手动安装特定版本:
sudo apt install nvidia-driver-535
第二种,如果官方没有签名版本,你需要用MOK(Machine Owner Key)自己签名。这需要生成一对密钥、用私钥签名模块、然后将公钥导入UEFI固件的MOK列表。具体步骤后面会详细讲。
第三种,如果你确实不需要安全启动的保护(比如在隔离测试环境中),可以在UEFI固件中关闭Secure Boot。但这会让你失去引导层面的防篡改保护,不建议在生产环境中这样做。
五、MOK密钥管理:自定义签名的完整流程
MOK(Machine Owner Key)是UEFI规范中专门为用户自定义签名设计的机制。它允许你生成自己的密钥对,用私钥签名内核模块,然后把公钥注册到固件中,这样你的模块就能通过验证。
步骤一:安装必要工具
sudo apt install mokutil sbsigntool openssl
步骤二:生成密钥对
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes -days 3650 -subj "/CN=My MOK/"
这会生成MOK.priv(私钥)和MOK.der(公钥证书)。私钥一定要妥善保管,公钥用于导入固件。
步骤三:签名内核模块
假设你要签名一个名为mymodule.ko的模块:
sudo sbsign --key MOK.priv --cert MOK.der --output mymodule.ko.signed mymodule.ko
步骤四:导入MOK到固件
sudo mokutil --import MOK.der
系统会提示你设置一个临时密码(这个密码在重启时需要输入,用于确认导入操作)。重启后会出现一个蓝色的MOK管理界面(MOK Manager),选择"Enroll MOK"并输入刚才设置的密码,完成公钥注册。之后重启,内核就会信任你签名的模块。
六、验证签名链是否完整
你可以用以下命令检查内核模块的签名状态:
modinfo -n vmlinuz | grep sig
或者更直观地查看内核日志中是否有签名相关的警告:
dmesg | grep -i "module signature"
如果看到大量"module verification failed"的信息,说明有模块没有通过签名验证。你需要逐一排查是哪些模块出了问题,然后决定是找签名版本还是自己签名。
另外,可以用efi-readvar工具查看当前UEFI变量中的签名数据库:
sudo apt install efitools sudo efi-readvar
这个命令会列出db、dbx、MOKList等变量的内容,帮你确认密钥链是否正确配置。
七、常见问题与排查思路
问题一:开启Secure Boot后系统无法启动。通常是因为shim或grub没有正确签名,或者EFI分区结构被破坏。解决方法是用Ubuntu Live USB启动,chroot到系统中重新安装grub-efi-amd64-signed。
问题二:安装NVIDIA驱动后黑屏或无法加载。这几乎都是签名问题。先确认是否安装了签名版驱动,如果没有,用MOK签名或切换到开源的nouveau驱动。
问题三:双系统环境下Windows和Ubuntu互相影响。如果你先装Windows再装Ubuntu,通常Ubuntu的shim会自动利用微软的密钥链工作。但如果你先装Ubuntu再装Windows,Windows的安全启动可能会覆盖UEFI密钥,导致Ubuntu无法启动。这时需要重新注册Ubuntu的MOK或者在UEFI中手动添加Ubuntu的密钥。
问题四:内核更新后模块签名失效。每次内核更新都会生成新的vmlinuz,如果新内核没有被正确签名或者签名链断裂,就会出问题。Ubuntu的自动更新机制通常会处理这个,但如果你手动编译内核,必须确保新内核也经过签名。
八、安全启动的实际价值与局限性
从安全角度看,UEFI安全启动能有效防止引导级rootkit和bootkit攻击,这类恶意软件在操作系统加载之前就运行,传统杀毒软件根本无法检测。对于服务器和企业环境,安全启动是合规要求的一部分,比如某些等保标准和国际安全框架都明确提到了引导完整性验证。
但它也有局限:安全启动只保护引导链,不保护运行时安全。一旦系统启动完成,内核运行起来后,安全启动的使命就结束了。它也无法防止已签名软件中的漏洞被利用。此外,对普通用户来说,MOK管理增加了操作复杂度,一旦密钥丢失或密码遗忘,恢复会比较麻烦。
总结来说,Ubuntu的UEFI安全启动是一套成熟且必要的机制。掌握shim的工作原理、学会MOK签名流程、理解内核模块签名要求,是每一个Ubuntu系统管理员和高级用户都应该具备的技能。不要因为怕麻烦就关闭它,而是要学会正确配置它,让它真正为你的系统安全服务。
