土法炼钢 · 系统与基础设施

【Tetragon / eBPF】选择器与内核过滤:AND、first-match 与动作挂载点

文章导航

分类入口
kubernetessecurity
标签入口
#tetragon#ebpf#selectors#matchbinaries#matchparentbinaries#override#sigkill#v1.7.0

目录

TracingPolicy 解决「策略如何加载」;本篇解决「hook 命中后是否产生事件、执行哪一个动作」。排障时若 tetra 仍看见「以为已滤掉」的事件,要先分清:内核选择器第 4 篇 的 JSON export allow/deny 不在同一层。

本篇在系列中的位置

篇目 核心内容
第 6 篇 · TracingPolicy CRD 与加载路径
第 7 篇 · 选择器与内核过滤 selector 语义、常用过滤器、动作挂载点
第 8 篇 · K8s 身份感知 namespace / pod / container / host
系列目录 全部篇目

版本锚定:Tetragon v1.7.0;选择器语义以 tag 文档 docs/.../concepts/tracing-policy/selectors.mdpkg/selectors/ 为准。本篇不是规则百科;CEL-in-BPF 只点到已核对的边界,不写成任意通用语言。


一、选择器语义:AND、最多 5、first-match

官方 Selectors 文档开篇定义(v1.7.0):

官方示例(文档示例,非本机输出):用第一个 selector 对 /usr/bin/mountNoPost,第二个 selector 对其余调用 Post——从而「排除某二进制的事件」:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "mount-example"
spec:
  kprobes:
  - call: "sys_mount"
    syscall: true
    selectors:
      - matchBinaries:
        - operator: In
          values:
          - "/usr/bin/mount"
        matchActions:
        - action: NoPost
      - matchActions:
        - action: Post
flowchart TD
  hook["Hook_fires"] --> s0{"Selector_0_all_filters_AND"}
  s0 -->|"match"| act0["Run_actions_of_selector_0"]
  s0 -->|"no"| s1{"Selector_1_AND"}
  s1 -->|"match"| act1["Run_actions_of_selector_1"]
  s1 -->|"no"| sN{"Later_selectors_up_to_5"}
  sN -->|"none matched"| none["No_selector_action"]

常见误读:把多个 selector 理解成「全部执行」或「合并动作」。v1.7.0 文档明确是 first-match;后面的 selector 在前者已匹配时不会再跑。

这与 JSON denylist 的关系:选择器决定内核是否 Post;denylist 只影响文件 sink。二者不一致时,见第 4 篇 Caution。

其它过滤器(本篇不展开)

同一文档还定义 matchDatamatchReturnArgsmatchPIDsmatchNamespacesmatchCapabilities 以及 namespace/capability 变更过滤器。它们服从同一套 AND / first-match 规则。例如 matchPIDs 文档用「namespace PID 不为 1」近似捕捉 kubectl exec 注入进程——这是模式提示,不是完备检测器;与进程模型中的 in_init_tree 叙述对照见 第 2 篇

本篇刻意不写成「每个算子 × 每个 hook」矩阵:规则百科会过时,且掩盖 first-match 顺序这个真正高频故障点。


二、matchArgs / matchBinaries / matchParentBinaries

本节约到三条最高频过滤器;PID / namespace / capability 等过滤器文档有完整列表,排障时回查 selectors.md,不在此做成规则库。

matchArgs

按函数参数值过滤。可用 index(函数参数位置)或 args(spec 中参数位置);args 优先。常用算子包括 EqualNotEqualPrefixPostfixMaskFileTypeNotFileType 等(以文档为准)。

file / path 类型参数,可直接按路径或文件类型匹配。多个 matchArgs 条目仍在同一 selector 内 AND

matchBinaries

按进程二进制路径过滤。算子含 In / NotIn / Prefix / NotPrefix / Postfix / NotPostfix。默认行为文档写明与 followForks: true 相关:子进程会被跟随。

followChildren: true 时额外限制:策略安装之前已存在的子进程不会匹配;带 followChildrenmatchBinaries 段不超过 64;算子限于 In / NotIn

Shebang 脚本:文档 Caution——匹配的是解释器路径,不是脚本路径。写 /opt/scripts/foo.py 不会按预期工作,应匹配 /usr/bin/python3 一类解释器。

matchParentBinaries(v1.7.0)

父进程二进制路径过滤,算子集合与 matchBinaries 类似。文档 Warning:仅在启用 BPF parents_map 时可用(选项 --parents-map-enabled),并带来额外内存开销。followChildren 的限制与 binaries 侧同类(安装前已有子进程不匹配;带 followChildren 的段 ≤ 64;算子限 In/NotIn)。

典型组合:父为交互 shell、子为 cat——用于区分「脚本/服务拉起」与「人在 shell 里敲」。

过滤器 匹配对象 v1.7.0 额外前提
matchArgs hook 参数 参数类型与 hook 声明一致
matchBinaries 当前进程二进制(解释器而非 shebang 脚本) followChildren 有名额与算子限制
matchParentBinaries 父进程二进制 --parents-map-enabled

matchBinariesmatchArgs 写在同一 selector 时仍然是 AND:例如「二进制是 sshd 子孙 fd 为 1/2/3」的官方 write 示例,依赖的是同一 selector 内组合,而不是两个 selector 的 OR。若误拆成两个 selector,first-match 可能让你只打到其中一半条件。


三、动作:Post / NoPost / Override / Sigkill / Signal

动作写在 matchActions(返回路径另有 matchReturnActions,本篇不展开百科)。默认无动作即 Post

动作 文档语义(摘要) 本系列后续
Post 事件从内核传到 agent;可带 rateLimit、栈追踪等参数 节流细节 → 第 11 篇
NoPost 抑制事件,但同 selector 内其它动作仍执行 适合「只要 Override/杀进程、不要日志洪水」
Override 改写调用返回值;kprobe 依赖内核 error injection / CONFIG_BPF_KPROBE_OVERRIDE;uprobe Override 文档限 x86_64 保证语义 → 第 10 篇
Sigkill 在内核同步终止匹配进程 不保证当前 syscall 无副作用 → 第 10 篇
Signal 向当前进程发 argSig 指定信号;SIGKILL(9) 可近似 Sigkill 示例 第 10 篇

PostrateLimit:文档写明默认可按秒,后缀 m/h 为分/时;在时间窗内对同线程、相同已检查参数(每个参数前 40 字节)抑制重复 Post;需内核 ≥ 5.3。可用 rateLimitScope 扩到 processglobal。完整观测税与误杀面放到第 11 篇,本篇只立指针。

Override 的文档约束值得单独记住(细节与 TOCTOU 在第 10 篇展开):

若策略在无 Override 能力的内核上仍写 action: Override,加载或运行期会失败/无效——不要把「YAML 能 apply」当成「阻断已生效」。


四、内核执行 vs 用户态 GetUrl / DnsLookup

selectors.md 的 Note(v1.7.0)划分:

工程含义:

  1. 依赖 GetUrl / DnsLookup 做「阻断」是错的——它们不在 syscall 路径上 inline 失败。
  2. 真正要缩小 Garfinkel 所说的检查窗口,应使用内核侧 Override / Sigkill(仍受 TOCTOU 与配置依赖约束,见第 10 篇)。
  3. FollowFD 族在文档中已标 deprecated(文案仍写计划于 1.5 移除;以 v1.7.0 文档现状为准:不要在新策略里依赖)。

文档 Note 没有Signal 写进「内核直接执行」那一列表,也未把它与 GetUrl 并列。写作上本篇只保证:Sigkill/Override/Post 的内核对齐、GetUrl/DnsLookup 的用户态对齐均有明文;Signal 的精确执行位点以 selectors 专节与第 10 篇源码路径为准,不在此脑补。


五、CEL-in-BPF:点到边界,不写成通用语言

v1.7.0 引入在 BPF 中求值 CEL 表达式的能力(Release notes · PR #4504)。本篇提供 CEL 教程。已从 tag 源码核对的硬边界:

因此:不要把 CEL-in-BPF 叙述成「任意 CEL 都能在所有 hook/内核上跑」。需要表达式能力时,回到 pkg/celbpf/ 与具体策略加载日志,而不是假设与用户态 CEL 库同构。


六、学术谱系、争论与开放问题

谱系:Wright et al.(USENIX Security 2002)把安全决策挂到内核 hook;选择器是 Tetragon 在 eBPF 程序里对「哪些调用值得处理」的可配置裁剪。Garfinkel(NDSS 2003)论证用户态系统调用拦截的竞态:过滤若发生在用户态,强制执行可能永远「慢半拍」。Tetragon 把 match* 与 Sigkill/Override 放进 BPF,是对该论点的工程回应——前提是 selector 真的在内核匹配,且动作选的是内核路径。

争论:表达力 vs 可预测性

主张 代价
多 selector + 丰富算子 一条 hook 覆盖复杂例外(如 mount 示例) first-match 顺序敏感;难审计「为何没 Post」
尽量单 selector + 显式 Post/NoPost 心智模型简单 例外逻辑可能拆成多条 TracingPolicy,增加 collectionKey 管理成本

开放问题Post.rateLimit 与 cgroup-rate(第 11 篇)叠加时,运维如何区分「被节流」与「未匹配」——需要 metrics 与排障坐标系,而不是再加一条更宽的 selector。


七、小结

  1. Selector 内 AND;每 hook ≤ 5;多 selector 为 first-match;默认 Post
  2. matchArgs / matchBinaries / matchParentBinaries 覆盖参数与血缘侧二进制;后者要 --parents-map-enabled
  3. NoPost 抑日志不抑同组动作;Override/Sigkill/Signal 的保证边界见第 10 篇。
  4. GetUrl/DnsLookup 在用户态,不能当 inline 阻断。
  5. CEL-in-BPF 有宏与最多 8 路表达式边界;下一篇谈 K8s 身份如何在加载前再裁一轮工作负载集合。

不写边界(本篇)


八、参考资料

规范 / 官方文档(A)

源码(A)

核心论文

站内对照

实验台账


上一篇:TracingPolicy · 系列目录 · 下一篇:K8s 身份感知策略

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-18 · kubernetes / security

【Tetragon / eBPF】强制执行:Override 与 SIGKILL 各自保证什么

按 Tetragon v1.7.0 官方 Enforcement 文档钉死:Override 使函数不执行并返回 argError;SIGKILL 终止进程但不保证当前 syscall 无副作用;monitor 与 enforce 如何把动作抹掉;以及 Bishop–Dilger–Garfinkel 的 TOCTOU 谱系落到 bpf_enforcer.c 的边界。


By .