第 2
篇 钉了 exec_id 与
execve_map;本篇钉 Sensor/Hook
轴。读者常见误区有两个:一是以为写一条
TracingPolicy 就覆盖了进程生命周期;二是把 kprobe
系统调用入口当成「已经看到内核最终用的那个对象」。真正要钉死的是:__base__
传感器负责 exec/clone/exit 与 map;TracingPolicy 按 hook
类型另挂程序;选错类型会表现为无事件、错参数或踩上
TOCTOU,而不是 YAML 没 apply。
本篇在系列中的位置
篇目 核心内容 第 2 篇 · 进程模型 exec_id与execve_map第 3 篇 · Sensor 与 Hook 内置进程传感器;kprobe / tracepoint / uprobe / LSM / fentry 第 4 篇 · 事件导出路径 JSON / gRPC 分流 系列目录 全部篇目
版本锚定:Tetragon v1.7.0(
docs/.../concepts/tracing-policy/hooks.md;bpf/process/bpf_generic_*.c;pkg/sensors/base/;CRDtracing_policy_types.go)。hooks.md 正文以 kprobe / tracepoint / uprobe / USDT / LSM 为主;spec.fentries以 CRD 与bpf_generic_fentry.c为准,不要用 live 文档补一篇本 tag 没有的 fentry 教程。Verifier 算法外链 ebpf/,本篇不重写。无真实节点则不粘贴伪造 hook 事件。
一、base / process sensor
进程生命周期不是 TracingPolicy
的默认附件。v1.7.0 用名为 __base__ 的 sensor
加载一组固定程序(pkg/sensors/handler.go 的
BaseSensorName;pkg/sensors/base/base.go
的 GetInitialSensor)。Linux 默认程序列表在
base_linux.go:Exit、Fork、Execve、ExecveBprmCommit、ExecveMapUpdate。
// project: cilium/tetragon, tag: v1.7.0
// path: pkg/sensors/base/base.go
// function: package-level Builders (excerpt)
Execve = program.Builder(
config.ExecObj(),
"sched/sched_process_exec",
"tracepoint/sys_execve",
"event_execve",
"execve",
).SetPolicy(sensors.BaseSensorName)
Fork = program.Builder(
config.ForkObj(),
"wake_up_new_task",
"kprobe/wake_up_new_task",
"kprobe_pid_clear",
"kprobe",
).SetPolicy(sensors.BaseSensorName)| 程序 | 用户态 Builder 锚点 | BPF 入口 | 职责 |
|---|---|---|---|
| Execve | attach sched/sched_process_exec,类型
tracepoint/sys_execve,event_execve |
bpf/process/bpf_execve_event.c |
填 EVENT_EXECVE,更新
execve_map,输出执行事件 |
| Fork | kprobe/wake_up_new_task |
bpf/process/bpf_fork.c |
每线程组一次 clone;父不在 map 则早退 |
| Exit | 优先 kprobe/acct_process,否则
disassociate_ctty |
bpf/process/bpf_exit.c |
线程组最后任务上发送 exit |
| ExecveBprmCommit | kprobe/security_bprm_committing_creds |
bpf_execve_bprm_commit_creds.o |
在凭据提交点记录 secureexec / inode 信息,供 execve 拼接 |
| ExecveMapUpdate | socket 程序 | execve_map_update |
大程序内核上更新 match-binaries 集合 |
共享 map 包括
execve_map、tg_conf_map、统计
map,以及内核 ≥ 某代对象文件时的 tg_rb_events
ring(base_linux.go 把 ringbuf 门闩放在 v5.11
对象集,并可用 perf ring 回退)。用户态
pkg/sensors/exec/exec_linux.go 把
MSG_OP_EXECVE / EXIT /
CLONE 登记到 observer。
TracingPolicy 通过 registeredPolicyHandlers
另造 sensor 集合,加载进同一 collection map,但
不会替代
__base__。__base__
在卸载时最后销毁。失败表象:策略事件偶发出现,但没有任何
process_exec——先查 base sensor 是否加载、BTF
是否足够跑 execve 对象,而不是先改 kprobe YAML。
v1.7.0 Tracing Policy
索引页的导语只写「kprobes、tracepoints、uprobes」。同 tag 的
CRD 还包含
lsmhooks、usdts、fentries。以
CRD 为准;导语不是能力闭包。
二、kprobe / kretprobe
Kprobe 在任意内核函数入口执行
BPF。Tetragon 用 spec.kprobes 描述,对应
bpf/process/bpf_generic_kprobe.c。入口
generic_kprobe_event 先跑进程过滤再 setup /
读参 / 选择器 / 动作 / 输出。文件头注释给出伪代码顺序:pid →
namespace → capability → 拷贝参数 → selector →
ring。老内核靠 tail call 把指令数压到 4k 限制以下。
// project: cilium/tetragon, tag: v1.7.0
// path: bpf/process/bpf_generic_kprobe.c
// function: generic_kprobe_event
__attribute__((section((MAIN)), used)) int
generic_kprobe_event(struct pt_regs *ctx)
{
return generic_start_process_filter(ctx, (struct bpf_map_def *)&kprobe_calls);
}kprobe_calls 是
BPF_MAP_TYPE_PROG_ARRAY:TAIL_CALL_SETUP
/ PROCESS / FILTER /
ARGS / ACTIONS /
SEND,小程序内核再拆 PROCESS_2 /
ARGS_2。排障「策略已加载但 CPU
高、无事件」时,先问过滤是否在
generic_process_filter 就
PFILTER_REJECT,而不是先怀疑 ring 满——后者是第
11 篇。
syscall: true 告诉 Tetragon 按系统调用 ABI
读参,而不是普通 C 函数 ABI。文档
Caution:写可移植策略时用无架构前缀的
sys_write,由 agent 补 __x64_ /
__arm64_;输出事件里仍可能带前缀。/proc/kallsyms
用来核对本机真实符号;符号随内核版本变化,这是 kprobe 相对
tracepoint 的主要代价。
返回值与「入口处尚未填满的缓冲区」走
kretprobe:return: true 加
returnArg,char_buf 上的
returnCopy
表示到返回时再读。bpf_generic_retkprobe.c
是对应对象。sizeArgIndex 文档写明当前实现按「第
n 个参数」计数(第三个参数写 3 而不是 index
2),属于已知不一致。
同一文件里还有 generic_kprobe_override:从
override_tasks 取出错误码并
override_return。这是强制执行轴的挂载点,本篇只登记「Override
长在 kprobe 上」;保证与
CONFIG_BPF_KPROBE_OVERRIDE 见第 10 篇。v1.7.0
genericfentry.go 写明 fentry 尚不支持
enforcement。
hooks.md 开篇 Warning:hook
系统调用且参数是用户态指针时存在 TOCTOU——用户态可在
hook
之后、内核消费之前改内存。要看内核已拷贝的对象,应改挂稍后的函数或
LSM security_*。这是 Garfinkel(NDSS
2003)在工程文档里的直接对应,不是产品修辞。
文档示例(配置片段,非本机加载结果):
spec:
kprobes:
- call: "fd_install"
syscall: false
args:
- index: 0
type: "int"
label: "FD"
- index: 1
type: "file"args 的 type 告诉 BPF
如何读参。char_buf / char_iovec
可加 returnCopy(到 kretprobe 再读)与
sizeArgIndex。hooks.md
Caution:sizeArgIndex 当前按「第 n
个参数」计数(第三个参数写 3 而不是 index 2)。默认
char_buf 最多 4096
字节;maxData: true 在文档所述内核 ≥5.4 上可到
327360 字节,且不能与
returnCopy 同用。resolve
从内核结构体解字段:kprobe ≥5.4,LSM ≥5.7,uprobe
不支持。
spec.lists 可以把一组符号折叠进
call: "list:NAME"。type: syscalls
的列表不能与普通函数混用;generated_syscalls /
generated_ftrace 会在加载时展开。列表共享同一套
args 与 selector——签名不同的 open
/ openat 不能靠 list 共用
matchArgs index,文档改走
selectorsMacros。
选错 kprobe 的失败表象:符号带错前缀导致加载失败;hook
sys_open 却去读内核
struct file(那是 fd_install / LSM
file_open 的世界);returnCopy
没开导致 read 缓冲区为空;list 里混了 syscall
与非 syscall。
三、tracepoint / rawtp
Tracepoint 是内核静态埋点,ABI 比 kprobe
稳定。策略写 spec.tracepoints,指定
subsystem 与 event。可用点在
/sys/kernel/tracing/events;参数布局读该事件的
format 文件。文档示例挂
raw_syscalls/sys_enter 并用
index: 4 取 syscall ID。BPF 对象为
bpf_generic_tracepoint.c。
Raw
tracepoint(raw: true)仍 attach
同一类事件,但参数是 TRACE_EVENT
的原始原型,而不是 perf 格式化后的字段。文档口径:不经过中间
perf 层,应比标准 tracepoint
更快。示例:sched/sched_process_exec 上
raw: true 后,index: 2 类型
linux_binprm
可直接拿出路径。Resolve 可从
task_struct 解字段(如
pid)。对象文件
bpf_generic_rawtp.c。
__base__ 的 exec 传感器自己就挂在
sched/sched_process_exec。再写一条相同
tracepoint 的 TracingPolicy 会多一份事件,不是「让 base
更准」。syscall 跟踪优先 raw_syscalls /
syscalls 子系统,而不是为每个
sys_* 发明不稳定 kprobe 名——除非你需要 kprobe
才有的 Override。
失败表象:format 里的 index 与 raw 原型
index 混用;把 rawtp
的结构体参数当成已经格式化的字符串。
四、uprobe / USDT
Uprobe
挂用户态函数:spec.uprobes
提供二进制/库 path 与
symbols。符号随二进制版本变化,跨发行版不可移植。文档用
nm -D /bin/bash 确认 readline 在
text 段。选择器与 kprobe
同一套(matchArgs、matchReturnArgs、matchBinaries
等)。返回值要
return: true。对象:bpf_generic_uprobe.c、bpf_generic_retuprobe.c。
hooks.md 写明:uprobe 不支持内核结构体
resolve(那是 kprobe/LSM 的 BTF 遍历)。
USDT(User Statically-Defined Tracing)读 ELF
stapsdt note。spec.usdts 用
path + provider +
name。每个 probe 最多 5 个参数(Tetragon
限制)。tetra tracingpolicy generate usdts --binary ...
可从二进制生成策略骨架。对象:bpf_generic_usdt.c。
失败表象:path 指向容器内路径而 agent 看到的是主机
mount;符号只在静态二进制里、nm -D 看不到;USDT
semaphore 未启用导致 probe 静默。无事件时先用
readelf -n 确认 note,再查 TracingPolicy
是否加载(第 5–6 篇)。
五、LSM 与 fentry(v1.7.0)
LSM BPF
spec.lsmhooks 挂 LSM 钩子,文档指向
security/security.c 中的 hook
名。前置条件(hooks.md):
- 内核
CONFIG_BPF_LSM=y。 /sys/kernel/security/lsm列表包含bpf;否则需把bpf加入 LSM 启动参数后重启。
示例策略 hook file_open,参数类型
file,再 matchBinaries +
matchArgs 限制 /usr/bin/cat 打开
/etc/passwd。对象:bpf_generic_lsm_core.c。
LSM 相对 syscall kprobe
的机制收益:参数已是内核对象,Warning 中的用户态指针 TOCTOU
被推到更早的拷贝完成之后。这不是「LSM
消灭竞态」,而是把检查点移到内核驻留状态。属性
resolve 对 LSM 要求内核 ≥ 5.7(hooks.md
Caution);kprobe 的 resolve 从 5.4 起。
Tetragon 在 LSM hook 上跑 eBPF 程序,不是自己实现一套 LSM 框架。学术前身是 Wright et al.(USENIX Security 2002)的 LSM 接口;本篇只把 Tetragon 定位为该接口上的 BPF 客户。展开见第 5–8 篇的策略加载与身份过滤。
fentry
CRD TracingPolicySpec.Fentries 类型为
[]KProbeSpec,JSON 键
fentries:
// project: cilium/tetragon, tag: v1.7.0
// path: pkg/k8s/apis/cilium.io/v1alpha1/tracing_policy_types.go
// struct: TracingPolicySpec
// A list of fentry specs.
Fentries []KProbeSpec `json:"fentries,omitempty"`加载路径:pkg/sensors/tracing/genericfentry.go
注册 probe 类型 generic_fentry,并调用与 kprobe
共享的 createGenericKprobeSensor。若选择器含
enforcement 动作,加载在用户态失败:
// project: cilium/tetragon, tag: v1.7.0
// path: pkg/sensors/tracing/genericfentry.go
// function: createGenericFentrySensor
for _, fentry := range spec.Fentries {
if selectors.HasEnforcementAction(fentry.Selectors) {
return nil, errors.New("enforcement is not supported for fentry yet")
}
}BPF 入口:
// project: cilium/tetragon, tag: v1.7.0
// path: bpf/process/bpf_generic_fentry.c
// function: generic_fentry_event
__attribute__((section((SECTION_ENTRY)), used)) int
generic_fentry_event(struct pt_regs *ctx)
{
return generic_start_process_filter(ctx, (struct bpf_map_def *)&fentry_calls);
}section 为 fentry/generic_fentry。tail call
链与 generic kprobe 同构。输出 opcode 仍是
MSG_OP_GENERIC_KPROBE;用户态信封以 observer
对该 opcode 的映射为准,不要假设存在独立的
PROCESS_FENTRY 枚举(v1.7.0
events.proto 的 EventType
无此值)。
同目录存在 bpf_generic_fexit.c,作为 fentry
族返回路径对象;CRD 没有单独的 fexits
字段。hooks.md @ v1.7.0 没有
fentry 专节。能力陈述以 CRD + 上述源码为准,不以 live
站点的后补教程为准。fentry 依赖 BTF 与 tracing
类程序;加载失败时表象与「写了 kprobe
但内核没有该符号」类似,应查 agent 日志与 BTF,而不是先改
matchArgs。Trampoline / 验证器细节见 ebpf/。
六、hook
类型对照,以及与 ebpf/ 的分工
| Hook 类型 | TracingPolicy 字段 | 能观察什么 | 内核 / 配置依赖 | 选错时的典型失败 |
|---|---|---|---|---|
| 内置 process sensor | 无(__base__) |
exec / clone / exit、维护 execve_map |
BTF/CO-RE 对象;exit 符号 acct_process 或
disassociate_ctty |
无 process_exec;fork 父缺失 |
| kprobe | kprobes |
任意内核函数入口参数 | 符号存在;syscall 需正确 ABI | 无事件;参数全是错的 |
| kretprobe | kprobes + return |
返回值、返回时才填充的缓冲 | 与 kprobe 相同 | 空 buffer;sizeArgIndex 数错 |
| tracepoint | tracepoints |
稳定跟踪点的格式化字段 | tracefs 上存在该 event | format index 错 |
| raw tracepoint | tracepoints + raw: true |
TP_PROTO 原始参数 |
同上;文档称绕过 perf 层 | 与 format index 混用 |
| uprobe | uprobes |
用户态函数参数/返回值 | 二进制 path+符号匹配 | 路径/符号版本不对 |
| USDT | usdts |
应用静态探针,最多 5 参数 | ELF stapsdt | 无 note 或 semaphore |
| LSM | lsmhooks |
内核安全对象(file/path/cred) | CONFIG_BPF_LSM 且 lsm 列表含
bpf |
加载被拒;无 bpf LSM |
| fentry | fentries |
带 BTF 的函数入口(策略形状同 kprobe spec) | BTF、tracing;无 enforcement | 加载失败;误以为能 Override |
flowchart TD
base["__base__ exec fork exit"] --> map["execve_map"]
tp["TracingPolicy"] --> kprobe["kprobe / kretprobe"]
tp --> trace["tracepoint / rawtp"]
tp --> uprobe["uprobe / USDT"]
tp --> lsm["LSM hooks"]
tp --> fentry["fentries"]
map --> kprobe
map --> trace
map --> uprobe
map --> lsm
map --> fentry
图:动态 hook 读取 __base__
维护的进程状态;它们不负责把进程第一次写进 map。
与 ebpf/ 的分工:该系列写 verifier 抽象解释、JIT、Map 实现、fentry trampoline、BTF/CO-RE。本篇只写 Tetragon 把哪些程序类型接到 TracingPolicy 字段上,以及选错时的产品失败表象。不要在本篇寻找「为什么这段 BPF 被 verifier 拒绝」的算法答案。
谱系:McCanne & Jacobson(USENIX
Winter 1993)把过滤放进内核 VM;eBPF 把 VM 接到
kprobe/tracepoint/LSM。Garfinkel(NDSS 2003)给出 syscall
拦截的 TOCTOU 与覆盖不全;hooks.md 的 Warning
是该争论的文档化版本。Höiland-Jørgensen et al.(CoNEXT
2018)的 XDP 早路径不解释 file_open 或
wake_up_new_task——那是数据面对照。
争论:CR 便利 vs 低层 hook 误配。YAML
让策略可声明,但 call
字符串仍是内核符号。无事件时,社区容易把它解释成「eBPF
不可靠」;v1.7.0 源码把更多失败推到「符号/ABI/LSM
列表/BTF」这些可核对条件上。另一侧是用户态规则引擎:可移植、不绑符号,但把
TOCTOU 窗口留在用户态(第 14 篇对照 Falco)。
开放问题:标签与容器选择器如何限制哪些 cgroup 真正 attach(第 8 篇);fentry 上的 enforcement 何时进入稳定语义(v1.7.0 明确未支持)。
七、小结
__base__维护进程生命周期与execve_map;TracingPolicy 只增加动态 hook。- kprobe 能力最宽、可移植性最差,且 syscall 指针参数有 TOCTOU。
- tracepoint / rawtp 用稳定埋点换原始或格式化参数;raw 更快、index 语义不同。
- uprobe / USDT 观察用户态;USDT 有 5 参数上限。
- LSM 需要
CONFIG_BPF_LSM与bpf在 lsm 列表;fentry 在 v1.7.0 经spec.fentries提供,且不能做 enforcement。下一轴是导出:hook 产生的事件如何在 JSON 与 gRPC 分成两套可见集合。
八、参考资料
规范 / 官方文档(A)
- Tetragon v1.7.0 Hook points —
docs/content/en/docs/concepts/tracing-policy/hooks.md@ v1.7.0(TOCTOU Warning、kprobe ABI、rawtp、uprobe、USDT、LSM、resolve内核版本)。 - Tetragon v1.7.0 Tracing Policy —
docs/content/en/docs/concepts/tracing-policy/_index.md@ v1.7.0(导语能力列表不完整,需与 CRD 对照)。
源码(A)
pkg/sensors/base/base.go、base_linux.go(__base__程序与 map)@ v1.7.0pkg/sensors/handler.goBaseSensorName;pkg/sensors/exec/exec_linux.goAddExec@ v1.7.0pkg/k8s/apis/cilium.io/v1alpha1/tracing_policy_types.goTracingPolicySpec(含fentries)@ v1.7.0pkg/sensors/tracing/genericfentry.gocreateGenericFentrySensor@ v1.7.0bpf/process/bpf_generic_{kprobe,retkprobe,tracepoint,rawtp,uprobe,retuprobe,usdt,lsm_core,fentry,fexit}.c@ v1.7.0
核心论文
- McCanne & Jacobson, The BSD Packet Filter, USENIX Winter 1993。
- Garfinkel, Traps and Pitfalls, NDSS 2003 —— syscall 拦截 TOCTOU,与 hooks.md Warning 对照。
- Höiland-Jørgensen et al., The eXpress Data Path, CoNEXT 2018 —— 数据面对照。
实验 / 工具
- 本篇给出文档中的命令语义(
grep /proc/kallsyms、ls /sys/kernel/tracing/events、cat /sys/kernel/security/lsm),不粘贴本机输出。未跑 TracingPolicy 加载。
站内对照
→ 上一篇:进程模型 · 系列目录 · 下一篇:事件导出路径
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Tetragon / eBPF】TracingPolicy:CRD、三条加载路径与 collectionKey
钉清 TracingPolicy 与 TracingPolicyNamespaced 的差异;给出 v1.7.0 CRD 字段总表(含 fentries);说明 kubectl、gRPC、静态文件如何共用 collectionKey;并写明 Enable/Disable gRPC 在默认配置下返回错误。
【Tetragon / eBPF】运行时安全全景:缺口、五轴坐标系与 16 篇路线
相对 Cilium 数据面、可观测对照与 Falco 口号,钉清 Tetragon v1.7.0 运行时安全内核缺口;定义进程血缘、Sensor/Hook、策略加载、过滤分层、强制执行五条坐标系,给出 16 篇阅读路线。
【Tetragon / eBPF】Agent 生命周期:BTF、安装形态与三条策略加载路径
钉清 Tetragon v1.7.0 agent 从 BTF/CO-RE 发现到 sensor 挂载的失败表象;对照 DaemonSet 与主机安装;说明默认 gRPC Unix socket 与 kubectl/gRPC/静态文件三条路径如何进入同一 collection map。
【Tetragon / eBPF】文件、网络与能力观测面:路径解析、socket 与 cred 的失败模式
把 Tetragon v1.7.0 的文件、网络、能力观测收成三类 hook 模式:d_path 与参数类型如何解析路径、sock/sockaddr 看见什么、cred/capset 记录什么;并钉死路径截断、单挂载点、硬链接与 6.2+ truncate 符号消失等失败表象。