ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

UEFI安全启动下安装NVIDIA驱动:MOK签名实战指南

UEFI安全启动下安装NVIDIA驱动:MOK签名实战指南 去年帮朋友装一台双系统工作站主板默认开启 Secure BootUbuntu 22.04 装好后系统一切正常直到准备上 NVIDIA 显卡驱动时内核直接拒绝加载模块接着就是黑屏、登录循环、驱动卸载又重装来回折腾。折腾到最后我才意识到UEFI 安全启动模式下安装 NVIDIA 驱动并不是简单的 apt install 就能收工真正的核心难点在于签名验证NVIDIA 闭源驱动的内核模块没有被厂商用 UEFI 证书签名Ubuntu 内核在安全启动状态下默认不加载这种模块。这篇文章把整个过程完整讲清楚包括 UEFI 安全启动和内核模块签名的关系、三种安装方式的取舍、以及如何通过 MOKMachine Owner Key自己给驱动签名让 Secure Boot 和 NVIDIA 驱动和平共处。无论你是刚装好 Ubuntu 准备装显卡驱动的新手还是已经在安全启动模式下踩过坑的老手这篇内容都能帮你省下至少半天排查时间。1. 安全启动为什么会拦下 NVIDIA 驱动先认清敌人是谁很多人第一次遇到这个问题时第一反应是“把 Secure Boot 关掉不就行了”。确实可以但这等于把安全机制放弃了。想要既不关安全启动又能正常用 NVIDIA 驱动得先理解这背后到底发生了什么。1.1 UEFI 安全启动到底在保护什么UEFI 是取代传统 BIOS 的固件标准而 Secure Boot 是 UEFI 规范里的一项安全功能。它做的事情可以类比成机场安检从你按下电源键开始主板固件只允许加载带有受信任签名的启动加载器然后启动加载器再校验内核一层一层往下验证确保整个启动链路上的每个环节都没有被篡改。微软要求所有预装 Windows 的电脑默认开启 Secure Boot。所以你的主板几乎一定是开启状态除非你进 BIOS 手动关掉。Ubuntu 本身是支持安全启动的它的 shimx64.efi 启动引导文件有微软签名所以能正常引导启动。问题出在启动之后的内核模块加载环节。1.2 内核模块与签名验证的关系Ubuntu 内核在安全启动模式下会开启 lockdown 机制其中对内核模块的强制要求是只允许加载有有效签名的模块。如果你试图加载一个没有签名或签名无效的 .ko 文件内核会直接拒绝并且会在 dmesg 中留下类似这样的记录Lockdown: insmod of unsigned module is disabled这就是问题所在。NVIDIA 的闭源驱动通过 DKMS 编译后会生成 nvidia.ko、nvidia_modeset.ko、nvidia_uvm.ko、nvidia_drm.ko 等内核模块文件但这些模块并没有被任何受系统信任的证书签名。相比之下Windows 下的显卡驱动需要微软签名或厂商 EV 签名所以能顺利加载而 Linux 内核走的是一套独立的信任体系NVIDIA 没有把驱动提交到 Linux 内核签名数据库所以安全启动开启时会撞墙。1.3 确认当前机器状态动手之前先确认自己的机器是不是真的处于安全启动状态以及当前内核有没有启用强制签名验证。以下命令可以帮你判断# 查看安全启动状态 mokutil --sb-state # 查看内核 lockdown 模式 cat /sys/kernel/security/lockdown # 查看内核是否启用模块签名强制验证 cat /proc/sys/kernel/module/sig_enforce如果 mokutil 输出 SecureBoot enabled且 sig_enforce 为 1说明你确实处于强制验证模式。此时即使你用 root 权限强行 modprobe nvidia内核也会甩你一句 “Operation not permitted” 或者干脆什么都不干。顺带说一句传统 BIOS 引导模式下没有 Secure Boot 这层校验所以很多教程写的安装方法在 UEFI 安全启动下不适用。这也是为什么网上大量 Ubuntu 装 NVIDIA 驱动的帖子成功但你在同样机器上照做却失败的根本原因——你们俩的固件启动模式根本不是一回事。2. 动手前必须确认的三件事BIOS、驱动版本、残留驱动在开始安装之前有三件事我建议你先确认一遍避免做完一半发现装错方向又得从头来。2.1 别急着装驱动先看主板和安全启动开关首先确认你的系统是 UEFI 模式还是传统 BIOS 模式。最简单的判断方式ls /sys/firmware/efi如果这个目录存在说明你是 UEFI 模式启动如果不存在说明是传统 BIOS 模式那 Secure Boot 根本不参与后面的签名步骤你也不需要。另外建议你进 BIOS 看一眼 Secure Boot 的实际状态。有些主板的 Secure Boot 选项有好几档比如 Disabled / Standard / Custom。如果你选择了 Custom 且自己删除了默认密钥那 Ubuntu 的 shim 可能无法启动系统可能直接就进不去了。所以如果你的机器本来就正常启动 UbuntuSecure Boot 保持默认的 Standard 模式就行不需要改动。还有一个经常被忽略的选项是 CSMCompatibility Support Module也就是传统 BIOS 兼容层。部分主板开启 CSM 后会禁用 Secure Boot 或者把启动模式切回传统 BIOS。如果你打算用 UEFI 安全启动CSM 必须关掉。2.2 选对驱动版本NVIDIA 驱动版本的选择在安全启动模式下特别重要因为每更新一次内核或驱动版本你生成的模块签名就对应特定版本号。选错版本或装了一个和内核不兼容的版本后续签名、加载都会出问题。我一般建议先用 ubuntu-drivers 工具查看当前系统推荐的驱动版本sudo ubuntu-drivers devices输出会列出你的 GPU 型号和推荐的驱动包类似driver : nvidia-driver-550 - third-party - recommended driver : nvidia-driver-545 - third-party - non-recommended直接装 recommended 版本的驱动是最稳妥的做法。如果你有 CUDA 开发需求再去查 NVIDIA CUDA 工具包对驱动版本的最低要求手动指定安装版本但注意一定要和内核版本匹配。2.3 清理旧驱动和禁用 nouveauNVIDIA 驱动的老对手是开源的 nouveau 驱动模块。这个驱动默认随 Ubuntu 内核一起加载会占用显卡设备导致 NVIDIA 闭源驱动无法正确绑定显卡。安装闭源驱动之前必须先把 nouveau 拉黑禁用。检查当前是否加载了 nouveaulsmod | grep nouveau如果有输出需要在 /etc/modprobe.d/blacklist-nvidia-nouveau.conf 里写入禁用规则blacklist nouveau options nouveau modeset0然后更新 initramfssudo update-initramfs -u如果你是从旧驱动升级上来的最好把之前的 NVIDIA 相关包彻底清干净sudo apt purge *nvidia* sudo apt autoremove这个清理步骤非常重要。我在实际安装中遇到过至少两次因为旧驱动残留导致新驱动签名后仍无法加载的情况。最典型的现象是 nvidia-smi 能识别到驱动版本但 dmesg 里报版本冲突或者多个 nvidia.ko 文件同时存在导致 modprobe 加载了错误的那一个。清理完之后重启一次确保 nouveau 不再自动加载再进行下一步。3. 安装路径详解apt 与 runfile 两条主流方案NVIDIA 驱动在 Ubuntu 下的安装方式基本分为两条路用 apt 安装软件源里的驱动包或者从 NVIDIA 官网下载 runfile 手动安装。这两条路在安全启动模式下的处理方式完全不同你需要根据实际情况选择。3.1 为什么我建议先考虑 aptapt 安装方式是 Ubuntu 官方维护的集成方案驱动以 deb 包形式安装并且会通过 DKMS 与当前内核版本绑定。它的最大优势是当内核更新时DKMS 会自动重新编译 NVIDIA 模块而且 Ubuntu 的驱动包在 Secure Boot 开启时能触发一个“自动签名 MOK 注册”的流程这比手动签名省心很多。具体操作如下sudo apt update sudo apt install nvidia-driver-550装完后先不要重启。对于 Secure Boot 开启的系统Ubuntu 在安装 NVIDIA 驱动时通常会弹出一个界面让你设置一个 MOK 密码这个密码是用来在重启时登记驱动签名证书的。如果你在安装过程中没看到这个界面或者不小心跳过了可以手动导入sudo mokutil --import /var/lib/shim-signed/mok/MOK.der运行时它会让设置一个密码这个密码不是登录密码而是重启后进入 MOK 管理界面时要输入的确认密码。设置完之后重启系统会自动进入蓝色背景的 MOK Manager 界面选择 Enroll MOK → Continue → Yes输入刚才设置的密码确认后系统继续启动。有一点需要留意apt 安装方式的安全性高度依赖 Ubuntu 软件源里的驱动版本。如果你需要非常新的驱动比如新显卡刚发布官方加了新架构支持软件源里的版本可能不够新这时候就需要考虑 runfile。3.2 runfile 方式的适用场景与操作要点runfile 方式适合要安装特定版本 NVIDIA 驱动的场景比如 CUDA 版本对驱动有硬性要求或者你想装 550 系列里某个具体的小版本。缺点是它不经过 DKMS 自动管理每次内核升级后模块都要手动重新编译、重新签名。基本流程是先下载对应版本的 .run 文件wget https://cn.download.nvidia.com/XFree86/Linux-x86_64/550.xx/NVIDIA-Linux-x86_64-550.xx.run chmod x NVIDIA-Linux-x86_64-550.xx.run然后切到文本模式避免图形界面占用显卡sudo telinit 3在文本模式下执行安装sudo sh NVIDIA-Linux-x86_64-550.xx.run --no-opengl-files加上--no-opengl-files是为了避免替换系统的 OpenGL 库导致桌面环境出问题。如果是笔记本双显卡Intel 核显 NVIDIA 独显这个参数几乎是必须的。安装完成后 reboot会因为模块没有签名而加载失败。所以 runfile 方式下第 4 章的 MOK 和签名流程是绕不开的。4. MOK 密钥落地的完整链路安全启动的钥匙怎么配MOK 是 Machine Owner Key 的缩写翻译过来就是“机器所有者密钥”。它允许你向 UEFI 固件的信任库中添加自签名的证书。简单来说你自己生成一对公私钥把公钥装进主板的信任列表再给内核模块签上自己的私钥。这样内核在安全启动模式下看到这个模块的签名就能通过信任校验。4.1 生成自定义密钥对首先安装 mokutil 和 openssl然后创建一个目录存放密钥sudo apt install mokutil openssl mkdir -p ~/mok cd ~/mok openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes -days 36500 -subj /CNMy Custom MOK/这里生成的是 RSA 2048 位密钥DER 格式的证书文件是给 UEFI 固件用的。-nodes表示私钥不加密方便后面签名操作时不交互输密码。密钥有效期我设了 36500 天也就是约 100 年省得几年后签名过期又要重新搞。生成之后把公钥导入 MOK 管理:sudo mokutil --import ~/mok/MOK.der此时会提示输入两次确认密码。这个密码只是本次 MOK 导入过程中使用的一次性密码重启进入 MOK 管理界面后就要输入它来完成登记之后就不需要了。注意别用太简单的密码万一输错还要重新生成密钥。4.2 对内核模块签名先生成密钥还不够要真正给驱动模块签名。NVIDIA 驱动安装完成后各个 .ko 文件通常位于/lib/modules/$(uname -r)/updates/dkms/目录。使用内核自带签名工具 sign-file 对模块签名sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 ~/mok/MOK.priv ~/mok/MOK.der $(modinfo -n nvidia)如果你用 DKMS 方式安装驱动模块路径可以通过modinfo -n nvidia获取。需要签名的模块不止 nvidia 一个通常还要给以下模块一起签for module in nvidia nvidia_modeset nvidia_uvm nvidia_drm nvidia_peermem; do sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 ~/mok/MOK.priv ~/mok/MOK.der $(modinfo -n $module) done如果modinfo -n nvidia输出为空说明模块还没被安装或不在当前内核路径下。先确认模块文件位置find /lib/modules/$(uname -r) -name nvidia*.ko*找到后手动指定路径签名。签名完成后可以用modinfo查看模块是否带有签名信息modinfo /lib/modules/$(uname -r)/updates/dkms/nvidia.ko | grep sig4.3 重启注册 MOK签名完成后重启进入 MOK 管理界面sudo reboot在主板厂商 logo 出现后会自动进入蓝色背景的 MOK Manager 界面这是个模拟 BIOS 界面的程序需要用键盘上下键操作。选择Enroll MOKContinueYes输入你在第 4.1 节设置的确认密码Reboot重启完成后检查公钥是否已进入系统信任列表mokutil --test-key ~/mok/MOK.der输出包含MOK is enabled就说明登记成功。如果输出的是MOK is not enrolled说明 MOK 登记流程没走完重新执行 mokutil --import 再重启一次。4.4 验证签名是否生效MOK 登记完成后重新加载 NVIDIA 模块进行验证sudo modprobe nvidia sudo nvidia-smi如果 nvidia-smi 正常输出 GPU 列表和驱动版本信息说明驱动加载成功。再用 dmesg 确认一下没有问题dmesg | grep -i nvidia | head -20这里需要特别提醒新装一次 NVIDIA 驱动或升级驱动版本之后模块文件会重新编译生成之前签过的名会失效必须重新签名再重启。我第一次就是漏了这一步装好驱动后 nvidia-smi 提示 couldnt communicate with the NVIDIA driver折腾了好久才发现是签名过期了。4.5 把自动签名挂进内核更新流程每次更新内核或驱动后手动签名太容易漏。为了省事我自己写了一个自动化脚本放在/etc/kernel/postinst.d/目录下这样每次内核更新后会自动对新编译的 NVIDIA 模块重新签名。#!/bin/bash set -e MOK_PRIV/home/youruser/mok/MOK.priv MOK_DER/home/youruser/mok/MOK.der KERNEL_VER$1 for module in nvidia nvidia_modeset nvidia_uvm nvidia_drm nvidia_peermem; do MODULE_PATH/lib/modules/$KERNEL_VER/updates/dkms/$module.ko if [ -f $MODULE_PATH ]; then /usr/src/linux-headers-$KERNEL_VER/scripts/sign-file sha256 $MOK_PRIV $MOK_DER $MODULE_PATH fi done把脚本保存为/etc/kernel/postinst.d/zz-sign-nvidia并加上执行权限sudo chmod x /etc/kernel/postinst.d/zz-sign-nvidia这样每次内核更新DKMS 重编完驱动后就会自动签名不用再手动进目录签一遍。这个脚本我已经用了快一年再也没有因为内核升级导致 NVIDIA 驱动突然加载失败的情况。5. 常见翻车场景与排查黑屏、加载失败、升级后失效安全启动模式下安装 NVIDIA 驱动最容易踩的坑就是加载失败后的各种诡异表现。这里把我在多台机器上遇到的问题整理成一张对照表你可以直接按症状找答案。5.1 常见错误现象与对应解决思路症状直奔原因解决方向nvidia-smi 报 couldnt communicate with the NVIDIA driver模块未加载或签名失效modprobe nvidia 看报错重签 MOK 并重启黑屏或登录循环nouveau 未禁用或 DRM 冲突确认 blacklist 生效内核参数加 nomodesetdmesg 报 Lockdown: insmod of unsigned module模块没有有效签名用第 4 章流程签名并导入 MOK内核升级后驱动没了新内核下 DKMS 重编但未签名安装自动签名脚本手动重签一次开机进 MOK 管理界面后没有 Enroll 选项之前导入过其他 MOK 或界面版本不同用 mokutil --import 重新导入后再重启5.2 黑屏问题的处置思路黑屏分两种一种是在 GRUB 引导阶段就黑屏一种是进入桌面后黑屏。前者通常是内核参数少了 nomodeset后者则可能是 nouveau 没有彻底禁用。如果你是装了驱动后重启黑屏可以在 GRUB 菜单按e编辑启动项在linux那一行的quiet splash后面加上nomodeset按F10启动。能进入系统后就确认 nouveau 禁用和 NVIDIA 模块签名状态。如果你已经彻底黑屏进不去图形界面可以开机时在 GRUB 菜单选择 Advanced options → 恢复模式 Recovery Mode或者直接切到 ttyCtrlAltF3然后用命令行排查。这点非常实用也是我在多次黑屏后摸索出来的最短路径。5.3 升级后失效的根本原因很多人的驱动在刚装好时是正常的但某天执行了sudo apt upgrade之后重启驱动就不工作了。本质原因在于内核更新后DKMS 会针对新内核重新编译 NVIDIA 模块但新编译出来的模块是没有签名的。即使你的 MOK 公钥还在信任列表里模块本身没有签名内核照样拒绝加载。所以只要看到linux-image-*-generic被升级就要意识到 NVIDIA 驱动需要重新签名。手动执行一次第 4.2 节的签名命令或者依靠我在 4.5 节写的自动脚本基本能解决这个问题。还有一个容易忽略的点驱动版本和内核版本的兼容性。某些新内核配合旧版 NVIDIA 驱动会触发编译错误DKMS 直接构建失败。这时候查看 DKMS 状态dkms status如果显示nvidia/xxx: failed建议先把驱动升级到支持当前内核的版本再重新编译。5.4 双显卡笔记本的额外注意双显卡笔记本Intel/AMD 核显 NVIDIA 独显在安全启动模式下会更复杂。装驱动时务必用--no-opengl-files参数避免 OpenGL 库冲突。装完后通过prime-select切换显卡模式sudo prime-select query # 查看当前模式 sudo prime-select nvidia # 切换到独显模式有些笔记本在独显模式下会强制开启 NVIDIA DRM modeset如果 dmesg 里出现nvidia-drm: couldnt schedule hw modeset之类的错误可能是内核参数里nvidia-drm.modeset1和桌面环境冲突尝试改为nvidia-drm.modeset0。另外安全启动开启时部分笔记本 BIOS 会把 PCIe Resizable BAR 选项锁死这可能导致显存访问异常或者性能偏低。这种属于主板固件层面的限制不是驱动问题不建议盲目改内核参数去规避。6. 别急着关 Secure Boot安全启动下也有省心方案很多人劝你直接把 Secure Boot 关掉一了百了这种方式在独立显卡台式机上确实可行但如果你在意系统安全或者有 BitLocker 之类的加密需求关掉 Secure Boot 会带来连锁问题。实际上保留 Secure Boot 并完成 NVIDIA 驱动签名整个流程只需要多做三个动作生成密钥、签模块、导入 MOK 并重启。熟练之后整个过程不超过十五分钟。还有一件事值得单独提一下如果你用的是 Ubuntu 24.04 及更新版本NVIDIA 驱动已经可以通过 apt 安装并在 Secure Boot 模式下自动处理 MOK 流程体验比 22.04 好很多。但底层原理没有变化依然是“模块签名 MOK 登记”这套逻辑所以理解本文的流程不管系统版本怎么变你都能应对。我建议每个人都把第 4.5 节的自动签名脚本配置好并在手边留一份生成密钥的命令备份。这样即使某天重装系统或者把硬盘换到另一台安全启动的机器上也能快速恢复工作环境。最后再补充一个我个人的小习惯密钥文件 MOK.priv 和 MOK.der 我会额外备份到一个 U 盘里不放在 /home 下。因为 /home 分区如果损坏或者你不小心格盘了私钥丢了就意味着之前注册的 MOK 信任失效所有已经签好名的模块全部不能加载还得重新生成密钥并再次导入 MOK。备份一份在当前机器之外能帮你省去很多重装系统之后重新配签名的麻烦。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进