Tetragon 强制执行:Override 与 SIGKILL 各自保证什么

原文发布于 quant67.com,转载请保留出处。

第 9 篇 把观测挂到 LSM / tcp_* / commit_creds。本篇回答强制执行轴上最容易写错的一句:发了 SIGKILL 是否等于这次操作没发生。 官方结论是否定的。Override 让函数根本不跑;Signal 保证进程同步终止,不保证当前 syscall 没有副作用。要拦住这次操作,文档要求把两者组合。

本篇在系列中的位置

篇目 核心内容
第 9 篇 · 文件 / 网络 / 能力 观测 hook 与失败表象
第 10 篇 · 强制执行 Override vs Signal;mode;TOCTOU
第 11 篇 · 节流与观测税 事件被丢掉的另一层
系列目录 全部篇目

版本锚定 :Tetragon v1.7.0 。机制叙述对齐 tag docs/content/en/docs/concepts/enforcement/_index.md、tracing-policy/mode.md、selectors.md 的 Override / Signal 节,以及 bpf/process/bpf_enforcer.c。无 CONFIG_BPF_KPROBE_OVERRIDE 的内核上不把 Override 写成已生效。无真实节点则不粘贴伪造阻断输出。


一、Override:函数不执行,调用方拿到 argError

selectors.md 把动作分成两类:Sigkill、Override、FollowFD 族、Post、TrackSock 等在 内核 BPF 里执行 ;GetUrl 与 DnsLookup 在用户态收到事件之后才发生。强制执行轴只讨论前一类里的 Override / Signal。把阻断写成「agent 收到 JSON 再 kubectl delete」,已经离开 v1.7.0 的 inline 模型。

v1.7.0 Enforcement 文档把第一种强制执行定义成:改写函数返回值,使该函数永不执行,改为把一个值(通常是错误)返回给调用者。 一般只有系统调用和安全检查函数允许以这种方式改返回值。策略侧字段是 matchActions 里的 Override,错误码写在 argError。

selectors.md 把同一件事写得更操作化:Sigkill 终止整个进程;Override 在被 kprobe 的函数位置上 运行,返回 argError 指定的值。之后是否停下来,取决于内核路径或用户态如何处理这个返回值。

内核依赖写在 note 里,不是可选项:

  • Override 走内核 error injection 框架,仅当内核编译了 CONFIG_BPF_KPROBE_OVERRIDE。
  • 覆盖系统调用是主用例;其它可注入函数带 ALLOW_ERROR_INJECTION(),可在 /sys/kernel/debug/error_injection/list 核对。
  • 从内核 5.7 起,覆盖 security_ hook 也成为可能。

没有该 config 却把策略写成「已经阻断」,是本篇明确禁止的结论。加载失败或动作被跳过时,排障落到第 13 篇,不要用「YAML 里有 Override」当证据。

tag 示例 examples/tracingpolicy/override-security.yaml(以下按 tag 原文,未改字段):

yaml 复制代码
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "security-override"
spec:
  kprobes:
  - call: "security_inode_mkdir"
    syscall: false
    selectors:
    - matchBinaries:
      - operator: "In"
        values:
        - "/usr/bin/bash"
      matchActions:
      - action: Override
        argError: -1

selectors.md 另给 sys_symlinkat 在路径等于 /etc/passwd\0 时返回 -1 的例子。两者都是「这次调用失败」,不是「进程消失」。argError: -1 对用户态通常表现为失败;具体 errno 是否被翻译成 -EPERM / -EACCES 取决于被覆盖函数的约定,文档没有为每个 hook 列一张表,本篇也不编。

uprobe 上的 Override 是另一条、更窄的路:文档警告容易把被跟踪进程打崩;仅 x86_64 ;还要求 uprobe 打在函数开头、经 call 进入、返回 int。argRegs 可以改寄存器,文档写明接口可能再变。本篇不把 uprobe Override 当成通用阻断手段。


二、Signal / Sigkill:进程停,当前 syscall 可以已经生效

第二种强制执行是向进程发信号。Sigkill 从内核同步终止匹配选择器的那个进程;Signal 用 argSig 指定信号号,argSig: 9 与 Sigkill 等价。

Enforcement 文档用 write() 钉死副作用边界:

在 write() 系统调用里发送 SIGKILL,不保证 数据不会被写入文件。它保证进程被同步终止(线程也会停)。某些场景下「进程已停、不再处理返回值」就够了。若要确保这次操作本身不完成,应把 Signal 与 Override 组合。

这与「杀了进程等于回滚这次 syscall」相反。write 已经把数据推进文件之前或之中被杀,磁盘上的字节可以已经存在。对 connect / unlink / chmod 同类问题成立与否取决于 hook 落在函数入口还是返回点、以及内核是否已产生副作用------文档只把 write() 写成可核对反例,本篇不推广成未核过的 syscall 表。

selectors.md 的 Sigkill 示例挂在 sys_write、用 type: fd 匹配 /etc/passwd,并对命名空间 PID 0/1 做 NotIn。注释写明 kubectl exec 会在容器 PID 命名空间里长出新进程,不是 PID 1 的孩子。这是选择器细节,不是「SIGKILL 让 write 没发生」。同一文档要求:若计划把该动作用于强制执行,先读 Enforcement 节------也就是本篇正在钉的那份。

组合写法在策略里就是两个 matchActions 项(字段以 selectors.md 已出现的为准,不发明第三种动作名):

yaml 复制代码
matchActions:
- action: Override
  argError: -1
- action: Signal
  argSig: 9

顺序与 first-match 语义见第 7 篇。本篇只要求:官方文档把「拦住这次操作」定义成 Override 负责函数体不跑,Signal 负责进程不再继续;缺任何一侧,保证就回到上表那一行。

sequenceDiagram participant Proc as Process participant Sys as Syscall body participant Hook as BPF selector participant FS as File or net effect Proc->>Sys: enter write or other call Sys->>Hook: kprobe at chosen hook alt Override Hook-->>Proc: return argError, body skipped else Signal only Hook->>Proc: SIGKILL Sys->>FS: side effects may already exist else Signal plus Override Hook->>Proc: SIGKILL Hook-->>Proc: body skipped if Override applies end

三、monitor 与 enforce:动作可以在不改 YAML 的情况下被抹掉

mode.md 把策略模式分成两种:

  • monitoring:强制执行操作被省略(elided)
  • enforcement:强制执行操作被尊重并执行

tetra tracingpolicy list 列出每条策略的 MODE 列。文档示例(文档示例 ,非本机输出)表头为 ID / NAME / STATE / FILTERID / NAMESPACE / SENSORS / KERNELMEMORY / MODE,同一张表出现 enforce 与 monitor 两种短名。STATE 是 enabled/disabled,与 MODE 正交:enabled + monitor 表示策略在跑、强制执行被省略。

写在 CR 里的形状(mode.md 文档示例,经删减):

yaml 复制代码
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
  name: "enforce-policy"
  namespace: "default"
spec:
  options:
    - name: "policy-mode"
      value: "monitor"

模式有三处来源,优先级从低到高:

  1. 写在策略自身 spec.options,键 policy-mode(示例值为 "monitor")。
  2. 加载时指定:tetra tracingpolicy add --mode monitor policy.yaml。
  3. 运行时经 gRPC:tetra tp set-mode --namespace pizza enforce-security enforce。

排障含义:YAML 里仍写着 Sigkill / Override,但策略处于 monitor 时,这些动作不会发生。把「策略里有阻断」等同于「节点上在阻断」,会误判。第 7 篇讨论选择器 first-match;本篇只钉 mode 可以把已挂上的强制执行动作关掉。


四、TOCTOU 谱系:从文件竞态到 inline BPF,仍拦不住的窗口

1. 经典定义

Bishop & Dilger(Checking for Race Conditions in File Accesses , Computing Systems 9(2), 1996)把文件访问里的竞态写成:检查时刻看到的路径/权限,与随后使用时刻的 inode 不必是同一个对象。经典形态是 access() 通过后再 open()------中间可以换成符号链接。论文的对象是「按名字检查、按对象使用」这条裂缝,不是某个具体 LSM。

Garfinkel(Traps and Pitfalls , NDSS 2003 §4.3)把同一裂缝移到系统调用插入式工具,并使用 TOCTTOU(time-of-check to time-of-use)这个已经在 Bishop & Dilger 工作里钉死的名字。§4.3 的要点是:工具在用户内存上做检查,内核稍后才使用该指针。 攻击者可以在两次之间替换指针指向的内容。Garfinkel 还区分了「检查窗口」与「工具自己的延迟」:用户态策略引擎若先把事件拷出内核再决定杀进程,窗口比 inline 检查更大。这不是实现 bug 清单,而是接口形状:检查与使用若不是同一原子步骤,窗口就存在。

v1.7.0 hooks.md 把这条谱系接到 Tetragon:挂系统调用且参数是用户态指针时,存在 TOCTOU;挂后续内核函数(LSM security_ hook)时,操作的是已经拷进内核的状态,这条用户指针窗口关掉。

2. Tetragon 的分叉:inline 动作

相对用户态规则引擎(在事件出内核之后再决定杀不杀),Tetragon 把 Override / Sigkill / Signal 标成 在内核 BPF 里执行 (selectors.md note;GetUrl / DnsLookup 才是用户态)。这消除的是「事件排队到 agent 再 SIGKILL」那一段延迟,不消除 Garfinkel 写的「syscall 参数仍是用户指针」窗口,也不消除 Enforcement 文档写的「SIGKILL 不撤销已发生的 write」。

工业反例留给第 14 篇:Falco GHSA-6v9j-2vm2-ghf7 / CVE-2022-26316 是用户态读 argv/sockaddr 的检查窗口。本篇只需要这一句对照:Tetragon 把动作推进内核,并没有把 Bishop--Dilger 的对象替换问题从文件系统里删掉。

3. bpf_enforcer.c:延迟到 enforcer 程序才动手

直接写在 kprobe 选择器上的 Override / Sigkill 走 generic kprobe 路径。另一条路径是 NotifyEnforcer:先把 {error, signal} 写入 enforcer_data map(键为当前 pid_tgid),再由独立的 enforcer 程序执行。

bpf/process/bpf_enforcer.c 的 do_enforcer()(源码摘录,删减了 map 未命中的早退):

c 复制代码
FUNC_INLINE int do_enforcer(void *ctx)
{
        __u64 id = get_current_pid_tgid();
        struct enforcer_data *data;

        data = map_lookup_elem(&enforcer_data, &id);
        if (!data)
                return 0;

        if (data->signal)
                send_signal(data->signal);

        map_delete_elem(&enforcer_data, &id);
        return data->error;
}

若编译了 __BPF_OVERRIDE_RETURN,kprobe 节里对非零返回值调用 override_return(ctx, ret)。否则走 fmod_ret/security_task_prctl 占位(注释写明正常运行时函数名由 Tetragon 动态设置)。信号在 override 之前发出:这与 Enforcement 文档「组合 Signal + Override」一致------enforcer 路径上两者可以写进同一条 enforcer_data(error 与 signal 两个 __s16 字段)。

bpf_enforcer.h 的 do_enforcer_action() 在 map 里已有条目时记 ENFORCER_MISSED_OVERWRITTEN;清理时若通知从未被消费则记 ENFORCER_MISSED_NOACTION。这是强制执行路径上的丢失信号,不是观测节流。

selectors.md 写明 NotifyEnforcer 面向 缺少 multi-kprobe 快速挂载 的内核,用 raw syscall tracepoint 挂 enforcer。kprobe_multi 可用时,同一需求可直接写 kprobe 动作。tag examples/tracingpolicy/killer.yaml 是 list:dups + NotifyEnforcer / argError: -1 / argSig: 9 的完整示例。本篇不把 killer 示例展开成规则百科。


五、Persistent enforcement:仅作边界指针

v1.7.0 docs/content/en/docs/concepts/enforcement/persistent-enforcement.md 的目标是:agent 进程消失后,强制执行策略仍保持运行。 开关是 --keep-sensors-on-exit。退出时程序/map/link 仍 pin 在 /sys/fs/bpf/tetragon。新进程启动会把已有目录改名为 /sys/fs/bpf/tetragon_old,装上配置的策略,再删掉 _old。

文档自己写的限制:停机期间 收不到事件,只有强制执行还在。 本篇不重写 pin 细节、不讨论晚于 v1.7.0 的 gRPC 策略持久化开关。把它记成:观测管道与强制执行管道可以暂时解耦;解耦期间第 12 篇的 SIEM 文件 sink 是空的,第 4 篇的 gRPC 也同样没有消费者。新进程启动仍会搬走旧 bpf 树------「keep on exit」不是「restart 后自动接管同一批 pin」,而是给加载新策略留出重叠窗口。细节以 tag 该页三条启动步骤为准。


六、Override 与 Signal 保证对照

口径:v1.7.0 Enforcement 文档 + selectors.md。无本机实测时长或成功率。

维度 Override Signal / Sigkill
当前函数体 不执行 可能已执行或部分执行
返回值 调用方收到 argError 进程可能已无法处理返回值
进程生命 默认仍活着,除非另有 Signal 同步终止(SIGKILL)或按 argSig
内核依赖 CONFIG_BPF_KPROBE_OVERRIDE;可注入函数列表 bpf_send_signal 路径;不依赖 override config
官方反例 --- write() 仍可能把数据写入文件
要拦住这次操作 单独 Override 针对该函数 文档要求与 Override 组合
monitor mode 动作被省略 动作被省略

争论(有文献支撑):Override 的正确性依赖「该函数允许 error injection、且调用方把错误当失败」;SIGKILL 的简便性依赖「杀进程就够了」。Garfinkel 2003 的工具若只做杀死而不撤销,与 Tetragon 文档的 write() 句是同一类缺口。生产上可接受边界------例如审计磁盘已写入但进程已死------是开放问题,不是本版本能关闭的不变量。

开放问题:

  1. TOCTOU 可接受边界 :策略挂在 sys_* 用户指针上时,是否必须改挂 security_* 才允许 enforce。入口:hooks.md 警告;Bishop & Dilger 1996;第 9 篇路径模式。
  2. monitor 优先级误用 :运行时 set-mode monitor 盖住 CR 里的 enforce 之后,审计日志仍显示策略「enabled」。入口:mode.md 三条来源。
  3. enforcer map 丢失 :ENFORCER_MISSED_OVERWRITTEN / NOACTION 在多线程密集 Notify 时是否可观察。入口:bpf_enforcer.h;第 12 篇 metrics 不覆盖这两类丢失,除非另有独立指标(本篇不编名字)。

七、小结

  1. Override:函数不跑 ,返回 argError;没有 CONFIG_BPF_KPROBE_OVERRIDE 就不能当阻断。
  2. SIGKILL:进程停 ,不保证 当前 syscall 无副作用;官方例子是 write()。
  3. 要让这次操作不完成:按官方文档组合 Signal 与 Override。
  4. monitoring / enforcement 可以在不改 hook YAML 的情况下关掉或打开动作;运行时 set-mode 优先级最高。
  5. TOCTOU:Bishop & Dilger 1996 → Garfinkel 2003 §4.3 → Tetragon 用 LSM/inline 动作缩小窗口,不删除 SIGKILL 副作用与用户指针 hook 的窗口。
  6. --keep-sensors-on-exit 只保证停机时强制执行仍在,不保证事件仍被导出。

下一篇把「策略匹配了但事件不见了」从强制执行挪到节流与环缓冲。


八、参考资料

规范 / 官方文档(A)

  • Tetragon v1.7.0 Enforcement --- docs/content/en/docs/concepts/enforcement/_index.md(Override 永不执行;SIGKILL 与 write();组合 Signal+Override)。
  • Tetragon v1.7.0 Enforcement Mode --- docs/content/en/docs/concepts/tracing-policy/mode.md。
  • Tetragon v1.7.0 Selectors --- Override / Sigkill / Signal / NotifyEnforcer。
  • Tetragon v1.7.0 Persistent enforcement --- docs/content/en/docs/concepts/enforcement/persistent-enforcement.md(指针,非全文)。
  • Tetragon v1.7.0 Hook points --- syscall 指针 TOCTOU 警告。

源码(A)

  • bpf/process/bpf_enforcer.c(do_enforcer、override_return、fmodret_enforcer)@ v1.7.0
  • bpf/process/bpf_enforcer.h(do_enforcer_action、enforcer_missed_notifications)@ v1.7.0
  • examples/tracingpolicy/override-security.yaml、killer.yaml @ v1.7.0

核心论文

  • Bishop, Dilger. Checking for Race Conditions in File Accesses . Computing Systems 9(2), 1996.
  • Garfinkel. Traps and Pitfalls: Practical Problems in System Call Interposition Based Security Tools. NDSS 2003, §4.3.

站内对照

实验台账

  • Override vs Sigkill 演练未跑;本篇不给伪造 Killed 终端输出。文档 persistent 示例中的 Killed 不转载为本机结果。

→ 上一篇:文件 / 网络 / 能力观测面 · 系列目录 · 下一篇:节流与观测税

相关推荐
IT大白鼠8 小时前
彭大帅的AI运维助手——自然语言管理 Linux 集群与网络设备——第 2 篇 · 安全守规矩的 AI:分级安全管控是灵魂
linux·运维·人工智能
风华同学8 小时前
免密SSH登录Ubuntu
linux·运维·ubuntu
꯭自꯭闭꯭8 小时前
达梦守护集群手工切换及故障切换
linux·运维·服务器·数据库
cuijiecheng20188 小时前
RK3588 DEP 接入 Dante Domain Manager(DDM)完整测试记录
linux
爱吃香菜的初学者8 小时前
十二.Linux——管道
linux·运维·服务器
网硕互联的小客服17 小时前
Linux服务器磁盘应该如何合理分区?
linux·运维·服务器
贵沫末18 小时前
Ubuntu——常用软件安装
linux·运维·ubuntu
Doraemomo18 小时前
Linux内核驱动开发——中断与定时器
linux·运维·驱动开发
2301_8084143819 小时前
Linux中动静态库的理解
linux·运维·服务器
M78佐菲19 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习