已签名的内核模块
已签名的 内核模块 提供了一种机制,使内核能够验证模块的完整性。
概述
Linux 内核区分并单独保留模块的验证与要求或强制模块在加载前进行验证。内核模块分为两类:
- 标准的*树内*模块,它们随内核源代码一起提供。它们在正常的内核构建过程中进行编译。
- *树外*模块,它们不是内核源代码分发的一部分。它们在内核树外部构建,需要为每个要构建的内核提供内核头文件包。它们可以手动为特定内核构建并打包,也可以使用 DKMS 在需要时构建。
在标准的内核编译过程中,内核构建工具会创建一个公钥/私钥对,并对所有树内模块进行签名(使用私钥)。公钥保存在内核本身中。当后续加载模块时,就可以使用公钥来验证模块是否未被更改。
内核可以被启用为始终验证模块,并将任何失败报告给标准日志。是否允许加载和使用无法验证的模块的选择可以在编译时写入内核,也可以在运行时使用 内核参数 启用,如下所述。
需要完成事项总结
起点基于自定义内核包,如 Kernel/Arch build system 中所述。我们将修改构建过程,对标准的树内内核模块进行签名,并提供签名和验证树外模块的先决条件。
目标是实现:
- 树内模块在标准内核构建过程中进行签名。标准的内核构建在每次构建时创建一个新的公钥/私钥对。
- 树外模块被签名,并且相关的公钥被编译到内核中。我们将为每次构建创建一个单独的公钥/私钥对。
每次内核构建都需要了解用于签名树外模块的密钥对。现在使用内核配置参数来使内核了解额外的签名密钥: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.genkeygenkeys.pyinstall-certs.pysign_module.pylib/arg_parse.pylib/refresh_needed.pylib/class_genkeys.pylib/get_key_hash.pylib/signer_class.pylib/update_config.pylib/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
在 dkms 目录中添加 2 个文件。
kernel-sign.confkernel-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[broken link: replaced by nvidia-open-dkms]