认识 Tetragon:基于 eBPF 的安全监控与强制执行工具

📅 2026/8/3 10:28:59 👁️ 阅读次数 📝 编程学习
认识 Tetragon:基于 eBPF 的安全监控与强制执行工具

在深入了解思科的内核安全实践之前,我们先来认识一下文中提到的核心工具——Tetragon

Tetragon是由 Cilium 社区(现属于 CNCF 毕业项目)开发的一款开源的、基于eBPF(Extended Berkeley Packet Filter)的安全可观测性与运行时强制执行工具。它的核心优势包括:

  1. 内核态直接处理:与传统安全工具将大量事件推送到用户空间进行分析不同,Tetragon 直接在 Linux 内核空间运行过滤与监控逻辑,性能开销极低。

  2. Kubernetes 原生感知:它不仅能深入监控 Linux 进程生命周期、系统调用和网络活动,还能无缝结合 Kubernetes 的元数据(如 Pod、Namespace 等),实现精准到容器级别的安全审计。

  3. 实时拦截(Enforcement):它不仅能“看”,还能通过 eBPF 钩子在内核中直接修改系统调用返回值等,实时阻止可疑或恶意行为。

利用 BPF 应对定制内核漏洞:思科的挑战与探索

思科在为运行定制内核的众多设备部署安全补丁时,面临着一些不寻常的挑战。在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会上,John Fastabend 介绍了利用 BPF 防止漏洞利用的工作。这项技术可以大大缩短响应内核漏洞所需的时间,但如果不向内核添加更多钩子(hooks),它就无法完全发挥作用。

Fastabend 首先指出,网络交换机的硬件范围很广,从小型单机架系统到大型高速设备应有尽有。思科支持的每个平台都有自己的内核团队,使用 Yocto 构建定制内核。在任何特定时间,这些团队都在支持大量不同的内核——幸运的是,其中大多数是稳定内核。所有这些广泛部署、连接互联网且带有定制内核的设备,都成了攻击者眼中诱人的目标。

显而易见,思科会发布安全更新,但识别问题、编写修复程序、创建新构建、进行测试、提供给客户并让交换机完成更新需要时间,特别是因为重启交换机会导致网络中断。这些中断需要客户计划和管理停机时间,从发现漏洞到修补完最后一台脆弱系统,整个过程可能需要几个月。Fastabend 这项工作的目标是利用 BPF 实时观察攻击,并在无需重启的情况下按需应对。他表示,理想情况下,整个过程只需几分钟。“我们离那个目标还有一段距离,但这正是我们的梦想。”

Tetragon 是一款开源的、基于 BPF 的监控与强制执行工具,用于收集有关运行系统的“大量数据”。在任何时候,交换机上的监控基础设施都可以显示哪些程序在何时运行,以及它们建立了哪些网络连接。这些数据存储在时序数据库中。Tetragon 目前确实依赖用户空间代理保持运行,但 Fastabend 及其同事一直在努力确保即使攻击者设法杀死了用户空间代理,BPF 组件也能继续存活。他解释说,当发现新的 CVE(通用漏洞披露)时,他希望能够对照该数据库进行检查,以查明它是否曾被利用过。这些数据还可以用来查看攻击的征兆,例如数据外泄或与命令与控制(C&C)服务器的连接。

一旦识别出漏洞利用,就可以直接从 BPF 中对其进行阻止。如果触发漏洞利用需要某个特定的系统调用,BPF 可以覆盖该系统调用的返回值以拒绝该操作。还可以使用跟踪点(tracepoints)来验证内部内核函数的参数是否正确。Fastabend 的团队同时使用了 uprobes 和 kprobes 来实现这一点。不过,这些探测点无法可靠地改变返回值,因此需要使用 Linux 安全模块(LSM)钩子来实现。

Andrii Nakryiko 询问这种设计每秒要检查并可能拦截多少个事件。Fastabend 解释说,网络数据包的路由主要由专用硬件完成,因此内核只需管理控制平面流量和用户空间应用程序。总体而言,每秒只有数百或数千个事件,而不是数十亿个,即使交换机每秒正在转发数十亿个数据包。

其中一个复杂之处在于,Fastabend 的团队还希望使用探测点来对内联函数(inlined functions)进行操作。这可以通过使用调试符号并在原始偏移量处设置探测点来实现。他解释说,思科有一个用于生产所有内核软件包的构建农场。构建机器保存了所有构建的构建 ID 和调试信息,包括 BTF(BPF Type Format)和常规调试符号。这些信息不仅用于调试客户的问题,还用于编写特定于所部署内核结构的实时内核补丁或 BPF 程序。

Jakub Sitnicki 指出,他在将探测点附加到部分内联的函数时遇到了问题,因为内核当前的 BTF 中没有包含这些函数被内联位置的信息,不过这个问题正在解决中。Alan Maguire 表示,这个话题将在他为会议第二天提议的其中一个会议上进行讨论。

阻止漏洞利用

Fastabend 随后展示了一个 BPF 程序的示例,该程序可用于阻止近期“copy fail”漏洞的影响。该程序仅在以会触发漏洞的方式调用splice()系统调用时,让其返回一个错误。他承认,使用普通的内核实时补丁在技术上也可以做到这一点,但思科拥有如此多的并发产品线,其上运行着许多不同的稳定内核,修补所有这些内核需要投入大量开发人员时间。而通过 BPF,同一个程序通常可以在所有支持的内核上运行——只需编写一次,然后在每个内核上自动测试以确保它不会破坏任何东西。

BPF 盾牌(shields)在起作用时非常棒,但也偶尔会有一些小波折。Fastabend 的团队经常会发现某个 CVE 在受影响的内核子系统中没有任何相关的钩子来构建盾牌。最近,就有一个可利用的“释放后重用”(use-after-free)漏洞,在接近问题根源的附近根本没有方便挂载钩子的位置。团队最终选择挂载可能导致触发该漏洞的系统调用,但这使得代码变得更加复杂。

因此,Fastabend 希望在内核中看到的、用于支持此类工作的主要改变,是为ALLOW_ERROR_INJECTION()采用更具包容性的政策。该宏用于标记可受内核错误注入框架支配的函数。该框架通常用于测试,它允许内核程序员使用自定义 BPF 程序覆盖内部函数的返回值,这与 Fastabend 的用例非常契合。不幸的是,只有一部分内核函数被标记为可用于错误注入。理想情况下,他希望能够使用 BPF 修改任何返回整数的函数的返回值,只要该整数与零进行比较并将该错误沿调用栈向上传播。他指出,有很多函数都符合这个标准。他认为应该为所有这些函数提供 LSM 钩子,并询问这些函数是否可以轻松地做到从 BPF 进行挂载。

Nakryiko 认为,错误处理代码的复杂性将使任何此类更改都变成一个手动过程。Alexei Starovoitov 建议,对于 Rust 代码来说,这应该是可以自动完成的,因为 Rust 代码中由编译器生成的清理逻辑具有可预测的结构。对于 C 代码,他建议让提交漏洞报告的人也引入相关的 LSM 钩子。笔者则建议使用 Coccinelle 来实现这一更改。

Fastabend 认为,如果最终确实需要手动操作,最需要作为目标的区域是内核的 netlink 代码。出于某种原因,他的团队发现有大量攻击针对内核的这一区域。

将挂载内部内核函数作为防范 vulnerabilities(漏洞)的技术,对于 Linux 内核的其他用户也同样非常有用。他展示的盾牌只有区区几行代码,不难看出,只要有足够的内核覆盖率,这种简单的修复方法就能为公司或发行版提供一种在多个内核版本之间部署 vulnerability mitigations(漏洞缓解措施)且无忧的途径。话虽如此,向相关区域添加 LSM 钩子的工作将是一项重大改变,可能需要一些时间——前提是 LSM 维护人员能够批准这项工作。