5. eBPF 在云原生环境中的应用
近年来,云计算原生方案呈指数增长。在本章中,我将讨论为什么 eBPF 如此适合云原生环境的工具。为了具体起见,我将参考 Kubernetes,但相同的概念适用于任何使用容器的平台。
每台主机一个内核
要理解为什么 eBPF 在云原生世界中如此强大,你需要非常清楚一个概念:每台机器(或虚拟机)只有一个内核,并且该机器上运行的所有容器共享同一个内核 ,1 如图 5-1 所示。同一个内核参与并知道在任何给定主机上运行的所有应用程序代码。
通过检测内核,就像在使用 eBPF 时所做的那样,我们可以同时检测在该机器上运行的所有应用程序代码。当我们将 eBPF 程序加载到内核中并将其附加到事件时,无论事件涉及哪个进程,eBPF 程序都会被触发。
图 5-1. 同一主机上的所有容器共享一个内核
eBPF vs Sidecar 模型
在 eBPF 之前,大多数 Kubernetes 的可观察性和安全工具都使用 sidecar 模型。此模型允许将检测部署在单独的容器中,但与应用程序位于同一 pod 中。当这种方法被采用时,已经前迈进了一步,因为这意味着你不再需要直接在应用程序中编写检测代码。只需部署 sidecar,它就可以看到同一个 pod 中的其他容器。 注入 sidecar 的过程通常是自动化的,因此这提供了一种机制来确保所有应用程序都被检测。
每个 sidecar 容器都会消耗资源,总量需要乘以注入 sidecar 的 pod 数量。这可能非常重要——例如,如果每个 sidecar 都需要自己的路由信息或策略规则副本,那么这是一种浪费。(关于这方面的更多信息,Thomas Graf 写了一篇 [sidecars 与 eBPF 用于服务网格的比较](https://isovalent.com/blog/post/2021-12-08-ebpf-servicemesh)。)
Sidecar 的另一个问题是,你不能保证机器上的每个应用程序都已正确检测。想象一下,攻击者设法破坏了一个主机并启动了一个单独的 pod 来运行,例如进行挖矿。他们不太可能礼貌地使用 sidecar 可观察性或安全工具来检测他们的挖矿程序。这时,你需要一个单独的系统来了解相关活动。
但是同一个挖矿 pod 与在该主机上运行的合法 pod 共享内核。 如果你使用基于 eBPF 的工具,如图 5-2 所示,挖矿者会被发现。
图 5-2.sidecar 只能观察到 pod 内的活动,但 eBPF 程序可以观察到所有活动
eBPF 和进程隔离
我主张将功能整合到单个每个节点的基于 eBPF 的代理中,而不是每个 pod 的 sidecar。如果该代理可以访问机器上运行的所有 Pod,这不是安全风险吗?我们是否已经失去了应用程序之间可能会阻止它们相互干扰的隔离?
作为一个花了很多时间在容器安全方面工作的人,我可以理解这些担忧,但重要的是要深入研究底层机制,以真正理解为什么它不是最初可能出现的缺陷。
需要记住的重要一点是,这些 pod 都共享一个内核,并且内核对 pod 或容器没有天生的理解。相反,内核对进程进行操作,并使用 cgroup 和命名空间将进程彼此隔离。这些结构阻止用户空间中的进程能够看到或相互干扰,这是由内核控制的。一旦在内核中处理数据(例如,从磁盘读取或发送到网络),你就依赖于内核的正确行为。例如,只有内核代码说应该尊重文件权限。没有额外的魔法权威层可以阻止内核忽略文件权限,并从它想要访问的任何文件中读取数据 —— 只是内核本身没有被编码为不这样做。
Linux 系统上存在的安全控制假定内核本身是可以信任的。它们可以防止在用户空间中运行的代码出现不良行为。
我们在第 2 章中看到,eBPF 验证器确保任何 eBPF 程序只尝试访问它应该有权访问的内存。验证程序检查程序是否不可能超出其权限,包括确保内存由当前进程拥有或者是当前网络数据包的一部分。这意味着 eBPF 代码比周围的内核代码受到更严格的控制,无需通过任何类型的验证步骤。
如果攻击者将容器化应用程序逃逸到节点上,并且能够提升权限,则该攻击者可以破坏同一节点上的其他应用程序。由于这些逃逸并不为人所知,因此作为容器安全专家,我不建议在没有某种级别的额外安全工具的情况下与不受信任的应用程序或用户一起在共享机器上运行敏感应用程序。对于高度敏感的数据,你甚至可能不想与不受信任的用户在同一裸机上的虚拟机中运行。但是,如果你准备在同一个虚拟机上并行运行应用程序(这在许多不是特别敏感的应用程序中是完全合理的),那么 eBPF 不会通过共享内核增加已经存在的风险。
当然,恶意的 eBPF 程序可能会造成各种破坏,编写恶意 eBPF 代码当然很容易——例如,复制每个网络数据包并将其发送给窃听者。默认情况下,非 root 用户不能加载 eBPF 程序的权限,2 你应该只授予信任用户或软件系统这个权限,就像 root 权限一样。所以,你必须小心运行的代码的出处(并且有一个[倡议](https://lwn.net/Articles/853489/)正在发挥作用,以支持对 eBPF 程序的签名检查,以帮助解决这种问题)。你还可以使用 eBPF 程序来密切关注其他 eBPF 程序!
现在你已经大致了解了为什么 eBPF 是云原生工具的强大基础,下一章将为你提供一些来自云原生生态系统的 eBPF 工具的具体示例。
1. 这几乎总是正确的,除非你使用像 Kata 容器、Firecracker 或 unikernel 这样的虚拟化方法,让每个“容器”都在自己的虚拟机中运行。 ↩
2. Linux 能力 CAP_BPF 授予加载 BPF 程序的权限。 ↩