ctrace实现了一个基于eBPF的容器跟踪工具和权限最小集分配工具,用于帮助用户跟踪容器行为、防范恶意攻击。它可以跟踪已存在和新开启的容器,收集各类信息并打印或以json文件的形式储存。同时它可以根据容器的行为通过linux系统的seccomp功能自动为容器分配权限最小集。ctrace的主要功能如下:
- 采集数据:使用Linux系统的eBPF技术对容器进行筛选跟踪与数据采集。
- 数据存储:将采集获得的数据按一定格式存储到json文件中。
- 权限最小集分配:使用seccomp技术根据json文件中跟踪到的系统调用来给容器分配权限。
-
实现353个系统调用+3个rawTracepoint+vfs kprobe
-
ctrace并没有采用相对比较成熟的BCC开发模式,而是使用libbpfgo框架进行开发,从而实现了CORE特性,摆脱了对内核版本的依赖(但仍需5.10以上的内核版本),使得eBPF程序的开发可以更加专注于功能本身而不是某个结构/函数是否随内核版本发生了改变。go语言用于用户程序开发也更易与当今云原生主流应用对接。
时间 | 任务 | 达成情况 |
---|---|---|
2022/4/20 - 2022/5/2 | 学习eBPF相关知识,确定项目实现方式 | 完成 |
2022/5/3 - 2022/5/4 | 搭建项目开发环境 | 完成 |
2022/5/5 - 2022/5/8 | 设计编写程序基础框架 | 完成 |
2022/5/9 - 2022/5/13 | 实现指定跟踪容器功能 | 完成 |
2022/5/14 - 2022/5/15 | 更换项目实现方式为libbpf-go | 完成 |
2022/5/16 - 2022/5/21 | 实现跟踪容器系统调用功能 | 完成 |
2022/5/22 - 2022/5/26 | 实现跟踪容器文件访问功能 | 完成 |
2022/5/27 - 2022/5/28 | 实现记录跟踪数据存储功能 | 完成 |
2022/5/29 - 2022/6/4 | 过程文档编写完善 | 进行 |
2022/6/3 - 2022/6/15 | 实现跟踪容器间互访功能 | 进行 |
2022/6/16 - 2022/6/20 | 实现使用seccomp自动读取数据分配权限最小集功能 | 待办 |
- make
- clang-12
- libbpf
- linux-tools-common
- go 1.14+
- kernel version:5.10+
make build
- 运行ctrace,监控容器行为
# 是否跟踪容器内openat系统调用
sudo ./dist/ctrace trace --event openat
sudo ./dist/ctrace trace --exclude-event openat
# 是否跟踪容器内文件系统、网络、进程相关事件
sudo ./dist/ctrace trace --set fs/net/proc
sudo ./dist/ctrace trace --exclude-set fs/net/proc
# 是否过滤掉容器内进程名为comm的相关事件
sudo ./dist/ctrace trace --comm bash
sudo ./dist/ctrace trace --exclude-comm bash
# 跟踪容器所有系统调用,不跟踪文件系统相关事件
sudo ./dist/ctrace trace --event sys_enter --exclude-set fs
# 跟踪容器内文件系统相关事件,过滤掉read事件
sudo ./dist/ctrace trace --set fs --exclude-event read
# 跟踪容器所有系统调用,不跟踪文件系统相关事件,只跟踪bash进程
sudo ./dist/ctrace trace --exclude-set fs --comm bash
# 实现docker ps,列出正在运行的容器
sudo ./dist/ctrace trace ls -c
- config配置输出
# 设置输出格式为json或table
sudo ./dist/ctrace config --set output-format=json
# 设置输出文件路径,不设置则输出到终端屏幕
sudo ./dist/ctrace config --set events-path=~/ctrace_output/events.json
# 设置错误事件输出文件路径
sudo ./dist/ctrace config --set errors-path=~/ctrace_output/error.json
json输出如下:
{
"timestamp": 29197306126, //时间戳
"processId": 14891, //进程ID
"threadId": 14891, //线程ID
"parentProcessId": 14876, //父进程ID
"userId": 0, //用户ID
"mountNamespace": 4026532760,
"pidNamespace": 4026532763,
"processName": "cat",
"hostName": "f1d4bd003923", //容器ID
"eventId": "257", //事件ID
"eventName": "openat", //事件名称
"argsNum": 4, //参数个数
"returnValue": 3,
"args": [
{
"name": "dirfd",
"type": "int",
"value": -100
},
{
"name": "pathname", //打开的文件名
"type": "const char*",
"value": "hello_ctrace"
},
{
"name": "flags",
"type": "unsigned int",
"value": "O_RDONLY"
},
{
"name": "mode",
"type": "mode_t",
"value": 0
}
]
}
目录结构说明:
.
├── command
│ ├── common.go //global flags/action
│ ├── config.go //cli config命令
│ └── trace.go //trace命令
├── config
│ └── config.go //读写配置文件
├── ctrace
│ ├── argprinters.go //参数打印
│ ├── bpf //bpf.c
│ │ ├── common.bpf.h
│ │ ├── ctrace.bpf.c
│ │ ├── ctrace.bpf.h
│ │ ├── missing_defines.h
│ │ └── vmlinux.h
│ ├── consts.go //枚举事件,参数类型
│ ├── container.go //容器相关实现
│ ├── ctrace.go //ctrace主体功能
│ ├── events_amd64.go //枚举事件常量定义
│ ├── external.go //事件定义与json输出
│ └── printer.go //事件打印
├── conf.yaml //ctrace配置文件
├── go.mod
├── go.sum
├── main.go
├── Makefile
通过阅读eBPF相关文章、学习类似项目经验,我们决定使用libbpf-go作为ctrace的实现方式。首先编写eBPF程序跟踪记录内核中各个进程进行的系统调用和文件访问等行为,分别对其编写函数并进行相应处理。随后根据cgroup id将容器进程挑选出来,再通过eBPF Map将采集到的信息传递给用户空间程序进一步的处理。用户程序则负责格式化打印储存event信息、跟踪选项设置、容器权限配置等功能的实现。程序架构如下所示:
企业导师:程泽睿志(华为)
学校导师:吴松(华中科技大学)
成员:洪涛、郭永强、吴浩