关注了就能看到更多这么棒的文章哦~Daroc Alden Gemini translation 原文链接:https://lwn.net/Articles/1049295/
Linux 内核运行时守护者(Linux Kernel Runtime Guard,简称 LKRG)是一个树外(out-of-tree)的可加载内核模块(loadable kernel module,LKM),它试图检测并报告违反内核内部不变量(invariants)的行为,例如由正在进行的漏洞利用或 Rootkit 引起的情况。自 2018 年首次发布以来,LKRG 一直处于实验阶段。2025 年 9 月,该项目宣布发布 1.0 版本。随着该版本带来的稳定性承诺,用户可能希望了解更多信息,以决定是否将其纳入自己的内核中。
通过多样性实现安全
LKRG 的使命在某种程度上是徒劳的——如果攻击者已经攻破了正在运行的内核,那么从理论上讲,攻击者没有理由不能识别并阻止或破坏 LKRG 内核模块。然而,在实践中,攻击者需要确实知道要这样做,并且操作得足够快,以逃离 LKRG 执行的定期扫描。因此,使用 LKRG 提高了攻击 Linux 内核的门槛。
项目文档将此称为通过多样性实现安全(security through diversity)——如果攻击者没有专门针对 LKRG 进行处理,运行它就能帮助发现那些本会被忽视的攻击。当然,使用 LKRG 也会带来一定的成本,这使得是否使用它成为一种权衡。
运行它
LKRG 已为 Arch、Gentoo、NixOS 和 Rocky Linux 提供了软件包。在其他系统上,它可以作为 DKMS 模块从源代码构建。加载后,它提供了一些 sysctl 设置,用于精确控制它应该检查的内容。LKRG 支持两类通用的检查:针对内核全局状态的检查和针对进程状态的检查。
内核拥有大量的全局状态;目前,LKRG 专门检查其中一个虽小但很重要的子集。具体来说,它会验证内核(包括已加载的模块)内的代码和只读数据在页表(page-tables)中是否具有正确的权限,代码或数据是否被修改(包括全局 SELinux 设置是否被篡改),以及一些 CPU 特定的状态寄存器。后者包括管理模式执行保护(Supervisor Mode Execution Protection,SMEP)和管理模式访问预防(Supervisor Mode Access Prevention,SMAP)设置,这些设置可以防止内核代码访问用户空间内存。SMEP 防止 CPU 在内核模式下执行用户空间代码,而 SMAP 则防止其他类型的访问。当内核刻意向用户空间内存复制数据或从中复制数据时,必须禁用这种保护,但在其余时间,它能防止内核中的漏洞利用利用存储在用户空间的数据。
SMEP 和 SMAP 设置的检查非常简单,但确保内核数据未被篡改则更为复杂。当首次加载时,LKRG 会对所有不应更改的数据进行哈希处理,并将该哈希值存储在自己的内存中。随后,可以定期对数据进行重新哈希,并与存储的哈希值进行比对。内核的某些部分无法被哈希覆盖,因为它们在正常运行期间确实会发生变化,但仍然可以针对内核的其他部分进行一致性检查。例如,LKRG 会扫描内核内存以查找已加载的模块,并将其与内核自身的已加载模块列表进行比较。如果某个模块存在但不在列表中,它可能正试图隐藏,因此很可能是恶意的。每个内核模块在首次加载时也会计算自己的哈希值,然后定期进行检查。
Kprobes 也会带来问题:它们被动态地插入到原本不应改变的内核代码中。LKRG 无法使用哈希来确保这些更改是正确的,但它可以检查这些更改是否仅在 kprobes 实际启用时出现,以及“启用”设置是否符合预期。
默认情况下,LKRG 每隔 15 秒检查一次这些全局属性,但也可以配置为更频繁或更低频地检查,或者仅在被要求时才检查。如果它确实发现了可疑情况,它可以打印日志消息、恢复之前的 SELinux “强制”设置和 SMEP/SMAP 状态,或者触发内核恐慌(panic)。
另一组检查是针对 per-CPU 的。这些检查也是定期运行的,但也可以在任何进程尝试执行某些特权操作之前运行。LKRG 会验证进程是否具有即将执行的操作的凭据,以及栈指针(以及可选的栈帧)是否合理。
这些检查中的每一项都可以单独配置,也可以使用 LKRG 配置文件(profiles)。它提供了几个预配置的检测和强制执行级别。这些级别从“仅在请求时检测”到“尽可能仔细检查所有内容”(通过 lkrg.profile_validate sysctl 设置),以及从“检测到违规时记录日志”到“触发内核恐慌”(通过 lkrg.profile_enforce sysctl 设置)。默认设置是一个合理的中间地带:在没有过多性能开销的情况下检查内核完整性,记录可疑行为,并在出现确定的问题时使内核崩溃。
它值得吗?
一方面,LKRG 确实存在一些公认的缺点:LKRG 的默认强制执行级别与运行 VirtualBox 不兼容;它在虚拟机内部运行良好,但当客户机 CPU 的状态与宿主机不匹配时,会在宿主机上引起误报。(一位 LKRG 维护者致信指出,该问题仅存在于 VirtualBox 上,而 KVM 在默认配置下可以正常工作。)这并非 LKRG 的根本限制——它可以被扩展为跟踪虚拟机的进入和退出,并相应地调整其预期——但考虑到有多少工作负载涉及一定程度的虚拟化,这确实使得很难默认推荐 LKRG。
性能也是一个问题。2020 年的一项 Phoronix 基准测试套件对比(开启和关闭 LKRG)表明,它引入了约 4.4% 的性能开销。自那时起,LKRG 开发人员已将其降低到 2.5% 左右,但测量中的误差使得很难确定确切的性能损失,尤其是因为它可能取决于工作负载。还有一个事实是,作为一个树外模块,它并不总是能立即支持新的内核版本。该项目的 README 建议,LKRG 的最佳用途是那些由于某种原因无法总是更新到最新内核以应对已公开漏洞的系统。
许多用户会为了安全而欢迎少许的性能下降,但有更简单的方法可以获得相同的一些好处。例如,内核内置的控制流完整性(control flow integrity,CFI)支持通过成熟的编译器和 CPU 特性提供了与 LKRG 栈检查大致相同的好处。它验证的其他内容较难被替代,但内核在采用保护页表的新内置方法方面有着悠久的历史。这些想法的实现中总有可能存在漏洞,但很难判断 LKRG 是否能足够可靠地检测到这些漏洞的利用从而体现其价值。
另一方面,Singularity Rootkit 计划绕过 LKRG。Metasploit 框架拥有专门的代码来处理它,并禁用它会检测到的漏洞利用。当 LKRG 证明确实能让攻击者的生活变得更加艰难时,很难辩称它不值得使用。
最终,LKRG 有一个相对小众的使用场景:当性能开销可以接受、计算机的工作负载不涉及 VirtualBox,且系统运行的不是最前沿的内核(或者用户乐于运行 LKRG 的预发布版本)时,LKRG 可以提供纵深防御(defense in depth)。或许有一天,LKRG 的数据验证功能会进入内核主线;这目前似乎并不是开发人员的优先事项。在 LKRG 的想法进入内核之前,用户必须根据自己的使用场景做出是否适合使用 LKRG 的判断。
LWN 评论概述:
一位 LKRG 维护者指出,文章中关于虚拟化不兼容性的描述存在细微误解:LKRG 在默认配置下与 KVM 和 libvirt 完全兼容,仅在作为 VirtualBox 宿主机运行时需要调整特定设置。此外,维护者澄清了对最新内核的支持情况,表示虽然正式发布版本可能略有滞后,但项目的持续集成(CI)系统始终确保代码能够兼容最新的开发版内核(如 6.19-rc)。
全文完 LWN 文章遵循 CC BY-SA 4.0 许可协议。 长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~