1.9. 为安全引导签名内核模块
Red Hat Enterprise Linux 7 包括对 UEFI 安全引导功能的支持,这意味着可以在启用了 UEFI 安全引导的系统上安装并运行 Red Hat Enterprise Linux 7。请注意,Red Hat Enterprise Linux 7 不需要在 UEFI 系统中使用安全引导机制。
如果启用了安全引导机制,则必须使用私钥签名并使用对应的公钥验证 UEFI 操作系统引导装载程序、Red Hat Enterprise Linux 内核和所有内核模块。如果未签名和验证,则不允许系统完成引导过程。
Red Hat Enterprise Linux 7 发行版本包括:
- 签名的引导装载程序
- 签名的内核
- 签名的内核模块
此外,签名的第一阶段引导装载程序和签名的内核包括嵌入式红帽公钥。这些签名的可执行二进制文件和嵌入式密钥可让 Red Hat Enterprise Linux 7 在支持 UEFI 安全引导引导的系统中使用 Microsoft UEFI 安全引导认证机构密钥安装、引导和运行这些密钥。
并非所有基于 UEFI 的系统都包括对安全引导的支持。
以下小节中提供的信息描述了在启用了安全引导机制的基于 UEFI 的构建系统上用于 Red Hat Enterprise Linux 7 的自签名专用内核模块的步骤。这些部分还提供了将您的公钥导入到要部署内核模块的目标系统中的可用选项概述。
要签名并载入内核模块,您需要:
1.9.1. 先决条件
要能够为外部构建的内核模块签名,请在构建系统上安装下表中列出的实用程序。
工具 | 由软件包提供 | 用于 | 目的 |
---|---|---|---|
|
| 构建系统 | 生成公共和专用 X.509 密钥对 |
|
| 构建系统 | 用于为内核模块签名的 Perl 脚本 |
|
| 构建系统 | 用于运行签名脚本的 Perl 解释器 |
|
| 目标系统 | 用于手动注册公钥的可选工具 |
|
| 目标系统 | 用于在系统密钥环中显示公钥的可选工具 |
构建系统(构建和签署内核模块)不需要启用 UEFI 安全引导,甚至不需要是基于 UEFI 的系统。
1.9.2. 内核模块验证
在 Red Hat Enterprise Linux 7 中,加载内核模块时,将使用内核系统密钥环中的公共 X.509 密钥来检查模块的签名,不包括内核的系统黑名单密钥环中的密钥。以下小节概述了密钥/密钥环的来源,如系统中不同源的载入密钥示例。此外,用户可以查看验证内核模块所需的一切。
1.9.2.1. 用于验证内核模块的公钥源
在启动过程中,内核从一组持久密钥存储中将 X.509 密钥加载到系统密钥环或系统黑名单密钥环中,如下表中所示。
X.509 密钥源 | 用户添加密钥的功能 | UEFI 安全引导状态 | 引导过程中载入的密钥 |
---|---|---|---|
嵌入于内核中 | 否 | - |
|
UEFI 安全引导 "db" | 有限 | 未启用 | 否 |
Enabled |
| ||
UEFI 安全引导 "dbx" | 有限 | 未启用 | 否 |
Enabled |
| ||
嵌入 in | 否 | 未启用 | 否 |
Enabled |
| ||
Machine Owner Key(MOK)列表 | 是 | 未启用 | 否 |
Enabled |
|
如果没有启用 UEFI 安全引导,或者没有启用 UEFI 安全引导,则只有嵌入内核中的密钥才会加载到系统密钥环中。在这种情况下,您无法在不重新构建内核的情况下添加这组密钥。
系统黑名单密钥环是一个已撤销的 X.509 密钥列表。如果您的模块由黑名单中的密钥签名,那么即使您的公钥位于系统密钥环中,它也会失败身份验证。
您可以使用 keyctl
实用程序显示系统密钥环上的密钥信息。以下是未启用 UEFI 安全引导的 Red Hat Enterprise Linux 7 系统的一个简化示例输出。
# keyctl list %:.system_keyring 3 keys in keyring: ...asymmetric: Red Hat Enterprise Linux Driver Update Program (key 3): bf57f3e87... ...asymmetric: Red Hat Enterprise Linux kernel signing key: 4249689eefc77e95880b... ...asymmetric: Red Hat Enterprise Linux kpatch signing key: 4d38fd864ebe18c5f0b7...
以下是启用了 UEFI 安全引导的 Red Hat Enterprise Linux 7 系统的一个简化示例输出。
# keyctl list %:.system_keyring 6 keys in keyring: ...asymmetric: Red Hat Enterprise Linux Driver Update Program (key 3): bf57f3e87... ...asymmetric: Red Hat Secure Boot (CA key 1): 4016841644ce3a810408050766e8f8a29... ...asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed... ...asymmetric: Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e... ...asymmetric: Red Hat Enterprise Linux kernel signing key: 4249689eefc77e95880b... ...asymmetric: Red Hat Enterprise Linux kpatch signing key: 4d38fd864ebe18c5f0b7...
以上输出显示了从 UEFI 安全引导 "db" 密钥以及 红帽安全引导(CA 密钥 1)中添加了两个密钥
,这些密钥嵌入在 shim.efi
启动加载器中。您还可以使用 UEFI 安全引导相关源查找内核控制台信息。这包括 UEFI 安全引导 db、内嵌的 shim 和 MOK 列表。
# dmesg | grep 'EFI: Loaded cert' [5.160660] EFI: Loaded cert 'Microsoft Windows Production PCA 2011: a9290239... [5.160674] EFI: Loaded cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309b... [5.165794] EFI: Loaded cert 'Red Hat Secure Boot (CA key 1): 4016841644ce3a8...
1.9.2.2. 内核模块验证要求
这部分论述了在启用了 UEFI 安全引导功能的系统中载入内核模块必须满足的条件。
如果启用了 UEFI 安全引导,或者指定了 模块.sig_enforce
内核参数,则只能加载使用系统密钥环中密钥进行身份验证的已签名内核模块。此外,公钥不得位于系统黑名单密钥环中。
如果禁用了 UEFI 安全引导,并且没有指定 模块.sig_enforce
内核参数,您可以加载未签名的内核模块和签名的内核模块,且没有公钥。下表总结了这一点。
模块已签名 | 找到公钥,且签名有效 | UEFI 安全引导状态 | sig_enforce | 模块载入 | 内核污点 |
---|---|---|---|---|---|
未签名 | - | 未启用 | 未启用 | 成功 | 是 |
未启用 | Enabled | Fails | - | ||
Enabled | - | Fails | - | ||
已签名 | 否 | 未启用 | 未启用 | 成功 | 是 |
未启用 | Enabled | Fails | - | ||
Enabled | - | Fails | - | ||
已签名 | 是 | 未启用 | 未启用 | 成功 | 否 |
未启用 | Enabled | 成功 | 否 | ||
Enabled | - | 成功 | 否 |
1.9.3. 生成公共和专用 X.509 密钥对
您需要生成一个公共和私有 X.509 密钥对,才能成功在启用了安全引导的系统上使用内核模块。之后您将使用私钥为内核模块签名。您还必须将对应的公钥添加到用于安全引导的 Machine Owner Key(MOK)中,以验证签名的模块。具体步骤请查看 第 1.9.4.2 节 “系统管理员手动将公钥添加到 MOK 列表中”。
这个密钥对生成的一些参数最好用配置文件指定。
使用密钥对生成参数创建配置文件:
# cat << EOF > configuration_file.config [ req ] default_bits = 4096 distinguished_name = req_distinguished_name prompt = no string_mask = utf8only x509_extensions = myexts [ req_distinguished_name ] O = Organization CN = Organization signing key emailAddress = E-mail address [ myexts ] basicConstraints=critical,CA:FALSE keyUsage=digitalSignature subjectKeyIdentifier=hash authorityKeyIdentifier=keyid EOF
如以下示例所示,创建 X.509 公钥和私钥对:
# openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 \ -batch -config configuration_file.config -outform DER \ -out my_signing_key_pub.der \ -keyout my_signing_key.priv
公钥将写入
my_signing_key_pub.der
文件,私钥将写入my_signing_key.priv
文件中。在您要验证并载入内核模块的所有系统中注册您的公钥。
详情请查看 第 1.9.4 节 “在目标系统中注册公钥”。
应用强大的安全措施和访问策略来保护您的私钥内容。对于一个恶意的用户,可以使用这个密钥破坏所有由对应公钥验证的系统。
1.9.4. 在目标系统中注册公钥
当 Red Hat Enterprise Linux 7 在启用了安全引导机制的基于 UEFI 的系统中引导时,内核将加载到系统密钥环中安全引导 db 密钥数据库中但不在已撤销密钥的 dbx 数据库中的所有公钥。以下小节描述了在目标系统上导入公钥的不同方式,以便系统密钥环可以使用公钥来验证内核模块。
1.9.4.1. 工厂固件映像(包括公钥)
为了便于对系统中的内核模块进行身份验证,请您的系统供应商将公钥合并到其工厂固件镜像中的 UEFI 安全引导密钥数据库中。
1.9.4.2. 系统管理员手动将公钥添加到 MOK 列表中
Machine Owner Key(MOK)功能可以用来扩展 UEFI 安全引导密钥数据库。当 Red Hat Enterprise Linux 7 在启用了安全引导机制的启用了 UEFI 的系统中引导时,MOK 列表中的密钥除密钥数据库中的密钥外也会添加到系统密钥环中。和安全引导数据库密钥相似,MOK 列表密钥会被安全地永久存储。但它们是两个独立的工具。MOK 工具由 shim.efi
、MokManager.efi
、grubx64.efi
和红帽企业 Linux 7 mokutil
实用程序支持。
注册 MOK 密钥需要用户在每个目标系统中在 UEFI 系统控制台上手动互动。MOK 工具为测试新生成的密钥对以及与其签注的内核模块提供了方便的方法。
将您的公钥添加到 MOK 列表中:
请在 MOK 列表中添加您的公钥:
# mokutil --import my_signing_key_pub.der
会要求您输入并确认此 MOK 注册请求的密码。
重启机器。
待处理的 MOK 密钥注册请求将由
shim.efi 通知
,它将启动MokManager.efi
,以便您从 UEFI 控制台完成注册。输入您之前与此请求关联的密码并确认注册。
您的公钥已添加到 MOK 列表中,这是永久的。
密钥位于 MOK 列表中后,它会在启用 UEFI 安全引导时自动传播到此列表中的系统密钥环,并在随后引导时自动传播到系统密钥环。
1.9.5. 使用私钥签名内核模块
假设您的内核模块已就绪:
使用 Perl 脚本使用您的私钥为内核模块签名:
# perl /usr/src/kernels/$(uname -r)/scripts/sign-file \ sha256 \ my_signing_key.priv\ my_signing_key_pub.der\ my_module.ko
注意Perl 脚本要求您同时提供包含私钥和公钥的文件,以及您要签名的内核模块文件。
您的内核模块采用 ELF 镜像格式,Perl 脚本计算,并将签名直接附加到内核模块文件中的 ELF 镜像中。
modinfo
实用程序可用于显示有关内核模块签名的信息(如果存在)。有关使用modinfo
的详情请参考 第 1.4 节 “显示模块信息”。附加的签名不包含在 ELF 镜像部分,不是 ELF 镜像的一个正式部分。因此,
readelf
等实用程序将无法在内核模块中显示签名。您的内核模块现在可以被加载。请注意,您签名的内核模块也可以在禁用 UEFI 安全引导的系统或非 UEFI 系统中加载。这意味着您不需要同时提供内核模块的签名和未签名版本。
1.9.6. 载入签名的内核模块
旦您的公钥注册并处于系统密钥环中,请使用 mokutil
将您的公钥添加到 MOK 列表中。然后,使用 modprobe
命令手动加载内核模块。
(可选)在注册公钥前验证您的内核模块是否不会被加载。
有关如何列出当前载入的内核模块的详情请参考 第 1.3 节 “列出当前加载的模块”。
在当前引导中验证哪些密钥被添加到系统密钥环中:
# keyctl list %:.system_keyring
由于您的公钥尚未注册,因此不应显示在 命令的输出中。
申请注册您的公钥:
# mokutil --import my_signing_key_pub.der
重新引导,并在 UEFI 控制台中完成注册:
# reboot
再次验证系统密钥环中的密钥:
# keyctl list %:.system_keyring
将模块复制到您想要的内核的
/extra/
目录中:# cp my_module.ko /lib/modules/$(uname -r)/extra/
更新模块依赖项列表:
# depmod -a
载入内核模块并确认它已被成功载入:
# modprobe -v my_module # lsmod | grep my_module
另外,要在引导时载入模块,将其添加到
/etc/modules-loaded.d/my_module.conf
文件中:# echo "my_module" > /etc/modules-load.d/my_module.conf