0
点赞
收藏
分享

微信扫一扫

操作系统大赛:基于 eBPF 的容器监控工具 Eunomia 初赛报告(目标描述、ebpf 调研)


操作系统大赛:基于 eBPF 的容器监控工具 Eunomia (目标描述、ebpf 监控调研)

项目仓库:https://github.com/yunwei37/Eunomia

2. 目标描述

本项目由两部分组成:

  • 一个零基础入门 ​​eBPF​​​ 技术的教程实践和对应的命令行工具集,主要使用 ​​C/C++​​ 语言开发, 同时作为原型验证;
  • 一个基于 ​​eBPF​​ 技术实现的用于监控容器的工具(Eunomia), 包含 ​​profile​​、容器集群网络可视化分析、容器安全感知告警、一键部署、持久化存储监控等功能, 主要使用 C/C++ 语言开发, 力求为工业界提供覆盖容器全生命周期的轻量级开源监控解决方案;

2.1. 功能概述

​Eunomia​​​ 是一个使用 C/C++ 开发的基于eBPF的云原生监控工具,旨在帮助用户了解容器的各项行为、监控可疑的容器安全事件,力求为工业界提供覆盖容器全生命周期的轻量级开源监控解决方案。它使用 ​​Linux​​​ ​​eBPF​​​ 技术在运行时跟踪您的系统和应用程序,并分析收集的事件以检测可疑的行为模式。目前,它包含 ​​profile​​、容器集群网络可视化分析*、容器安全感知告警、一键部署、持久化存储监控等功能。

  • 开箱即用:以单一二进制文件或 ​​docker​​ 镜像方式分发,一次编译,到处运行,一行代码即可启动,包含多种 ebpf 工具和多种监测点,支持多种输出格式(json, csv, etc) 并保存到文件;
  • 通过 ​​ebpf​​ 自动收集容器相关元信息,并和多种指标相结合;
  • 可集成 ​​prometheus​​​ 和 ​​Grafana​​,作为监控可视化和预警平台;


  • 可通过 ​​graphql​​​ 在远程发起 http 请求并执行监控工具,将产生的数据进行聚合后返回,用户可自定义运行时扩展插件进行在线数据分析;可外接时序数据库,如 ​​InfluxDB​​ 等,作为可选的信息持久化存储和数据分析方案;

除了收集容器中的一般系统运行时内核指标,例如系统调用、网络连接、文件访问、进程执行等,我们在探索实现过程中还发现目前对于 ​​lua​​​ 和 ​​nginx​​​ 相关用户态 ​​profile​​​ 工具和指标可观测性开源工具存在一定的空白,但又有相当大的潜在需求;因此我们还计划添加一系列基于 uprobe 的用户态 ​​nginx/lua​​ 追踪器,作为可选的扩展方案;(这部分需求来自中科院开源之夏, APISIX 社区的选题)

和过去常用的 ​​BCC​​​ 不同,​​Eunomia​​​ 基于 ​​Libbpf​​​ + BPF CO-RE(一次编译,到处运行)开发。Libbpf 作为 BPF 程序加载器,接管了重定向、加载、验证等功能,BPF 程序开发者只需要关注 BPF 程序的正确性和性能即可。这种方式将开销降到了最低,且去除了庞大的依赖关系,使得整体开发流程更加顺畅。目前,我们已经发布了 ​​pre-release​​ 的版本,其中部分功能已经可以试用,只需下载二进制文件即可运行,

2.2. Tutorial

​Eunomia​​​ 的 ​​ebpf​​​ 追踪器部分是从 ​​libbpf-tools​​​ 中得到了部分灵感,但是目前关于 ebpf 的资料还相对零散且过时,这也导致了我们在前期的开发过程中走了不少的弯路。因此, 我们也提供了一系列教程,以及丰富的参考资料,旨在降低新手学习eBPF技术的门槛,试图通过大量的例程解释、丰富对 ​​eBPF、libbpf、bcc​​​ 等内核技术和容器相关原理的认知,让后来者能更深入地参与到 ebpf 的技术开发中来。另外,​​Eunomia​​​ 也可以被单独编译为 C++ 二进制库进行分发,可以很方便地添加自定义 libbpf检查器,或者直接利用已有的功能来对 syscall 等指标进行监测,教程中也会提供一部分 ​​EUNOMIA​​ 扩展开发接口教程。

教程目前还在完善中。

  1. ​​eBPF介绍​​
  2. ​​eBPF开发工具介绍: BCC/Libbpf,以及其他​​
  3. ​​基于libbpf的内核级别跟踪和监控: syscall, process, files 和其他​​
  4. ​​基于uprobe的用户态nginx相关指标监控​​
  5. ​​seccomp权限控制​​
  6. ​​上手Eunomia: 基于Eunomia捕捉内核事件​​

3. 比赛题目分析和相关资料调研

3.1. 题目描述

容器是一种应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享操作系统内核。这使得容器的隔离性相对较弱,带来安全上的风险,最严重时会导致容器逃逸,严重影响底层基础设施的保密性、完整性和可用性。

eBPF 是一个通用执行引擎,能够高效地安全地执行基于系统事件的特定代码,可基于此开发性能分析工具**、网络数据包过滤、系统调用过滤,**系统观测和分析等诸多场景。eBPF可以由hook机制在系统调用被使用时触发,也可以通过kprobe或uprobe将eBPF程序附在内核/用户程序的任何地方。

这些机制让eBPF的跟踪技术可以有效地感知容器的各项行为,包括但不限于:

  • 容器对文件的访问
  • 容器对系统的调用
  • 容器之间的互访

请基于eBPF技术开发一个监控工具,该工具可以监控容器的行为,并生成报表(如json文件)将各个容器的行为分别记录下来以供分析。

  • 第一题:行为感知
    编写eBPF程序,感知容器的各项行为。
  • 第二题:信息存储
    在第一题的基础上,令工具可以将采集到的数据以特定的格式保存在本地。
  • 第三题:权限推荐(可选)
    Seccomp是Linux内核的特性,开发者可以通过seccomp限制容器的行为。capabilities则将进程作为root的权限分成了各项更小的权限,方便调控。这两个特性都有助于保障容器安全,但是因为业务执行的逻辑差异,准确配置权限最小集非常困难。请利用上面开发的监控工具,分析业务容器的行为记录报表,然后基于报表自动推荐精准的权限配置最小集。

3.2. 赛题分析

本赛题分为三个部分,第一部分为编写ebpf程序,感知容器行为,这一部分的关键点在于找到合适的ebpf程序挂载点。挂载点确定后我们可以使用现有的各种ebpf开发框架编写开发代码,完成此部分任务。第二部分信息存储则需要我们将内核态捕捉到的信息传递到用户态,然后在用户态进行输出。这一部分具有较大的可操作空间。我们可以使用最简单的打印方式,将所有数据打印出来,也可以将所有数据存储到日志文件中,还可以通过可视化的手段,将数据进行可视化展示。第三部分则涉及到了一个新的模块Seccomp,seccomp是linux内核中的一个安全模块,可以限制某一进程可以调用的syscall的数量。该技术可以进一步增强本工具的监督保护能力,因此本次也应当将其融合进入本项目。

3.3. 相关资料调研

3.3.1. ebpf

eBPF是一项革命性的技术,可以在Linux内核中运行沙盒程序,而无需更改内核源代码或加载内核模块。通过使Linux内核可编程,基础架构软件可以利用现有的层,从而使它们更加智能和功能丰富,而无需继续为系统增加额外的复杂性层。

  • 优点:低开销
    eBPF 是一个非常轻量级的工具,用于监控使用 Linux 内核运行的任何东西。虽然 eBPF 程序位于内核中,但它不会更改任何源代码,这使其成为泄露监控数据和调试的绝佳伴侣。eBPF 擅长的是跨复杂系统实现无客户端监控。
  • 优点:安全
    解决内核观测行的一种方法是使用内核模块,它带来了大量的安全问题。而eBPF 程序不会改变内核,所以您可以保留代码级更改的访问管理规则。此外,eBPF 程序有一个验证阶段,该阶段通过大量程序约束防止资源被过度使用,保障了运行的ebpf程序不会在内核产生安全问题。
  • 优点:精细监控、跟踪
    eBPF 程序能提供比其他方式更精准、更细粒度的细节和内核上下文的监控和跟踪标准。并且eBPF监控、跟踪到的数据可以很容易地导出到用户空间,并由可观测平台进行可视化。
  • 缺点:很新
    eBPF 仅在较新版本的 Linux 内核上可用,这对于在版本更新方面稍有滞后的组织来说可能是令人望而却步的。如果您没有使用较新版本的 Linux 内核,那么 eBPF 根本不适合您。

3.3.2. ebpf 开发工具技术选型

原始的eBPF程序编写是非常繁琐和困难的。为了改变这一现状,llvm于2015年推出了可以将由高级语言编写的代码编译为eBPF字节码的功能,同时,其将 ​​bpf()​​​ 等原始的系统调用进行了初步地封装,给出了 ​​libbpf​​ 库。这些库会包含将字节码加载到内核中的函数以及一些其他的关键函数。在Linux的源码包的 ​​samples/bpf/​​ 目录下,有大量Linux提供的基于 ​​libbpf​​ 的eBPF样例代码。一个典型的基于 ​​libbpf​​ 的eBPF程序具有 ​​*_kern.c​​ 和 ​​*_user.c​​ 两个文件,
​*_kern.c​​ 中书写在内核中的挂载点以及处理函数, ​​*_user.c​​ 中书写用户态代码,完成内核态代码注入以及与用户交互的各种任务。

更为详细的教程可以参考​​该视频​​。

然而由于该方法仍然较难理解且入门存在一定的难度,因此现阶段的eBPF程序开发大多基于一些工具,比如:

  • ​BCC​
  • ​BPFtrace​
  • ​libbpf​
  • ​go-libbpf​
  • etc

目前使用较多的是 ​​BCC​​​ 工具,但本项目放弃了 ​​BCC​​​ ,选择了 ​​libbpf​​ 作为我们的开发工具。

​BCC​​​ 全称为 ​​BPF Compiler Collection​​​ ,是一个python库,包含了完整的编写、编译、和加载 ​​BPF​​​ 程序的工具链,以及用于调试和诊断性能问题的工具。自2015年发布以来,​​BCC​​​ 经过上百位贡献者地不断完善后,目前已经包含了大量随时可用的跟踪工具。并且 ​​其官方项目库​​​ 提供了一个方便上手的教程,用户可以快速地根据教程完成 ​​BCC​​​ 入门工作。用户可以在 ​​BCC​​ 上使用Python、Lua等高级语言进行编程。

相较于使用C语言直接编程,这些高级语言具有极大的便捷性,用户只需要使用C来设计内核中的 ​​BPF​​​ 程序,其余包括编译、解析、加载等工作在内,均可由 ​​BCC​​ 完成。

然而使用 ​​BCC​​​ 存在一个缺点便是在于其兼容性并不好。基于 ​​BCC​​​ 的 ​​eBPF​​​ 程序每次执行时候都需要进行编译,编译则需要用户配置相关的头文件和对应实现。在实际应用中,相信大家也会有体会,编译依赖问题是一个很棘手的问题。也正是因此,在本项目的开发中我们放弃了BCC,选择了可以做到一次编译-多次运行的 ​​libbpf​​ 工具。

​libbpf-bootstrap​​​ 是一个基于 ​​libbpf​​​ 库的BPF开发脚手架,从其 ​​github​​​ 上可以得到其源码。 ​​libbpf-bootstrap​​ 综合了BPF社区过去多年的实践,为开发者提了一个现代化的、便捷的工作流,实现了一次编译,重复使用的目的。

基于 ​​libbpf​​​ 的BPF程序在编译时会先将 ​​*.bpf.c​​​ 文件编译为对应的​​.o​​​文件,然后根据此文件生成 ​​skeleton​​​ 文件,即 ​​*.skel.h​​​ ,这个文件会包含内核态中定义的一些数据结构,以及用于装载内核态代码的关键函数。在用户态代码 ​​include​​ 此文件之后调用对应的装载函数即可将字节码装载到内核中。

我们选择现代 C++ 语言(cpp20)开发 Eunomia 的时候也主要是看中和 libbpf 库以及 bpf 代码的良好兼容性,libbpf 库目前还在迅速更新迭代过程中,我可以直接基于 libbpf 库进行开发,不需要被其他语言(go/rust)的运行时 bpf 库所限制。现代 C++ 的开发速度和安全性应该并不会比其他语言差太多(要是编译提示能像 rust 那样好点就更好了,用了 concept 还是不够好)

3.3.3. 容器可观测性

​Docker​​类容器本身提供了较多命令用于观测容器,比如:

  • ​docker ps​​ 命令可以显示出目前正在运行的所有容器的ID,名称,运行时间等等数据,
  • ​docker top​​ 命令可以显示容器中所有正在运行的进程,并且显示其在宿主机上的进程号,通过这种方式我们可以在宿主机中找到和容器有关的进程号并进行重点追踪。
  • ​docker inspect​​ 命令则可以根据需要具体查看容器的各种信息。

通过这些命令,我们可以较为快速地得到容器内的一些情况。

容器中的进程会映射到宿主机中,他们和宿主机上的其他进程最直接的区别就在于namespace。为了隔离资源,容器中的进程和宿主机上的进程具有不同的namespace。因此,监测容器行为可以转变为监测特定进程,通过复用现有process模块的基础上添加container追踪模块。

容器追踪模块的内核态ebpf代码和process模块一样,都是利用了 ​​sched_process_exec​​​ 和 ​​sched_process_exit​​​ 两个挂载点,区别在于用户态代码中对于内核态返回的数据的处理方式。在容器追踪模块中,每次有内核态数据写入时我们会调用 ​​judge_container()​​​ 函数,该函数会检查此进程的namespace和其父进程是否相同。如果相同那么我们就会认为他和父进程是归为一类的,通过检查存有所有容器进程信息的哈希map即可确定此新进程的归属。如果不同,那么我们便会认为可能有新的容器产生。对于 ​​Docker​​​ 类容器,我们会直接调用 ​​Docker​​​ 给出的命令的进行观测。首先调用 ​​docker ps -q​​​ 命令获得现有在运行的所有容器id,之后调用 ​​docker top id​​ 命令获取容器中的进程在宿主机上的进程信息,如果这些信息没有被记录到哈希map中,那么就将他们添加到其中并输出。在有进程退出时,我们只需要检查其是否存在于哈希map,如果存在删去即可。

3.3.4. 信息可视化展示

​Prometheus​​​ 是一套开源的监控、报警、时间序列数据库的组合,受启发于Google的Brogmon监控系统,2012年开始由前Google工程师在Soundcloud以开源软件的形式进行研发,并且于2015年早期对外发布早期版本。2016年,​​Prometheus​​​ 加入了云计算基金会,成为 ​​kubernetes​​ 之后的第二个托管项目。其架构如下所示:

Prometheus架构

操作系统大赛:基于 eBPF 的容器监控工具 Eunomia 初赛报告(目标描述、ebpf 调研)_docker

​Prometheus​​具有以下特点:

  • 可以自定义多维数据模型并且使用metric和
  • 存储高效,不依赖分布式存储,支持单节点工作
  • 使用灵活且强大的查询语言 ​​PromQL​
  • 通过基于http的pull方式采集时许数据
  • 通过push gateway进行序列数据推送

​Grafana​​ 是一款用Go语言开发的开源数据可视化工具,具有数据监控、数据统计和告警功能,是目前较为流行的一种时序数据展示工具,并且支持目前绝大部分常用的时序数据库。

在本项目中,我们计划将程序捕获到的数据使用 ​​Prometheus​​​ 进行存储,之后对于存储的数据我们使用 ​​Grafana​​ 进行可视化

3.3.5. 容器运行时安全

确保容器运行时安全的关键点:

  • 使用 ​​ebpf​​ 跟踪技术自动生成容器访问控制权限。包括:容器对文件的可疑访问,容器对系统的可疑调用,容器之间的可疑互访,检测容器的异常进程,对可疑行为进行取证。例如:
  • 检测容器运行时是否创建其他进程。
  • 检测容器运行时是否存在文件系统读取和写入的异常行为,例如在运行的容器中安装了新软件包或者更新配置。
  • 检测容器运行时是否打开了新的监听端口或者建立意外连接的异常网络活动。
  • 检测容器中用户操作及可疑的 shell 脚本的执行。

项目仓库:https://github.com/yunwei37/Eunomia


举报

相关推荐

0 条评论