已签名内核模块
概述
Linux 内核区分并隔离了模块的验证,使其与加载模块前要求或强制验证模块相分离。内核模块分为 2 类:
- 标准树内模块,它们随内核源代码一起提供。它们在正常的内核构建过程中编译。
- 树外模块,它们不是内核源代码发布的一部分。它们在内核树外部构建,需要为每个要构建的内核提供内核头文件包。它们可以为特定内核手动构建和打包,或者可以使用 DKMS 在需要时构建。
在标准内核编译期间,内核构建工具会创建一对私钥/公钥,并对每个树内模块进行签名(使用私钥)。公钥保存在内核本身中。当随后加载模块时,然后可以使用公钥来验证模块是否未更改。
可以启用内核以始终验证模块,并将任何失败报告给标准日志。是否允许加载和使用无法验证的模块的选择,可以编译到内核中,也可以使用下面解释的内核参数在运行时打开。
需要完成内容的概要
起点是基于 内核/Arch 构建系统 中概述的自定义内核包。我们将修改构建以对标准树内内核模块进行签名,并为签名和验证树外模块提供先决条件。
目标是拥有
- 在标准内核构建过程中对树内模块进行签名。标准内核构建在每次构建时都会创建一个新的公钥/私钥对。
- 树外模块已签名,并且关联的公钥已编译到内核中。我们将在每次构建时创建一个单独的公钥/私钥对。
每个内核构建都需要知道用于签署树外模块的密钥对。内核配置参数现在用于使内核知道其他签名密钥:CONFIG_SYSTEM_TRUSTED_KEYS="/path/to/oot-signing_keys.pem"
。
密钥和签名工具将存储在当前模块构建目录中。无需执行任何操作来清理它,因为删除由标准模块清理处理。私钥和公钥都安装在 /usr/lib/modules/kernel_version-build/certs-local
中。
内核配置
CONFIG_SYSTEM_TRUSTED_KEYS
将使用下面提供的脚本 genkeys.py
自动更新。此外,应通过手动编辑 .config
文件,或通过 Linux src
目录中的 make menuconfig
设置以下配置选项,然后将更新后的 .config
文件复制回构建文件 config
。 最好使用椭圆曲线类型密钥和 zstd 压缩。
CONFIG_MODULE_SIG=y Enable Loadable module suppot ---> Module Signature Verification - activate CONFIG_MODULE_SIG_FORCE=n Require modules to be validly signed -> leave off (for now) This allows the decision to enforce verified modules only as boot command line. If you are comfortable all is working then by all means change this to 'y' Command line version of this is : module.sig_enforce=1 CONFIG_MODULE_SIG_HASH=sha512 Automatically sign all modules - activate Which hash algorithm -> SHA-512 openssl 3.2+ and kernel 6.7+ bring support for SHA3-xxx (e.g. SHA3-512) CONFIG_MODULE_COMPRESS_ZSTD=y Compress modules on installation - activate Compression algorithm (ZSTD) CONFIG_MODULE_SIG_KEY_TYPE_ECDSA=y Cryptographic API ---> Certificates for Signature Checking ---> Type of module signing key to be generated -> ECDSA CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=n Enable Loadable module support ---> Allow loading of modules with missing namespace imports - set to no
内核命令行
当您确认模块已签名并且内核工作正常后,您可以启用以下内核参数,以要求内核仅允许加载经过验证的模块
module.sig_enforce=1
在强制启用已验证模块之前,请确认系统日志未显示任何模块签名失败的报告。
所需工具
内核构建包
在构建内核包的目录中
$ mkdir certs-local
此目录将提供用于创建密钥以及签名内核模块的工具。
将这些文件放入 certs-local
x509.oot.genkey
genkeys.py
install-certs.py
sign_module.py
lib/arg_parse.py
lib/refresh_needed.py
lib/class_genkeys.py
lib/get_key_hash.py
lib/signer_class.py
lib/update_config.py
lib/utils.py
文件 genkeys.py
及其配套配置文件 x509.oot.genkey
用于创建密钥对。
genkeys.py
还通过更新用于构建内核的配置文件,向内核提供密钥信息。
脚本 sign_module.py
对树外内核模块进行签名。它可以手动运行,并由 dkms/kernel-sign.sh
调用。它处理使用 xz 和 gzip 压缩的模块,并依赖 python-zstandard 来帮助处理使用 zstd 压缩的模块。
genkeys.py
将在以日期-时间命名的目录中创建密钥对
genkeys.py
将检查和更新 --config config(s) 选项给出的内核配置。它接受单个配置文件或多个文件的 shell glob。例如 --config 'conf/config.*'。所有配置都将使用相同的密钥更新。默认密钥类型为 ec(椭圆曲线),默认哈希为 sha512。这些可以使用命令行选项更改。有关更多详细信息,请参阅 genkeys.py
-h。
它还会创建一个软链接 current
到保存当前密钥对的同一目录。
install-certs.py
将从 PKGBUILD 的 package_headers() 函数调用,以安装签名密钥。下面给出了示例。
这些文件是可用的,并在下面提供了链接。
DKMS 支持
原生 DKMS 方法
DKMS 原生支持签名构建的模块,只要您的内核头文件在 scripts
目录中包含 sign-file
程序(大多数基于 archkernel 的 PKGBUILD,包括官方的)。
然后,您需要让它知道您的签名密钥和 x509 证书(在常规内核构建中,这些文件将位于 certs/signing_key.pem
和 certs/signing_key.x509
中)位于 /etc/dkms/framework.conf
中
/etc/dkms/framework.conf
# $kernel_source_dir resolves to /lib/modules/`uname -r`/build mok_signing_key=$kernel_source_dir/certs/signing_key.pem mok_certificate=$kernel_source_dir/certs/signing_key.x509
在此示例中,它将查找每个内核的 certs
目录,因此您可以将签名密钥/证书放在那里,或者如果您使用的是自定义 PKGBUILD,只需从内核安装它即可
PKGBUILD
_package-headers() { # ... # copy signing keys for dkms install -Dt "$builddir/certs" -m 400 certs/signing_key.* }
根据您的安全偏执程度,您肯定不希望分发这些密钥,但这对于 certs-local 方法也是如此。
certs-local 方法
$ mkdir certs-local/dkms
将 2 个文件添加到 dkms
目录
kernel-sign.conf
kernel-sign.sh
这些文件将安装在 /etc/dkms
中,并为 DKMS 提供使用本地密钥自动签名模块的方法。这是签名树外内核模块的推荐方法。如下所述,一旦安装了此功能,DKMS 自动签名模块所需做的就是为每个模块创建一个指向配置文件的软链接。
$ cd /etc/dkms # ln -s kernel-sign.conf module_name.conf
例如
# ln -s kernel-sign.conf vboxdrv.conf
链接创建可以轻松添加到 arch 包中,以便在需要时进一步简化。
修改 PKGBUILD
我们需要按如下方式更改内核构建
prepare()
将以下内容添加到 prepare()
函数的顶部
PKGBUILD
prepare() { msg2 "Rebuilding local signing key..." ../certs-local/genkeys.py -v --config config ... }
默认密钥重新生成刷新周期为 7 天,但这可以在命令行上更改。因此,如果您想每月创建新密钥,请添加“--refresh 30days”作为 genekeys.py 的参数。您可以使用“--refresh always”在每次构建时刷新。刷新单位可以是秒、分钟、小时、天或周。
_package-headers()
将以下内容添加到 _package-headers()
函数的底部
PKGBUILD
_package-headers() { ... # # Out-of-tree module signing # This is run in the kernel source / build directory # msg2 "Local Signing certs for out-of-tree modules..." certs_local_src="../../certs-local" certs_local_dst="${builddir}/certs-local" $certs_local_src/install-certs.py $certs_local_dst # DKMS tools dkms_src="$certs_local_src/dkms" dkms_dst="${pkgdir}/etc/dkms" mkdir -p $dkms_dst rsync -a $dkms_src/{kernel-sign.conf,kernel-sign.sh} $dkms_dst/ }
所需文件
上面引用的 5 个支持文件可从 github.com/gene-git/Arch-SKM 存储库下载
/usr/src/certs-local
中找到它们- certs-local/x509.oot.genkey
- certs-local/genkeys.py
- certs-local/install-certs.py
- certs-local/sign_module.py
- certs-local/lib/signer_class.py
- certs-local/lib/utils.py
- certs-local/dkms/kernel-sign.conf
- certs-local/dkms/kernel-sign.sh
请记住确保脚本是可执行的。
辅助脚本
arch-sign-modulesAUR 构建
& 其他 AUR 内核。
abk 辅助脚本将构建完全签名的自定义内核的手动步骤减少到 3 个命令,以更新 / 构建 & 安装 内核
abk -u kernel-name abk -b kernel-name abk -i kernel-name
具有已签名内核模块支持,适用于
- zfs-dkmsAUR nvidia-beta-dkmsAUR lkrg-dkmsAUR nvidia-dkms