本文已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

在 Kubernetes 中使用 eBPF

简介

Kubernetes 提供了一个高级 API 和一组组件,几乎隐藏了系统级别发生的所有复杂且(对我们中的一些人来说)有趣的细节。应用程序开发人员不需要了解机器的 IP 表、cgroup、命名空间、seccomp,甚至现在也不需要了解其应用程序运行在其上的容器运行时。但在底层,Kubernetes 和它所依赖的技术(例如,容器运行时)大量利用了核心 Linux 功能。

本文重点介绍在网络、安全和审计,以及跟踪和监控工具中越来越多地使用的核心 Linux 功能。此功能称为扩展的伯克利数据包过滤器(eBPF)

注意:在本文中,我们使用两个首字母缩写词:eBPF 和 BPF。前者用于扩展的 BPF 功能,后者用于“经典” BPF 功能。

什么是 BPF?

BPF 是一个驻留在 Linux 内核中的小型虚拟机,它运行 BPF 程序。在运行之前,BPF 程序使用 bpf() 系统调用加载,并进行安全验证:检查循环、代码大小等。BPF 程序附加到内核对象,并在这些对象上发生事件时执行 - 例如,当网络接口发出数据包时。

BPF 超能力

BPF 程序从定义上来说是事件驱动的,这是一个非常强大的概念,并且在发生事件时在内核中执行代码。 Netflix 的 Brendan Gregg 将 BPF 称为Linux 超能力

eBPF 中的“e”

传统上,BPF 只能附加到套接字以进行套接字过滤。 BPF 的第一个用例是在 tcpdump 中。当您运行 tcpdump 时,过滤器会被编译成 BPF 程序,并附加到原始的 AF_PACKET 套接字,以便打印出过滤后的数据包。

但多年来,eBPF 添加了附加到其他内核对象的能力。除了套接字过滤之外,一些受支持的附加点是

  • Kprobes(和用户空间等效的 uprobes)
  • Tracepoints
  • 用于分类或操作的网络调度器或 qdiscs (tc)
  • XDP(eXpress 数据路径)以及其他更新的功能,如内核助手函数和共享数据结构(映射),可用于与用户空间通信,扩展了 BPF 的功能。

eBPF 在 Kubernetes 中的现有用例

一些开源 Kubernetes 工具已经使用了 eBPF,许多用例值得仔细研究,尤其是在网络、监控和安全工具等领域。

使用 Cilium 的动态网络控制和可见性

Cilium 是一个网络项目,它大量使用 eBPF 超能力来路由和过滤基于容器的系统的网络流量。通过使用 eBPF,Cilium 可以动态生成和应用规则,即使是在使用 XDP 的设备级别,而无需更改 Linux 内核本身。

Cilium 代理在每台主机上运行。它不是管理 IP 表,而是将网络策略定义转换为加载到内核并附加到容器的虚拟以太网设备的 BPF 程序。这些程序在发送或接收的每个数据包上执行(应用规则)。

此图显示了 Cilium 项目的工作方式

根据应用的网络规则,可以使用 tcXDP 附加 BPF 程序。通过使用 XDP,Cilium 可以将 BPF 程序附加到最低的可能点,这也是网络软件堆栈中性能最高的点。

如果您想了解更多关于 Cilium 如何使用 eBPF 的信息,请查看该项目的BPF 和 XDP 参考指南

在 Weave Scope 中跟踪 TCP 连接

Weave Scope 是一种用于监控、可视化和与基于容器的系统交互的工具。就我们的目的而言,我们将重点介绍 Weave Scope 如何获取 TCP 连接。

Weave Scope 采用一个在集群的每个节点上运行的代理。该代理监控系统,生成报告并将其发送到应用程序服务器。应用程序服务器编译它接收到的报告,并在 Weave Scope UI 中显示结果。

为了准确地绘制容器之间的连接,代理将 BPF 程序附加到跟踪套接字事件的 kprobes:打开和关闭连接。 BPF 程序 tcptracer-bpf 被编译成 ELF 对象文件,并使用 gobpf 加载。

(作为旁注,Weave Scope 还有一个利用 eBPF 的插件:HTTP 统计信息。)

要了解更多关于它是如何工作以及为什么这样做,请阅读 这篇详细的文章,这是 Kinvolk 团队为 Weaveworks 博客撰写的。您还可以观看关于该主题的最近的演讲

使用 seccomp-bpf 限制系统调用

Linux 有 300 多个系统调用(read、write、open、close 等)可供使用 - 或误用。大多数应用程序只需要一小部分系统调用即可正常运行。seccomp 是一种 Linux 安全工具,用于限制应用程序可以使用的系统调用集,从而限制潜在的误用。

seccomp 的原始实现具有高度的限制性。一旦应用,如果应用程序尝试做任何超出读取和写入已打开文件的操作,seccomp 将发送 SIGKILL 信号。

seccomp-bpf 启用了更复杂的过滤器和更广泛的操作。 seccomp-bpf 也称为 seccomp 模式 2,允许以 BPF 程序的形式应用自定义过滤器。加载 BPF 程序时,过滤器将应用于每个系统调用,并执行相应的操作(允许、杀死、陷阱等)。

seccomp-bpf 广泛用于 Kubernetes 工具并在 Kubernetes 本身中公开。例如,seccomp-bpf 在 Docker 中用于应用自定义的seccomp 安全配置文件,在 rkt 中用于应用seccomp 隔离器,并在 Kubernetes 本身的安全上下文中使用。

但在所有这些情况下,BPF 的使用都隐藏在 libseccomp 之后。在幕后,libseccomp 从提供给它的规则生成 BPF 代码。一旦生成,BPF 程序将被加载并应用规则。

eBPF 在 Kubernetes 中的潜在用例

eBPF 是一种相对较新的 Linux 技术。因此,还有许多用途尚未探索。 eBPF 本身也在不断发展:eBPF 中正在添加新功能,这将启用当前不可能实现的新用例。在以下部分中,我们将研究一些最近才成为可能的功能以及即将出现的功能。我们希望这些功能将被开源工具利用。

Pod 和容器级网络统计信息

BPF套接字过滤并非新事物,但按cgroup进行BPF套接字过滤则是新的。Linux 4.10引入的cgroup-bpf允许将eBPF程序附加到cgroup。一旦附加,该程序将为cgroup中任何进程进出网络的数据包执行。

cgroup在其他方面中,是进程的层次结构分组。在 Kubernetes 中,这种分组位于容器级别。利用 cgroup-bpf 的一种思路是安装 BPF 程序来收集详细的每个 Pod 和/或每个容器的网络统计信息。

通常,此类统计信息是通过定期检查 Linux 的 /sys 目录中的相关文件或使用 Netlink 来收集的。通过使用附加到 cgroup 的 BPF 程序,我们可以获得更详细的统计信息:例如,tcp 端口 443 上有多少数据包/字节,或者来自 IP 10.2.3.4 的有多少数据包/字节。总的来说,由于 BPF 程序具有内核上下文,它们可以安全有效地向用户空间提供更详细的信息。

为了探索这个想法,Kinvolk 团队实现了一个概念验证项目:https://github.com/kinvolk/cgnet。该项目将一个 BPF 程序附加到每个 cgroup,并将信息导出到 Prometheus

当然,还有其他有趣的可能,例如进行实际的数据包过滤。但目前阻碍这一点的障碍是在Docker 和 Kubernetes 中拥有 cgroup v2 的支持,而这是 cgroup-bpf 所必需的。

应用级LSM

Linux 安全模块(LSM)在 Linux 内核中实现了一个用于安全策略的通用框架。SELinuxAppArmor 就是这方面的例子。这两者都在系统全局范围内实现规则,将配置安全策略的责任放在管理员身上。

Landlock 是另一个正在开发的 LSM,它将与 SELinux 和 AppArmor 共存。最初的补丁集已提交到 Linux 内核,目前处于早期开发阶段。与其他 LSM 的主要区别在于,Landlock 旨在允许非特权应用程序构建自己的沙箱,有效地限制自己,而不是使用全局配置。使用 Landlock,应用程序可以加载 BPF 程序,并在进程执行特定操作时执行该程序。例如,当应用程序使用 open() 系统调用打开文件时,内核将执行 BPF 程序,并且根据 BPF 程序的返回值,该操作将被接受或拒绝。

在某些方面,它类似于 seccomp-bpf:使用 BPF 程序,seccomp-bpf 允许非特权进程限制它们可以执行的系统调用。Landlock 将更强大,并提供更大的灵活性。考虑以下系统调用

C  
fd = open(“myfile.txt”, O\_RDWR);

第一个参数是“char *”,指向内存地址的指针,例如 0xab004718

使用 seccomp,BPF 程序只能访问系统调用的参数,但不能解引用指针,因此无法根据文件做出安全决策。seccomp 也使用经典的 BPF,这意味着它不能使用 eBPF 映射,这是与用户空间交互的机制。此限制意味着安全策略不能在 seccomp-bpf 中根据 eBPF 映射中的配置进行更改。

使用 Landlock 的 BPF 程序不会接收系统调用的参数,而是接收对内核对象的引用。在上面的示例中,这意味着它将具有对文件的引用,因此不需要解引用指针,考虑相对路径或执行 chroot。

用例:基于 Kubernetes 的无服务器框架中的 Landlock

在 Kubernetes 中,部署单元是 Pod。Pod 和容器是主要的隔离单元。但是,在无服务器框架中,部署的主要单元是函数。理想情况下,部署单元等于隔离单元。这使 KubelessOpenFaaS 等无服务器框架陷入困境:针对隔离单元还是部署进行优化?

为了实现最佳的隔离,每个函数调用都必须在其自己的容器中发生,但是,对隔离有利的并不总是对性能有利。相反,如果我们在同一容器内运行函数调用,则会增加冲突的可能性。

通过使用 Landlock,我们可以在同一个容器内将函数调用彼此隔离,例如,使一个函数调用创建的临时文件对下一个函数调用不可访问。Landlock 与 Kubernetes 等基于 Kubernetes 的无服务器框架之间的集成将是一个值得进一步探索的领域。

使用 eBPF 审计 kubectl-exec

在 Kubernetes 1.7 中,审计提案开始逐步引入。它目前处于预稳定状态,计划在 1.10 版本中稳定下来。顾名思义,它允许管理员记录和审计 Kubernetes 集群中发生的事件。

虽然这些事件记录了 Kubernetes 事件,但它们目前没有提供某些人可能需要的可见性级别。例如,虽然我们可以看到有人使用 kubectl exec 进入容器,但我们无法看到在该会话中执行了哪些命令。使用 eBPF,可以附加一个 BPF 程序,该程序会记录在 kubectl exec 会话中执行的任何命令,并将这些命令传递给记录这些事件的用户空间程序。然后,我们可以回放该会话,并知道发生的事件的确切顺序。

了解更多关于 eBPF 的信息

如果您有兴趣了解更多关于 eBPF 的信息,这里有一些资源

结论

我们才刚刚开始看到 eBPF 的 Linux 超能力被用于 Kubernetes 工具和技术中。我们无疑会看到 eBPF 的使用增加。我们在这里强调的只是您未来可能期望看到的一小部分。真正令人兴奋的是看到这些技术将如何以我们尚未想到的方式使用。敬请期待!

Kinvolk 团队将在奥斯汀的 KubeCon 的 Kinvolk 展位上驻足。欢迎来和我们聊聊关于 Kubernetes、Linux、容器运行时以及 eBPF 的所有事情。