「策略已经 apply,为什么 tetra getevents
仍是空的?」与「denylist 已经滤掉,为什么 CLI
还在刷事件?」很少落在同一层。前者可能是 hook 没挂上、BTF
找不到、或三条加载路径撞名;后者在 v1.7.0 文档里已经写成
Caution:JSON allow/deny 不作用于
gRPC。若一上来重装 DaemonSet 或改随机 TracingPolicy
YAML,因果链会被冲掉,还可能把可恢复的加载失败放大成双故障。
本文是系列第 13
篇:按五轴映射症状,并把每一步钉到
bugtool / metrics / 加载键 / 内核
config。口诀与第 01
篇相同:先点名轴,再下钻模块。观测字段与
metrics 更名见第 12
篇;强制执行语义见第 10
篇。无 Tetragon 节点则不粘贴伪造 tetra /
JSON 输出。
本篇在系列中的位置
篇目 核心内容 第 12 篇 · 运维与导出工程 metrics、轮转、SIEM 与 gRPC 地址 第 13 篇 · 排障坐标系 按五轴归因 第 14 篇 · 对照替代路径 Falco / auditd / CNP·Hubble / seccomp 系列目录 全部篇目
版本锚定:Tetragon v1.7.0(源码 tag v1.7.0,提交
1de2ed8「Prepare for v1.7.0 release」)。加载键是collectionKey{name, namespace}(pkg/sensors/collection.go);不是 live 文档里的 k8s/grpc/static domain sharding。官方 Troubleshooting / System dump 为 bugtool 入口;机制结论以 tag 下docs/content/en/docs/与pkg/、bpf/为准。
一、先点名轴,再动 YAML
轴一:进程血缘 — exec_id / execve_map / process cache / procFS 回填
轴二:Sensor/Hook — 内置 process sensor;kprobe / tracepoint / uprobe / LSM / fentry
轴三:策略加载 — kubectl CR、tetra gRPC add、--tracing-policy 文件,同一 collection map
轴四:过滤分层 — 内核 selector vs JSON export allow/deny vs field/redaction
轴五:强制执行 — Override(需 CONFIG_BPF_KPROBE_OVERRIDE)vs Signal/SIGKILL
每轴固定三列:症状 → 先查什么 → 不要先做什么。与 Cilium 排障 同一纪律:一次否证一轴。
flowchart TD
symptom["Symptom"]
symptom --> axis["Name the axis first"]
axis --> a1["Lineage execve_map cache"]
axis --> a2["Hook Sensor BTF"]
axis --> a3["Load path collectionKey"]
axis --> a4["Filter JSON vs gRPC"]
axis --> a5["Enforcement Override Signal"]
| 轴 | 触发选轴的症状 | 先查 | 不要先做 |
|---|---|---|---|
| 一 | 短命进程无父、flags 含
procFS/miss、血缘像断裂 |
process cache 指标、execve_map、启动时
procfs 回填 |
写成「零缺口血缘」后停查 |
| 二 | 策略已 apply,任何事件都没有 | BTF 路径、hook 名、tetra probe 能力、sensor
是否 attach |
先重装 DaemonSet;先改 selector 值 |
| 三 | 改了 CR「不生效」、同名策略互相覆盖 | 三条加载路径是否共用
name+namespace |
假定 kubectl 是唯一所有者 |
| 四 | denylist 已配,tetra getevents
仍看见事件 |
该 sink 是 JSON file 还是 gRPC | 把导出过滤当成内核过滤 |
| 五 | 以为拦住了,副作用已发生;或 Override 从未生效 | CONFIG_BPF_KPROBE_OVERRIDE、monitor vs
enforce |
把 SIGKILL 写成「syscall 未发生」 |
默认入口顺序(可裁剪):agent 日志里的
BTF/加载错误 →
tetra tracingpolicy list(看集合键与 state)→
分清观察通道(JSON 文件 vs gRPC)→ 需要归档时
tetra bugtool。节流造成的「EXEC
突然消失」先对照第
11 篇,不要直接定轴二。
层坐标(全文复用)
| 层 | 典型对象 | 否证问题 |
|---|---|---|
| Agent 日志 / BTF | pkg/btf
发现路径、/sys/kernel/btf/vmlinux |
程序有没有加载成功? |
| Policy 集合 | collectionKey{name, namespace} |
你改的那份策略是否就是正在跑的那份? |
| 内核 selector | TracingPolicy selectors(AND、每 hook 最多
5、first-match) |
事件在 hook 内就被丢掉了吗? |
| Sink | JSON file vs gRPC/tetra |
过滤发生在哪一条出口? |
| bugtool / metrics | pkg/bugtool/、tetragon_process_cache_early_deletions_total |
有没有可归档的状态,而不是口头复述? |
tetra bugtool(cmd/tetra/bugtool,默认写出
tetragon-bugtool.tar.gz)由
pkg/bugtool.Bugtool 打包。v1.7.0
doBugtool 会尝试收入:init info、lib 下的 BPF
文件、BTF、agent 日志、metrics、dmesg、tc /
bpftool / gops / pprof、policyfilter map、process
cache、execve_map、gRPC 探测、pmap、cgroup
内存统计、BPF map 统计、tracefs。官方 System dump
文档把入口写成
kubectl exec … -- tetra bugtool。本文不展开任何归档内容——没有本机
dump,就不假装读过 tar。
二、无事件:Hook、加载与 BTF
症状
- TracingPolicy 已 apply,JSON 与 gRPC 都看不到对应事件。
- 内置
process_exec/process_exit也没有(比「某一条策略没匹配」更底层)。 - agent 可能仍 Running。
先定轴二,还是轴三?
轴二:BPF 程序没有 attach,或 hook
符号/类型写错,或内核没有足够的 BTF/特性。
轴三:程序在跑,但你以为生效的那份策略根本不在
collection map 里(下一节)。
两轴都空时,先否证轴二:没有程序,谈策略无意义。
BTF / CO-RE
v1.7.0 pkg/btf/btf.go 的
observerFindBTF 按顺序找:环境变量
TETRAGON_BTF、用户指定
--btf、默认内核 BTF
defaults.DefaultBTFFile(/sys/kernel/btf/vmlinux)、再回退到
lib
目录下的候选文件。找不到则返回明确错误,而不是静默用「大概能跑」的通用字节码。这是
CO-RE 的前提,不是可选项。站内 eBPF 系列 讲 verifier/BTF
全书;本轴只问:这台内核有没有 vmlinux BTF,agent
有没有找到它。
官方 FAQ 把 tetra probe config 与
tetra probe
标成能力探测入口(后者需能加载探测用
BPF)。把输出留在事故节点;本文不复制文档里的示例表冒充实测。
Hook 名与类型
策略「已 apply」只说明控制面对象存在。kprobe 符号写错、把
LSM hook 写成 kprobe、在没有 fentry 支持的内核上用
spec.fentries,都会让 sensor
加载失败或永远打不中。对照第 3 篇
的 hook 类型表与第 5
篇 的 attach 顺序。LoadErrorState
的集合仍占着
collectionKey(下一节),表现为「CR
还在,事件没有」。
不要先做:为「让事件出来」把所有 selector 删光却不看加载错误;把空事件写成「eBPF 不适合这台机器」而不读 BTF 错误。
三、策略不生效:三条路径撞名、policyfilter、selector
v1.7.0 文档 tracing-policy/_index.md
写明三条加载路径:
- Kubernetes:
kubectlapply/deleteTracingPolicy/TracingPolicyNamespaced - gRPC:
tetraadd/delete - 静态文件:
--tracing-policy与--tracing-policy-dir(默认目录defaults.DefaultTpDir=/etc/tetragon/tetragon.tp.d,见cmd/tetragon/main.go的loadTpFromDir)
三条路径进入同一
pkg/sensors collection map。键的定义是:
// Tetragon v1.7.0 pkg/sensors/collection.go(注释已删)
type collectionKey struct {
name, namespace string
}Manager.AddTracingPolicy 用
collectionKey{tp.TpName(), tp.TpNamespace()}。同名且同
namespace 的第二次 add,若已有集合不处于
LoadErrorState,pkg/sensors/handler.go
直接返回:
failed to add tracing policy %s, a sensor collection with the key already exists
这就是「改了 CR 不生效」的机制原因之一:文件或 gRPC
侧已经占用了同一个
name+namespace,CR 更新进不了
map;或反过来,你在看 gRPC
list,改的却是另一条路径上的同名对象。v1.7.0
没有把 k8s / grpc / static 分成独立
domain——那是 tag 之后的 live 文档能力,见第 16 篇
开放问题。排障口语应说「集合键撞名」,不要说「domains
命令没列出」。
policyfilter
身份感知(namespace / podSelector / containerSelector /
hostSelector)依赖
pkg/policyfilter。Helm chart v1.7.0
values.yaml 默认
tetragon.enablePolicyFilter: True;daemon flag
--enable-policy-filter 的代码默认值是
false(pkg/option/flags.go)。主机直接跑二进制、又写了
TracingPolicyNamespaced
时,这条默认差会造成「CR 合法、过滤从未建立」。
pkg/sensors/k8s.go 的
updatePolicyFilter
写明:若过滤实际不需要(集群级策略且选择器全空或全
{} 匹配一切),则不调用
AddPolicy,policyfilter
关闭时仍能加载;一旦需要过滤(有
namespace,或 selector 真的在筛选),AddPolicy
在 disabled 状态下返回
"policyfilter is disabled",集合进入
LoadErrorState。
无 OCI runtime hook 时,身份过滤是 API server 的 best-effort,窗口见第 8 篇 与第 15 篇。排障时不要把「刚启动的容器漏了一次 enforce」默认写成 selector 写错。
selector
内核选择器语义钉在 tag 文档
selectors.md:同一 selector 内过滤器
AND;每个 hook 最多 5 个
selector;多个 selector 是
first-match(短路 OR);无 action 则默认
Post。事件在 BPF 里被 NoPost
吃掉,用户态看起来像「策略没匹配」。先读第 7
篇,再改 YAML。
不要先做:同时从三条路径塞同名策略「看谁赢」;在 Helm 已开 policyfilter、主机安装却未开的环境之间复制 YAML 并认定语义相同。
四、导出缺口:JSON allow/deny 不是 gRPC
这是轴四,不是「CLI 坏了」。
v1.7.0
docs/content/en/docs/concepts/events.md
Caution(原文大意,不把文档示例 JSON
当本机输出):denylist / allowlist
只作用于以文件 sink 导出的 JSON(例如配置了
file 的
tetragon.log)。它们不影响
gRPC API。tetra getevents 走 gRPC
时仍会看到未被该导出过滤器丢掉的事件。
tag 文档把默认 JSON 路径写成
/var/run/cilium/tetragon/tetragon.log。把文件接到
SIEM 之后,值班若用 tetra getevents 验收
denylist,会得到「过滤没生效」的假阴性。完整分层见第 4
篇;SIEM 只能接到 file sink 见第 12
篇。
另一种缺口是内核已经 Post、用户态 field
filter / redaction
裁掉了字段,事件还在但「看起来不像那条规则」。先问
哪一个 sink、哪一层过滤器,再改策略。
学术对照:Garfinkel(NDSS 2003)指出,观测工具若在用户态解释「发生了什么」,必须交代自己看到的是哪一份副本。JSON 与 gRPC 是两份出口,不是同一完备性证明。导出完备性仍是开放问题,见第 16 篇。
不要先做:为了对齐 CLI
与日志,关掉全部导出过滤当长期方案;把 tetra
管道 JSON(kubectl logs … | tetra getevents)与
gRPC tetra getevents
当成同一条路径——前者吃的是已经按文件过滤写出来的日志,后者不是。
五、误阻断与 Override 不可用
症状
- 策略意图是「这次调用不要发生」,进程却被杀掉,文件/套接字副作用已经留下。
- YAML 写了
action: Override,行为与未写时相同。 - monitor 模式误当成 enforce。
Override 的内核门闩
tag 文档 selectors.md:Override
使用内核 error injection,仅在编译了
CONFIG_BPF_KPROBE_OVERRIDE 的内核上可用。官方
FAQ 把该项列入 Enforcement
所需配置;tetra probe 输出里的
override_return 是运行时对应位。没有该
config,不要把 Override 演示写成成功阻断。
enforcement/_index.md
把两种强制执行钉死:
| 动作 | 保证 | 不保证 |
|---|---|---|
| Override | 函数不执行,向调用者返回 argError |
内核未开 error injection 时不可用;不是任意函数都能改返回值 |
| Signal / SIGKILL | 进程被同步终止 | 不保证当前这次 syscall 没有副作用(官方
write() 例:数据仍可能写入) |
要「这次操作本身不完成」,文档要求 Signal
与 Override 组合。monitor vs enforce 模式见
mode.md 与第 10
篇:monitoring 会省略 enforcement 动作。YAML 仍写着
Override,运行时模式却是
monitor,表现为「策略在、阻断无」。
这不是产品口号差异。Bishop 与 Dilger(Computing Systems 9(2), 1996)把 TOCTTOU 写成:检查与使用之间存在可被并发改写的绑定;Garfinkel NDSS 2003 §4.3 把同一结构搬到系统调用拦截(参数竞态、路径竞态)。Tetragon 的 SIGKILL 停在「进程结束」,不停在「该次内核操作未发生」。把「装了 Tetragon 就能在 syscall 前拦住」写成绝对句,会在轴五事故里无法归因。
不要先做:在未确认
CONFIG_BPF_KPROBE_OVERRIDE 前把 Override
当生产门禁;用 SIGKILL 当 Override 的别名。
六、血缘空洞:execve_map 未命中与 procFS
站内 linux/ebpf-security 曾用「零缺口血缘」概括内核进程树。本系列不沿用该口号。v1.7.0 源码给出两条必须写进排障手册的失败路径。
fork 时父不在 map
bpf/process/bpf_fork.c(Tetragon
v1.7.0,以下经删减)挂在
kprobe/wake_up_new_task。若找不到父进程的
execve_map
项,子进程不会被插入 map,也不会发 clone
消息:
/* Do not try to create any msg or calling execve_map_get
* (that will add a new process in the execve_map) if we
* cannot find it's parent in the execve_map.
*/
parent = __event_find_parent(task);
if (!parent)
return 0;父缺失会沿子树传播:后续子孙同样进不了图。排障时应把「进程不在 map」当作轴一的一等故障,而不是「eBPF 保证完整树」的反例去忽略。
启动快照:procFS
标志
agent 启动时 cmd/tetragon/main.go 调用
procevents.GetRunningProcs(),从
--procfs(默认
/proc)列出已存在进程,写入事件缓冲。api/v1/tetragon/tetragon.proto
与 pkg/api/flags.go 把这类事件标为
procFS:它们不是本次
execve 观测到的,而是 init 时从 proc
接口回填。proto 同时说明:miss
表示缓冲里找不到父信息,将尽量从内核结构恢复,args
不可用。
Garfinkel NDSS 2003 §4.1 的教训是:用户态复制 OS
状态会与内核不一致。procfs 快照是启动窗口的折中,不是与
execve hook 同构的观测。flags
里出现 procFS
时,血缘深度与参数完备性都要降级解释。
process cache 的过早删除
用户态 cache(pkg/process/cache.go)在 GC
已把条目标为 deleted
之后若再次收到删除,会增加计数器。v1.7.0 指标名:
tetragon_process_cache_early_deletions_total
pkg/process/metrics.go 的 Help:GC
试图删除一个已经标记为 deleted
的进程的次数;可能表示 GC
删得过早。docs/content/en/docs/reference/metrics.md
使用同一句话。该计数上升是轴一的信号,不是 cache
工作正常的证明。未采集到该指标时不要编造曲线。
bugtool 会尝试 dump process cache 与
execve_map(addProcessCache /
addExecveMap)。用归档对照「map 里有没有这个
pid」,不要用 /proc
事后重建冒充事故瞬间的内核图。
不要先做:把短命进程缺失写成 Falco
才有的问题;把 procFS 事件的 args 空洞当成 hook
失效。
参考资料
规范 / 官方文档 / 源码(A)
- Tetragon
v1.7.0:
docs/content/en/docs/concepts/events.md(JSON allow/deny 不影响 gRPC 的 Caution);tracing-policy/_index.md(三条加载路径);tracing-policy/selectors.md(AND / 最多 5 / first-match;CONFIG_BPF_KPROBE_OVERRIDE);enforcement/_index.md;reference/metrics.md(tetragon_process_cache_early_deletions_total);troubleshooting/sysdump.md;installation/faq.md - 源码 tag
v1.7.0:
pkg/sensors/collection.go(collectionKey);pkg/sensors/handler.go(撞名错误);pkg/sensors/k8s.go(updatePolicyFilter);pkg/policyfilter/disabled.go;pkg/btf/btf.go;pkg/bugtool/bugtool.go(doBugtool);pkg/process/cache.go、metrics.go;bpf/process/bpf_fork.c;pkg/api/flags.go;api/v1/tetragon/tetragon.proto(procFS/miss);cmd/tetragon/main.go(GetRunningProcs、loadTpFromDir) - Helm chart @
tag:
install/kubernetes/tetragon/values.yaml(enablePolicyFilter: True)
论文 / 谱系
- Garfinkel, T. (2003). Traps and Pitfalls: Practical Problems in System Call Interposition Based Security Tools. NDSS 2003. §4.1 复制 OS 状态;§4.3 竞态(TOCTTOU)
- Bishop, M. & Dilger, M. (1996). Checking for Race Conditions in File Accesses. Computing Systems 9(2):131–152
站内对照
实验台账
- 本篇无 Tetragon 节点实测;不粘贴
tetra getevents/ bugtool 归档 / metrics scrape。命令语义以 tag 文档与源码为准。
轴选错时,后面的 YAML 与重装都在解决另一个问题。记住一个动作:先说出是血缘、hook、加载键、sink 还是 Override 门闩,再改那一层的对象。
→ 上一篇:运维与导出工程 · 下一篇:对照替代路径 · 系列目录
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Tetragon / eBPF】强制执行:Override 与 SIGKILL 各自保证什么
按 Tetragon v1.7.0 官方 Enforcement 文档钉死:Override 使函数不执行并返回 argError;SIGKILL 终止进程但不保证当前 syscall 无副作用;monitor 与 enforce 如何把动作抹掉;以及 Bishop–Dilger–Garfinkel 的 TOCTOU 谱系落到 bpf_enforcer.c 的边界。
【Tetragon / eBPF】运行时安全全景:缺口、五轴坐标系与 16 篇路线
相对 Cilium 数据面、可观测对照与 Falco 口号,钉清 Tetragon v1.7.0 运行时安全内核缺口;定义进程血缘、Sensor/Hook、策略加载、过滤分层、强制执行五条坐标系,给出 16 篇阅读路线。
【Tetragon / eBPF】进程模型:exec_id、血缘、in_init_tree 与 procfs 回填
钉清 Tetragon v1.7.0 的进程身份:process_exec/exit 字段、exec_id 如何由 node:ktime:pid 生成、execve_map 与用户态 cache 的分工、in_init_tree 如何区分 kubectl exec 注入,以及 fork 时父不在 map 则子不入图的失败路径。
【Tetragon / eBPF】Sensor 与 Hook:进程传感器、kprobe、tracepoint、uprobe、LSM 与 fentry
钉清 Tetragon v1.7.0 内置 __base__ 进程传感器与 TracingPolicy 动态 hook 的分工:kprobe/kretprobe、tracepoint/rawtp、uprobe/USDT、LSM 与 fentry 各自能观察什么、依赖哪些内核能力,以及选错 hook 类型时的失败表象。