安装新的nvidia驱动遇到了内核模块签名的问题
在linux系统里安装了nvidia的CUDA,然后在支持UEFI的系统里启动会报错。
检查发现,系统的新安装的驱动,即新添加的一些nvidia相关的kernel module没有被sign,在secure boot启动过程中check failed。
这里检查时主要有两个:一个是fedora文档,一个是nvidia文档
1 | # sh ./NVIDIA-Linux-x86-319.12.run -s \ |
这里有说明用nvidia的runfile加参数,runfile会自动给你签名,但是安装CUDA的时候下载的runfile报错没有这些参数
顺便一说,CUDA的安装也有两种方式:rpm和runfile
http://developer.download.nvidia.com/compute/cuda/7.5/Prod/docs/sidebar/CUDA_Quick_Start_Guide.pdf
在rpm方式下已经安装成功,因为rpm基本都是下载文件,然后安装文件,最后一步直接yum install cuda完成所有依赖的东西的安装
runfile的话则是先要进行一些配置文件的修改,有些命令会执行失败
手动siging kernel module的流程
需要的tools
- openssl openssl
- sign-file kernel-devel
- perl perl
- mokutil mokutil
- keyctl keyutils
其中,openssl、sign-file和perl都是在build system里操作。
openssl用来生成密钥对,perl命令用来执行sign-file这个脚本文件,来给kernel module进行签名
mokutil和keyctl是在Target system执行。
mokutil用来enroll the public key,就是把公钥加到MOK(Machine Owner Key)
表里。
keyctl用来显示system key ring
里的公钥
在build system里,不需要UEFI Secure Boot被enabled也不需要是一个支持UEFI的系统
kernel module authentication
fedora系统中,内核模块加载后,内核模块的签名signature会用存储在内核的system key ring里的公钥进行检查,包括检查白名单和黑名单。
在启动的时候,内核会从多处加载X.509 keys,加载到system key ring里,以便后面的对内核模块的check。
可以查看具体哪些source: Sources For System Key Rings
- Embedded in kernel
- UEFI Secure Boot “db”
- UEFI Secure Boot “dbx”
- Embedded in shim.efi boot loader
- Machine Owner Key (MOK) list
kernel本身用户无法操作,UEFI的处于机器固件层面的,不好操作,就剩下shim.efi和MOK了。
在正常的系统中,shim.efi用户无法直接改动的,但在制作虚拟机镜像的时候,可以直接修改内核的所有文件,这时就可以替换或修改这个efi文件。
而通常情况下,比如在运行中的系统中,一般会用命令去操作MOK表,把前面步骤里生成的key注入到MOK中。
- 如果uefi secure boot没有启动或系统本身就不支持uefi secure boot,那么内核只会加载keys Embedded in kernel。
- 黑名单有更高优先级,如果存在于黑名单中了,那么就算内核模块以及签名也没用
下面是几个显示system key ring中密钥列表的命令例子:
UEFI Secure Boot is not enabled
1 | ~]# keyctl list %:.system_keyring |
UEFI Secure Boot is enabled
1 | ~]# keyctl list %:.system_keyring |
可以查看kernel console messages,查看密钥是从哪里加载进来的。示例中是搜索从efi文件加载的。
1 | ~]# dmesg | grep 'EFI: Loaded cert' |
kernel module在启动过程中的规则
kernel module在启动过程中可能被检测,因为还要依托于一个参数module.sig_enforce
。
kernel module在启动过程中的整体流程
key是否强制加载,是否找到,是否验证通过,是否支持uefi secure boot,内核模块是否加载成功,内核是否被污染
Kernel Module Authentication Requirements for Loading
生成密钥
- 用openssl生成X.509密钥对
执行命令时依赖一个配置文件,要先创建一个配置文件(尖括号括起来的需要手动修改)
1 | ~]# cat << EOF > configuration_file.config |
- 执行openssl命令
1 | ~]# openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 \ |
注意保护私钥
把生成的密钥加入到target system的多种途径
因为内核从多处加载密钥,所以也有多种方式放置密钥
让服务器硬件厂商把你自己生成的密钥加入到他们自己的固件镜像的UEFI Secure Boot key database
有db和dbx两种database,内核加载是会过滤掉dbx中的失效key(revoked key)
Executable Key Enrollment Image Adding Public Key
没太理解,应该是是编辑一个已有的image手动把public key加入到MOK list
MOK是fedora里的一个功能,The MOK facility is supported by shim.efi, MokManager.efi, grubx64.efi, and the Fedora mokutil utility.
1 | ~]# mokutil --import my_signing_key_pub.der |
reboot the machine
MOK key enrollment的请求会被shim.efi注意到,然后它会调用MokManager.efi,从UEFI console完成密钥的登记。这时候你需要输入你刚才mokutil命令输入的密码
对于ironic的user image怎么办??,无法启动,是否会在image加载后,启动时完成enrollment??
Signing Kernel Module with the Private Key
- 编译出来module(或者其他方式生成的module)
1 | ~]# make -C /usr/src/kernels/$(uname -r) M=$PWD modules |
- 执行perl脚本,签名
1 | ~]# perl /usr/src/kernels/$(uname -r)/scripts/sign-file \ |
- 可以用modinfo来查看module的信息,链接
加载signed kernel module
- 首先确认系统本次启动中内核没有加载密钥,用命令来查看system keyring
1 | keyctl list %:.system_keyring |
- 像之前的步骤一样,把公钥登记到MOK中,准确点叫做请求登记
1 | ~]# mokutil --import my_signing_key_pub.der |
- Reboot, and complete the enrollment at the UEFI console.
- 重启后,再次检查keyring
1 | keyctl list %:.system_keyring |
- 现在则可以正常的加载你的kernel module了
1 | ~]# modprobe -v my_module |
有个疑问,这里内核模块没有签名,则该内核模块加载失败,
kernel modules and their utilities
一些内核相关的命令
1 | lsmod(8) — The manual page for the lsmod command. |
回到对ironic user image的签名
回到实际的场景,对添加了nvidia kernel module的image进行签名,有以下几种可操作的方法:
- 使用自签名,那么使用命令操作MOK后,系统文件里就包含密钥了,但是需要把这个密钥最终写入到uefi主板上,这一步需要在系统启动时手动操作,所以无法自动化。
- 使用已有签名,就是uefi主板的数据库里已经带了默认密钥,是在出厂时就写入的官方公钥。所以如果想想一个内核模块加载成功,就需要这个内核模块被官方的私钥签名,但是这个操作个人是无法做的,只能由fedora官方来做。