项目仓库:https://github.com/yunwei37/Eunomia
4.1. 系统设计
系统架构
关于详细的系统架构设计和模块划分,请参考 系统设计文档
4.2. 模块设计
-
tracker_manager
负责启动和停止 ebpf 探针,并且和 ebpf 探针通信(每个 tracer 是一个线程);
- start tracker
- stop tracker(remove tracker)
我们主要有五个ebpf探针:
- process
- syscall
- tcp
- files
- ipc
-
container_manager
负责观察 container 的启动和停止,保存每个 container 的相关信息:(cgroup,namespace),同时负责 container id, container name 等 container mata 信息到 pid 的转换(提供查询接口)
-
seccomp_manager
负责对 process 进行 seccomp 限制
-
handler/data collector
负责处理 ebpf 探针上报的事件
-
security analyzer
容器安全检测规则引擎和安全分析模块,通过ebpf采集到的底层相关数据,运用包括AI在内的多种方法进行安全性分析,可以帮助您检测事件流中的可疑行为模式。
-
prometheus exporter
将数据导出成Prometheus需要的格式,在Prometheus中保存时序数据,方便后续持久化和可视化功能。
-
config loader
解析 toml
-
cmd
命令行解析模块,将命令行字符串解析成对应的参数选项,对Eunomia进行配置。
-
core
负责装配所需要的 tracker,配置对应的功能用例,并且启动系统。
-
server
http 通信:通过
graphql
在远程发起 http 请求并执行监控工具,将产生的数据进行聚合后返回,用户可自定义运行时扩展插件进行在线数据分析。这一个部分还没有完成。
4.3. ebpf 主要观测点
-
process追踪模块
进程的追踪模块本项目主要设置了两个
tracepoint
挂载点。
第一个挂载点形式为SEC("tp/sched/sched_process_exec") int handle_exec(struct trace_event_raw_sched_process_exec *ctx) { }
当进程被执行时,该函数会被调用,函数体中会从传入的上下文内容提取内容,我们需要的信息记录在Map中。
第二个挂载点形式为SEC("tp/sched/sched_process_exit") int handle_exit(struct trace_event_raw_sched_process_template *ctx) { }
当有进程退出时,该函数会被调用,函数体同样会从传入的上下文内容提取内容,我们需要的信息记录在Map中。
-
syscall追踪模块
对于系统调用的追踪模块设置了一个
tracepoint
挂载点。挂载点形式为SEC("tracepoint/raw_syscalls/sys_enter") int sys_enter(struct trace_event_raw_sys_enter *args) { }
当有syscall发生时,其经过
sys_enter
执行点时我们的函数将会被调用,将相关信息存入map后供用户态读取。 -
file追踪模块
对于文件系统,我们设置了两个
kprobe
挂载点。第一个挂载点形式为SEC("kprobe/vfs_read") int BPF_KPROBE(vfs_read_entry, struct file *file, char *buf, size_t count, loff_t *pos) { }
第二个挂载点形式为
SEC("kprobe/vfs_write") int BPF_KPROBE(vfs_write_entry, struct file *file, const char *buf, size_t count, loff_t *pos) { }
当系统中发生了文件读或写时,这两个执行点下的函数会被触发,记录相应信息。
-
tcp追踪模块
SEC("kprobe/tcp_v6_connect") int BPF_KPROBE(tcp_v6_connect, struct sock *sk) { return enter_tcp_connect(ctx, sk); } SEC("kretprobe/tcp_v6_connect") int BPF_KRETPROBE(tcp_v6_connect_ret, int ret) { return exit_tcp_connect(ctx, ret, 6); }
4.4. ebpf 探针设计
采用 ebpf 探针的方式,可以获取到安全事件的相关信息,并且可以通过 prometheus 监控指标进行监控和分析。
我们的探针代码分为两个部分,其一是在 bpftools
中,是针对相关 ebpf 程序的 libbpf 具体探针接口实现,负责1ebpf 程序的加载、配置、以及相关用户态和内核态通信的代码;另外一部分是在 src 中,针对 ebpf 探针上报的信息进行具体处理的 C++ 类实现,负责根据配置决定ebpf上报的信息将会被如何处理。
4.4.1. ebpf 探针相关 C 代码设计,以 process 为例:
process 部分的代码主要负责获取进程的执行和退出时和进程相关的以下的信息:
- pid
- cgroup
- namespace:user pid mount
- ppid
- command
- 可执行文件路径
其中容器相关信息会保存起来并被其他 tracker 用以查询。
ebpf 代码:在 bpftools\process\process.bpf.c 中,这里贴出来的代码经过了一定程度的化简。
static __always_inline void fill_event_basic(pid_t pid, struct task_struct *task, struct process_event *e)
{
e->common.pid = pid;
e->common.ppid = BPF_CORE_READ(task, real_parent, tgid);
e->common