已签名内核模块

出自 ArchWiki

签名 内核模块 提供了一种内核验证模块完整性的机制。

概述

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.pemcerts/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 存储库下载

注意: 还有这些文件的 aur 版本可用 (arch-sign-modulesAUR)。构建和安装后,可以在 /usr/src/certs-local 中找到它们

请记住确保脚本是可执行的

辅助脚本

本条目或章节的准确性存在争议。

理由: arch-sign-modulesAUR 软件包来自 Arch-SKM 存储库的一个分支,该存储库在前面的章节中使用。尚不清楚原始存储库有什么问题,也不清楚为什么更改导致了分支而不是修复/改进原始存储库。(在已签名内核模块的讨论页中讨论)

arch-sign-modulesAUR 构建

& 其他 AUR 内核。

abk 辅助脚本将构建完全签名的自定义内核的手动步骤减少到 3 个命令,以更新 / 构建 & 安装 内核

abk -u kernel-name
abk -b kernel-name
abk -i kernel-name

具有已签名内核模块支持,适用于