一些开源框架

常用 BCC 工具在内核中的位置
https://www.brendangregg.com/Perf/bcc_tracing_tools.png

bpftrace 内置的工具

开源项目的主要发展趋势

  • eBPF 在操作系统内核的支持
  • 辅助 eBPF 程序开发和运行的开源项目
  • eBPF 在跟踪监控、网络、安全等各个领域应用的开源项目

Go eBPF 开发库汇总

Go 开发库 使用说明 依赖环境
cilium/ebpf Cilium 和 Cloudflare 开源的 eBPF 开发库,不依赖外部库。不支持所有 eBPF 特性 内核版本 >= 4.4
iovisor/gobpf BCC 库的 Go 语言绑定,底层依赖 BCC 完成 eBPF 程序的动态编译和加载 需安装 BCC 库 libbcc、LLVM、Go 等
aquasecurity/libbpfgo libbpf 的 Go 语言绑定,底层依赖 libbpf 完成 eBPF 程序管理 需安装 libbpf
dropbox/goebpf Dropbox 开源的 eBPF 开发库,支持有限的 eBPF 特性 内核版本 >= 4.15

Rust eBPF 开发库汇总

Rust 开发库 使用说明 依赖环境
libbpf-rs libbpf 的 Rust 绑定,配合 libbpf-cargo 自动生成 eBPF 程序脚手架 需安装 libbpf
redbpf 提供 eBPF 工具集,支持用 Rust 同时开发用户态和内核态程序 需要 LLVM 和内核头文件
Aya 类似 redbpf 的工具集,支持 Rust 开发用户态/内核态程序 可选 LLVM (替换 rust-llvm)
rust-bcc BCC 库的 Rust 语言绑定 需安装 libbcc

eBPF 应用类汇总

项目 类别 说明
Cilium 网络、安全、观测 功能完善的 Kubernetes 网络解决方案,基于 eBPF 实现网络、安全和观测(观测由开源项目 Hubble 提供)
Falco 安全 Sysdig 开源的运行时安全监控项目,可根据预定的安全规则进行告警
Katran 网络 Facebook 开源的高性能四层负载均衡器,底层基于 XDP 实现高性能网络
Calico 网络 流行的 Kubernetes 网络插件,基于 eBPF 实现了高性能数据平面
Tracee 安全 Aqua Security 开源的运行时安全和取证工具,底层使用 eBPF 技术检测和过滤操作系统事件
KubeArmor 安全 容器运行时安全监测和策略执行项目,底层会把安全策略应用到 LSM,从而限制容器内的可疑行为
Pixie 观测 基于 eBPF 的 Kubernetes 集群和应用观测工具,不依赖于任何特定的网络插件
kubectl-trace 调度 将 bpftrace 程序调度到 Kubernetes 集群的 kubectl 插件,简化了 bpftrace 程序的管理
L3AF 调度 分布式环境 eBPF 程序管理和调度平台
ply 观测 为嵌入式系统设计的动态追踪工具,程序生成不依赖于 LLVM
Bumblebee 调度 基于 OCI 镜像构建、运行和分发 eBPF 程序的项目,支持自动将数据导出为指标或日志

eBPF 资源

linux 发行说明

Service Mesh

观察

安全

k8s

利用 eBPF 的能力伪装自身行为的恶意程序

对恶意程序的防御方式

  • 限制 eBPF 程序的运行,禁止普通用户和普通容器应用运行 eBPF 程序,特别是不要给普通进程赋予 CAP_BPF 和 CAP_SYS_ADMIN 权限,更不要给容器随便赋予特权容器的权限
  • 对系统 eBPF 关键事件、eBPF 程序运行状态以及 eBPF 映射的访问等进行审计,异常事件及时告警处理
  • 对系统中的网络连接进行监控,实时探测网络连接的行为,最好是对所有的公网连接透过防火墙进行保护
  • 添加签名机制,只有经过签名的 eBPF 程序才可以加载运行

一次编译到处执行(CO-RE)

解决内核版本不兼容的方案

  • 在运行 eBPF 程序的时候使用当前系统安装的内核头文件进行就地编译,这样就可以确保 eBPF 程序中所引用的内核数据结构和函数签名等,跟运行中的内核是完全匹配的
  • 在 eBPF 程序编译前事先探测内核支持的函数签名和数据结构,进而为 eBPF 程序生成适配当前内核的版本。比如,在块设备 I/O 延迟跟踪程序 biolantecy 中,BCC 借助库函数 BPF.get_kprobe_functions() 来判断内核是不是支持特定的探针函数,进而再根据结果去选择挂载点
1
2
3
4
5
6
7
8
9
  if BPF.get_kprobe_functions(b'__blk_account_io_start'):
    b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_req_start")
  else:
    b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start")
  
  if BPF.get_kprobe_functions(b'__blk_account_io_done'):
      b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_done")
  else:
      b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_done")

CO-RE 借助了 BPF 类型格式(BPF Type Format, 简称 BTF)提供的调试信息,再通过下面的四个步骤

  • 在 bpftool 工具中提供了从 BTF 生成头文件的工具,从而摆脱了对内核头文件的依赖
  • 通过对 BPF 代码中的访问偏移量进行重写,解决了不同内核版本中数据结构偏移量不同的问题
  • 在 libbpf 中预定义不同内核版本中数据结构的修改,解决了不同内核中数据结构不兼容的问题
  • 在 libbpf 中提供一系列的内核特性探测库函数,解决了 eBPF 程序在不同内核内版本中需要执行不同行为的问题。比如,你可以用 bpf_core_type_exists() 和bpf_core_field_exists() 分别检查内核数据类型和成员变量是否存在,也可以用类似 extern int LINUX_KERNEL_VERSION __kconfig 的方式查询内核的配置选项

参考