Skip to content

架构设计

eCapture 是一个基于 eBPF 的复杂可观测性平台,能够在不需要 CA 证书或代码修改的情况下捕获 SSL/TLS 明文并执行系统审计。系统架构由五个主要层级组成,它们协同工作以在不同系统级别拦截加密流量并生成格式化输出。

本页面提供高层次的架构概述。有关特定组件的详细信息,请参阅:

有关模块特定的实现细节,请参阅捕获模块。有关构建系统架构,请参阅构建系统

系统架构概述

eCapture 实现了一个五层架构,具有清晰的关注点分离。数据从被监控的应用程序通过内核空间的 eBPF 钩子流向用户空间的事件处理,最终以多种格式(文本、PCAP-NG、密钥日志文件或 protobuf 流)生成格式化输出。

图表:五层架构

来源:cli/cmd/root.go:80-153, user/module/probe_openssl.go:83-106, user/module/imodule.go:47-75, user/module/probe_openssl.go:178-278, user/module/imodule.go:191-214

架构层级说明

每一层都有特定的职责:

层级职责关键组件
1. 用户界面命令解析、配置输入、运行时更新rootCmd (Cobra CLI)、HTTP 配置服务器、eCaptureQ 集成
2. 捕获模块协议特定逻辑、字节码选择、探针附加IModule 接口、MOpenSSLProbeMGoTLSProbe
3. eBPF 运行时版本检测、CO-RE/non-CO-RE 选择、eBPF 程序生命周期manager.Manager、uprobe/TC/kprobe 程序、BTF 检测
4. 事件处理事件读取、聚合、协议解析、连接跟踪EventProcessoreventWorkerIParser 实现
5. 输出格式转换、文件写入、网络流Text/PCAP/Keylog/Protobuf 写入器、PCAP-NG DSB 块

有关 IModule 接口的详细信息,请参阅模块系统与生命周期;有关事件流详细信息,请参阅事件处理流程

关键架构决策

架构做出了几个关键的设计决策以实现其功能:

决策理由实现
模块的工厂模式支持基于 CLI 命令的动态模块加载IModule 接口 user/module/imodule.go:47-75;模块通过 RegisteFunc 在包初始化时注册
双字节码编译支持 BTF 启用(CO-RE)和非 BTF 内核构建系统生成 *_core.o*_noncore.o 变体;运行时通过 geteBPFName 选择 user/module/imodule.go:191-214
版本检测层处理 20+ 个具有不同结构布局的 OpenSSL/BoringSSL 版本detectOpenssl user/module/probe_openssl.go:178-278 解析 ELF .rodata,通过 sslVersionBpfMap 将版本映射到字节码
事件处理流程将捕获与输出格式化解耦,支持协议解析EventProcessor user/module/imodule.go:104 按 UUID 聚合事件,应用 HTTP/HTTP2 解析器
多种输出格式支持实时分析(文本)、取证(PCAP)、解密(密钥日志)TlsCaptureModelType 枚举 user/module/probe_openssl.go:58-76 控制捕获模式
连接跟踪在没有用户空间协作的情况下将网络数据包映射到进程Kprobes 填充 network_map LRU 哈希表;TC 钩子查找 PID/UID。参见网络连接跟踪
双 Worker 生命周期针对不同连接模式优化资源使用持久连接使用基于 Socket 的生命周期,短期连接使用默认(10-tick 超时)。参见事件处理流程

来源:user/module/imodule.go:47-75, user/module/probe_openssl.go:58-76, user/module/probe_openssl.go:178-278, user/module/imodule.go:191-214

数据流管道

以下图表显示了数据如何从应用程序流向输出:

图表:完整数据流

来源:user/module/imodule.go:285-391, user/module/imodule.go:409-448, cli/cmd/root.go:250-403

用户界面层

eCapture 提供三种用户交互界面:CLI 命令、HTTP 配置 API 和 eCaptureQ GUI 集成。

CLI 入口点

CLI 使用 Cobra 命令框架。每个子命令对应一个捕获模块。

图表:CLI 命令结构

来源:main.go:9-11, cli/cmd/root.go:80-153, cli/cmd/root.go:250-403, cli/cmd/root.go:156-175

持久化标志(应用于所有模块)cli/cmd/root.go:140-153

标志类型默认值用途
--pid / -puint640 (全部)目标特定进程 ID
--uid / -uuint640 (全部)目标特定用户 ID
--btf / -buint80 (自动)BTF 模式:0=自动,1=core,2=non-core
--mapsizeint1024每 CPU 的 eBPF map 大小(KB)
--logaddr / -lstring""日志目的地:文件路径、tcp://host:portws://host:port/path
--eventaddrstring""事件目的地(与日志分开)
--listenstringlocalhost:28256HTTP 配置服务器监听地址
--tsize / -tuint640文本模式下的截断大小(字节,0=不截断)
--ecaptureqstring""监听 eCaptureQ 客户端连接

HTTP 配置服务器

HTTP 服务器并发运行以接受运行时配置更新而无需重启。

图表:运行时配置更新

来源:cli/cmd/root.go:313-322, cli/cmd/root.go:368-396

HTTP 服务器支持动态重新配置。当收到带有更新配置 JSON 的 POST 请求时,系统会:

  1. 关闭当前模块(分离 eBPF 程序)
  2. 创建新的模块实例
  3. 使用更新的配置初始化
  4. 使用新设置重新启动事件捕获

有关配置结构详细信息,请参阅配置系统;有关 API 详细信息,请参阅 HTTP API 文档

输出目的地

eCapture 支持多个日志和事件的输出目的地:

图表:输出路由

来源:cli/cmd/root.go:178-247, cli/cmd/root.go:255-295

输出类型 cli/cmd/root.go:69-73

  • Stdout(类型 0):仅控制台输出
  • File(类型 1):写入本地文件,可通过 --eventroratesize--eventroratetime 选择性轮转
  • TCP(类型 2):流式传输到 tcp://host:port
  • WebSocket(类型 3):流式传输到 ws://host:port/pathwss://(TLS)

eventCollector 接收捕获的事件,而 logger 接收操作日志。它们可以通过 --logaddr--eventaddr 标志使用相同或不同的目的地。

捕获模块层

模块系统使用工厂模式进行动态模块实例化。每个模块都实现 IModule 接口并嵌入基础 Module 结构体以获得通用功能。

模块工厂和注册

模块在包初始化时自注册。

图表:模块工厂模式

来源:user/module/probe_openssl.go:777-786, cli/cmd/root.go:344-347

来自 OpenSSL 模块的注册示例 user/module/probe_openssl.go:777-786

go
func init() {
    RegisteFunc(NewOpenSSLProbe)
}

func NewOpenSSLProbe() IModule {
    mod := &MOpenSSLProbe{}
    mod.name = ModuleNameOpenssl
    mod.mType = ProbeTypeUprobe
    return mod
}

CLI 通过 module.GetModuleFunc(modName) cli/cmd/root.go:344 检索构造函数并调用它来创建实例。

IModule 接口

所有模块都实现 IModule 接口 user/module/imodule.go:47-75,该接口定义了生命周期和事件处理方法。

IModule 接口方法

方法用途阶段责任
Init(context.Context, *zerolog.Logger, config.IConfig, io.Writer)初始化模块,设置 EventProcessor,BTF 检测初始化基础 Module + 子类重写
Start()加载 eBPF 字节码,附加探针/钩子启动子类实现
Run()启动事件读取器,开始处理循环运行基础 Module(调用 child.Start)
Events() []*ebpf.Map返回要读取事件的 eBPF maps运行子类实现
Decode(*ebpf.Map, []byte) (event.IEventStruct, error)将原始事件字节解析为结构体事件处理基础委托给 child.DecodeFun
DecodeFun(*ebpf.Map) (event.IEventStruct, bool)返回特定 map 的解码器事件处理子类实现
Dispatcher(event.IEventStruct)路由事件(缓存、处理、输出)事件处理基础 + 子类都实现
Close()停止 eBPF 程序,清理资源关闭基础 + 子类都实现

有关详细的生命周期信息,请参阅模块系统与生命周期

来源:user/module/imodule.go:47-75, user/module/imodule.go:110-171

基础模块实现

Module 结构体 user/module/imodule.go:83-108 提供了所有探针通过嵌入继承的通用功能。

图表:模块结构体组成

来源:user/module/imodule.go:83-108, user/module/probe_openssl.go:83-106

基础模块职责 user/module/imodule.go:83-460

  1. BTF 检测autoDetectBTF() 检查 /sys/kernel/btf/vmlinux 和容器环境 user/module/imodule.go:173-190
  2. 字节码选择geteBPFName() 附加 _core.o/_noncore.o_less52.o 后缀 user/module/imodule.go:191-214
  3. 事件读取器perfEventReader()ringbufEventReader() 为每个 eBPF map 设置 goroutines user/module/imodule.go:308-391
  4. EventProcessor:使用截断大小和十六进制模式初始化 user/module/imodule.go:127
  5. 输出路由:检测 eventCollector 类型以选择文本或 protobuf 编码 user/module/imodule.go:122-126, user/module/imodule.go:461-479
  6. 生命周期管理:通过 Start()Run()Close() 协调子模块的生命周期 user/module/imodule.go:236-262

模块特定实现

每个探针模块都嵌入 Module 并添加模块特定的状态和逻辑。有关详细的实现信息,请参阅捕获模块

关键模块类型

模块用途目标库/二进制文件关键状态另请参阅
MOpenSSLProbeTLS 明文捕获libssl.so、libcrypto.so、BoringSSLsslVersionBpfMappidConnsmasterKeyseBPFProgramTypeOpenSSL 模块
MGoTLSProbeGo TLS 明文捕获Go 二进制文件(crypto/tls)isRegisterABItcPacketsChankeyloggerGo TLS 模块
MGnuTLSProbeGnuTLS 明文捕获libgnutls.sokeyloggermasterKeysGnuTLS 与 NSS 模块
MNSSProbeNSS/NSPR 明文捕获libnss3.so、libnspr4.so主密钥提取GnuTLS 与 NSS 模块
MBashProbeBash 命令审计bash 二进制文件通过 readline 钩子过滤命令Shell 命令审计
MZshProbeZsh 命令审计zsh 二进制文件通过 zle 钩子过滤命令Shell 命令审计
MMysqldProbeMySQL 查询审计mysqld 二进制文件funcName,从 dispatch_command 提取 SQL数据库查询审计
MPostgresProbePostgreSQL 查询审计postgres 二进制文件从 exec_simple_query 提取查询数据库查询审计

来源:user/module/probe_openssl.go:83-106

示例:MOpenSSLProbe 状态 user/module/probe_openssl.go:83-106

字段类型用途
pidConnsmap[uint32]map[uint32]ConnInfo映射 PID → FD → 连接元组和套接字 user/module/probe_openssl.go:91
sock2pidFdmap[uint64][2]uint32反向映射:套接字 → [PID, FD] 用于连接清理 user/module/probe_openssl.go:93
masterKeysmap[string]bool通过客户端随机数去重 TLS 主密钥 user/module/probe_openssl.go:98
sslVersionBpfMapmap[string]string将 SSL 版本字符串映射到字节码文件名 user/module/probe_openssl.go:101
eBPFProgramTypeTlsCaptureModelType确定捕获模式(Text/Pcap/Keylog)user/module/probe_openssl.go:99
keylogger*os.File密钥日志模式输出的文件句柄 user/module/probe_openssl.go:96
bpfManager*manager.ManagereBPF 程序生命周期管理器 user/module/probe_openssl.go:85

这些映射表启用了 SSL 数据事件(由 PID/FD 标识)与 TC 钩子捕获的网络元组之间的关联。有关 sslVersionBpfMap 的使用,请参阅版本检测与字节码选择;有关连接映射的详细信息,请参阅网络连接跟踪

eBPF 运行时层

eBPF 运行时层连接用户空间模块和内核空间检测。它通过 ebpfmanager 库处理版本检测、字节码选择和 eBPF 程序生命周期。

有关 eBPF 程序和钩子的综合详细信息,请参阅 eBPF 引擎。有关版本检测算法,请参阅版本检测与字节码选择

eBPF 运行时组件概述

图表:eBPF 运行时组件

来源:user/module/probe_openssl.go:178-278, user/module/imodule.go:191-214, user/module/probe_openssl.go:312-331, user/module/imodule.go:285-391

运行时层执行以下操作:

  1. 版本检测:确定目标库版本(参见版本检测与字节码选择
  2. 字节码选择:根据 BTF 可用性选择 CO-RE 或 non-CO-RE 字节码
  3. 资源加载:从 assets 包加载嵌入的字节码
  4. eBPF 验证:内核验证程序安全性
  5. 探针附加:附加 uprobes、TC 分类器、kprobes
  6. 事件读取:为 eBPF maps 设置读取器

BTF 检测与字节码选择

eCapture 编译每个 eBPF 程序的两个变体:CO-RE(启用 BTF,内核 >= 5.2)和 non-CO-RE(传统,所有内核)。运行时选择基于内核 BTF 支持。

图表:BTF 检测与字节码模式选择

来源:user/module/imodule.go:154-170, user/module/imodule.go:173-190, user/module/imodule.go:191-214

BTF 检测逻辑 user/module/imodule.go:173-190

  1. 检查是否在容器中运行(在容器中 BTF 检测可能不可靠)
  2. 查找 /sys/kernel/btf/vmlinux 文件以确认 BTF 支持
  3. 根据检测结果设置 m.isCoreUsed 标志

文件名转换示例 user/module/imodule.go:191-214

  • openssl_3_0_0_kern.oopenssl_3_0_0_kern_core.o(BTF 内核 >= 5.2)
  • openssl_3_0_0_kern.oopenssl_3_0_0_kern_noncore.o(非 BTF 内核 >= 5.2)
  • openssl_3_0_0_kern.oopenssl_3_0_0_kern_core_less52.o(BTF 内核 < 5.2)
  • openssl_3_0_0_kern.oopenssl_3_0_0_kern_noncore_less52.o(非 BTF 内核 < 5.2)

CO-RE 字节码使用 BTF 类型信息在加载时解析结构布局,实现一次编译 - 随处运行。non-CO-RE 字节码为特定内核版本硬编码偏移量。有关编译详细信息,请参阅构建系统

eBPF 程序生命周期

ebpfmanager.Manager user/module/probe_openssl.go:85 管理 eBPF 程序的加载、验证、附加和清理。

图表:eBPF 生命周期管理

来源:user/module/probe_openssl.go:280-357, user/module/imodule.go:236-262

生命周期阶段

  1. 设置Start() 调用模式特定的设置(setupManagersTextsetupManagersPcapsetupManagersKeylog
  2. 字节码加载assets.Asset(bpfFileName) 检索嵌入的字节码 user/module/probe_openssl.go:312-317
  3. 初始化bpfManager.InitWithOptions() 加载字节码,内核验证程序 user/module/probe_openssl.go:320-326
  4. 附加bpfManager.Start() 附加 uprobes/TC/kprobes user/module/probe_openssl.go:329-331
  5. Map 注册initDecodeFun*() 填充 eventMapseventFuncMaps user/module/probe_openssl.go:333-348
  6. 运行:基础 Module.Run() 生成事件读取器和 EventProcessor user/module/imodule.go:236-262
  7. 关闭bpfManager.Stop(manager.CleanAll) 分离并清理 user/module/probe_openssl.go:352-357

通过常量编辑器注入配置

eBPF 程序定义在加载时重写的常量变量以注入运行时配置(PID、UID 过滤器)。

常量编辑器机制

常量名称用途类型值来源效果
target_pid按进程 ID 过滤uint64conf.GetPid()0 = 捕获所有 PID,非零 = 仅特定 PID
target_uid按用户 ID 过滤uint64conf.GetUid()0 = 捕获所有 UID,非零 = 仅特定 UID

来源:user/module/probe_openssl.go:361-387

constantEditor() 方法 user/module/probe_openssl.go:361-387 返回一个 manager.ConstantEditor 结构体切片。eBPF 管理器在加载到内核之前重写字节码中的这些常量。这使得无需重新编译 eBPF 程序即可实现参数化过滤。

对于内核 < 5.2,全局变量支持受限。EnableGlobalVar() 检查 user/config/iconfig.go:194-203 返回 false,禁用某些功能。

事件处理层

在 eBPF 程序捕获事件并通过 perf 数组或环形缓冲区传输后,用户空间事件处理流程会聚合、解析并格式化它们以供输出。

有关全面的事件处理详细信息,请参阅事件处理流程

架构设计 has loaded