Linux内核模块签名验证是如何实现的?

来源:TypeScript教程作者:小黄人头衔:程序员
导读:本期聚焦于小黄人创作的《Linux内核模块签名验证是如何实现的?》,敬请观看详情。Linux内核在加载.ko模块时不会无条件信任文件内容。自3.7版本引入模块签名机制后,内核可以在模块插入地址空间前完成非对称加密校验。模块签名验证的核心链路是,构建阶段用私钥对模块文件哈希做PKCS#7签名,签名数据作为ELF附加段写入模块末尾;内核编译时把对应公钥或证书嵌入信任密钥环。运行时load_module会调用module_sig_check解析模块尾部,使用内嵌公钥验签,并再次比对模块哈希。若签名无效、证书不受信任或模块被篡改,内核会拒绝加载并记录日志。该机制常与Secure Boot和内核锁定联动,用于阻止未授权代码进入内核态,也是服务器和桌面安全基线的重要组成。

Linux内核为了支持设备驱动和文件系统扩展,允许在运行期间加载内核模块。这个机制虽然灵活,但也打开了注入恶意代码的通道。攻击者一旦拿到root权限,就可以通过加载一个构造好的.ko文件隐藏进程、替换系统调用或植入后门。内核模块签名验证就是针对这一风险设计的防线,它让内核在模块代码进入内核地址空间之前,先确认模块来源可信且内容没有被篡改。

Linux内核模块签名验证是如何实现的?

一、从ELF文件到签名验证的完整链路

内核模块本质上是ELF格式的可重定位目标文件。启用模块签名后,编译系统不会直接使用普通的strip流程把文件末尾清理干净,而是在模块文件尾部附加一个签名段。这个段不是ELF标准中的加载段,而是内核约定的一段数据,包含PKCS#7格式的数字签名以及签名者信息。模块加载器在拷贝模块内容之前会检查这段数据,因此签名不会影响模块原有的代码节、数据节和重定位信息。

签名验证的核心不是简单比对哈希值,而是走完整的非对称加密流程。构建阶段使用私钥对模块文件的摘要做签名,公钥或X.509证书则被编译进内核。内核加载模块时,module_sig_check函数会定位签名段,用内嵌的公钥验证PKCS#7签名,再重新计算模块内容的哈希并和签名中的哈希比对。只有验签通过且哈希一致,模块才会被允许继续解析和重定位。正因为公钥在编译时已经进入内核镜像,攻击者即便替换模块文件,也无法伪造出能被内核接受的签名。

可以用modinfo命令查看一个已签名模块的签名信息。签名后的模块通常会包含sig_id、signer、sig_key和sig_hashalgo等字段,这些字段直接来自签名时使用的证书和算法。

modinfo hello.ko | grep -E 'sig_id|signer|sig_key|sig_hashalgo'

输出的sig_id通常为PKCS#7,signer是证书中的CN字段。如果模块没有经过签名,这些字段不会出现。需要说明的是,仅靠modinfo看到签名字段并不能证明签名一定有效,最终结果仍以内核加载时的验签为准。

二、生成密钥并启用内核签名选项

要使用模块签名验证,首先要在内核配置中打开相关选项。CONFIG_MODULE_SIG是总开关,CONFIG_MODULE_SIG_ALL表示构建过程中自动为所有模块签名,CONFIG_MODULE_SIG_FORCE决定是否强制拒绝未签名或签名无效的模块。如果只是希望具备验签能力但允许未签名模块继续加载,可以只打开CONFIG_MODULE_SIG,不打开CONFIG_MODULE_SIG_FORCE。生产环境中为了提高安全性,通常会把强制模式一并打开。

CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_KEY="certs/kernel_key.pem"
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_SHA256=y

签名密钥可以通过OpenSSL生成。密钥类型必须是X.509证书对应的私钥,常见做法是直接生成一个自签名证书和私钥放在同一个PEM文件中。配置项CONFIG_MODULE_SIG_KEY可以指向这个PEM文件。若把密钥放在内核源码的certs/目录下并命名为kernel_key.pem,构建系统会自动使用,不需要额外指定绝对路径。私钥的安全性非常重要,特别是启用了强制验签的内核,拿到私钥就相当于拿到了向该内核注入任意模块的能力。

# x509.genkey 内容示例
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts

[ req_distinguished_name ]
O = ExampleOrg
CN = ExampleOrg kernel signing key
emailAddress = admin@ipipp.com

[ myexts ]
basicConstraints = critical,CA:FALSE
keyUsage = digitalSignature
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid

上面的x509.genkey文件定义了一个不具CA属性的数字签名证书模板。生成命令中-days 36500表示证书有效期约为100年,避免短期内证书过期导致已签名模块无法加载。生成后可以得到同时包含私钥和证书的kernel_key.pem。

openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
    -config x509.genkey -outform PEM -out kernel_key.pem \
    -keyout kernel_key.pem

openssl x509 -in kernel_key.pem -outform DER -out kernel_key.x509

接下来重新编译内核并安装模块。如果开启了CONFIG_MODULE_SIG_ALL,make modules_install阶段会自动调用scripts/sign-file为每个模块签名。构建完成后可以用modinfo批量检查。CONFIG_MODULE_SIG_KEY指定的私钥不会被安装到目标系统的模块目录中,构建完成后应妥善保存或从构建目录中移除。

三、手动签名、内核验证与强制模式

对于第三方模块或单独编译的模块,构建系统不会自动处理签名,这时需要手动调用内核源码中的sign-file脚本。第一个参数是哈希算法,通常与内核配置的CONFIG_MODULE_SIG_SHA256保持一致;第二个参数是私钥PEM文件;第三个参数是DER格式的证书文件;最后是待签名的.ko文件。

cd /usr/src/linux
scripts/sign-file sha256 \
    certs/kernel_key.pem \
    certs/kernel_key.x509 \
    /lib/modules/$(uname -r)/extra/hello.ko

手动签名后,可以看到模块文件变大,尾部出现签名数据。使用readelf -S查看节区时会发现一个名为.module_sig的节。不同工具链对尾部附加数据的处理方式不同,因此不建议在签名后继续执行strip操作。若必须strip,应使用内核构建流程自带的方式,而不是任意使用strip --strip-debug等命令,否则可能破坏签名导致验签失败。

模块加载时,内核会检查CONFIG_MODULE_SIG_FORCE下的策略。未签名模块、签名哈希不匹配的模块、证书不受信任的模块都会被拒绝。dmesg中常见日志包括PKCS#7 signature not signed with a trusted key和Required key not available。如果内核编译时没有把正确公钥编译进去,即使模块本身签名有效,也会因为证书不在信任链中而加载失败。此时需要重新编译内核,把对应证书或PEM文件作为CONFIG_MODULE_SIG_KEY的输入。

dmesg | grep -Ei 'module|signature|key'

还有一种情况是开启了Secure Boot的机器。UEFI Secure Boot会限制内核和模块的启动链,如果启用内核锁定,模块签名策略会被进一步收紧。此时不仅要通过内核内置公钥验签,还要符合UEFI平台对签名的要求。很多发行版通过shim和MOK机制管理这类密钥,用户自编译内核后如果使用第三方模块,通常需要把证书导入MOK列表,否则模块会被内核锁定策略拦截。

四、生产环境实践与常见误区

模块签名验证不是银弹。它主要防止攻击者在获得root权限后加载任意内核模块,但不能阻止root用户修改内核内存、挂载调试接口或利用已有漏洞。启用强制签名后,系统可用性也会受到影响,例如自行编译的驱动、厂商闭源驱动以及DKMS生成的模块都需要使用同一套签名证书。如果私钥丢失,原先签名的模块仍然可以加载,但后续更新模块需要重新生成密钥并重编译内核,因此证书和私钥管理是运维中必须提前规划的事项。

另一个常见误区是认为只要开启CONFIG_MODULE_SIG就能拦截所有未签名模块。实际上该选项只提供验签能力,是否强制由CONFIG_MODULE_SIG_FORCE和内核启动参数module.sig_enforce共同决定。如果内核配置没有把强制模式编译进去,攻击者仍可能加载未签名模块。使用时可以通过/proc/sys/kernel/modules_disabled进一步限制模块加载,但要注意启用后无法撤销,只能重启。

在企业环境中,模块签名策略通常和完整性度量、安全启动、审计日志一起组成内核安全基线。对于需要发布内核模块的团队,建议使用独立的签名证书,不要与代码签名证书混用;私钥不要进入版本控制,构建机与签名机分离;定期检查已加载模块的签名信息,并监控dmesg中的验签失败日志。这些措施配合强制签名和内核对模块参数的严格限制,能显著提高攻击者植入内核级恶意代码的成本。

内核模块签名验证属于防御纵深中的一环,它解决的是模块来源和完整性问题。理解其证书链、签名段结构和强制策略,不仅有助于在自编译内核时避免模块加载失败,也能在安全事件排查中快速定位是否为签名不匹配或证书不受信任导致的问题。对于运维和内核开发者来说,掌握这套机制的实际操作远比记住配置项名称更加重要。

内核模块签名模块签名验证Linux内核安全修改时间:2026-10-02 06:58:22

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1002/64554.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。