选择器
在 hook
内过滤参数与二进制;本篇过滤的是哪些工作负载会进入该策略的生效集合。表象常是「策略只打中部分
Pod」或「主机进程也被误杀」——优先查 namespace / pod /
container / host 组合,以及 policyfilter
是否真正启用,而不是先改 matchArgs。
本篇在系列中的位置
篇目 核心内容 第 7 篇 · 选择器与内核过滤 hook 内 selector 第 8 篇 · K8s 身份感知策略 Namespaced、pod/container/host、policyfilter 第 9 篇 · 文件/网络/能力观测面 典型 hook 模式 系列目录 全部篇目
版本锚定:Tetragon v1.7.0;身份过滤以
docs/.../concepts/tracing-policy/k8s-filtering.md、pkg/policyfilter/、pkg/sensors/k8s.go为准。本篇不写spec.nodeSelector(晚于 v1.7.0)。 无 OCI hook 的完整接线见 第 15 篇。
一、TracingPolicyNamespaced:按命名空间裁剪
TracingPolicyNamespaced 与集群级策略共用
TracingPolicySpec,但对象落在某一 Kubernetes
namespace,并且只应用于该 namespace 的
Pod(官方 k8s-filtering.md · Namespace
filtering)。
与集群级策略的关键差别(复核第 6 篇):
TpNamespace()返回对象 namespace,进入collectionKey的第二分量。- 同名策略可以在不同 namespace 各有一份;与集群级空 namespace 键不冲突。
- 禁止非空
hostSelector(CRD XValidation +updatePolicyFilter双保险):命名空间策略不能匹配主机工作负载。
文档动机写得很硬:身份感知过滤在 eBPF 内核侧完成——观测上少拷无关事件;强制执行上避免用户态「事后」拦截的竞态。这与 Garfinkel(NDSS 2003)对用户态系统调用拦截窗口的批评同向;实现载体是 policyfilter,不是「再实现一套 LSM」。
与集群级策略并存时的键
| 对象 | collectionKey 示例 |
|---|---|
TracingPolicy
metadata.name=foo |
(foo, "") |
TracingPolicyNamespaced
default/foo |
(foo, "default") |
TracingPolicyNamespaced
kube-system/foo |
(foo, "kube-system") |
三份可以同时存在。删除或 disable 时必须带对 namespace,否则会打到错误集合——这是第 5–6 篇撞名问题在身份轴上的投影。
二、podSelector
与 containerSelector
podSelector
标准 label selector,选择策略生效的 Pod。文档示例用
matchLabels: app: lseek-test;也可使用
matchExpressions(例如按
k8s:io.kubernetes.pod.namespace
之类键——以你集群实际标签为准)。
pkg/sensors/k8s.go:若只写了
podSelector 而
containerSelector == nil,会把
containerSelector 填成空
{},表示「匹配 Pod
内全部容器」。
containerSelector
文档(v1.7.0 k8s-filtering.md)写明:当前支持字段为
name 与
repo(容器仓库)。
源码核对结果:
| 位置 | 说法 |
|---|---|
TracingPolicySpec.ContainerSelector
注释 |
「Currently, only the “name” field is supported.」 |
pkg/policyfilter/state.go
containerMatches |
构造标签映射含 name 与
repo,再交给 selector |
pkg/podhelpers.PodContainersInfo |
Name ← 容器名;Repo ← 从
ImageID 按 @ 分割取前半(digest
形式) |
结论(v1.7.0):运行时 policyfilter
同时支持 name 与
repo;CRD
结构体注释滞后于实现与概念文档。repo 依赖
ImageID 能拆出 @
前缀——若运行时给出的 ImageID 形态不含
@,repo
可能为空字符串,选择器会匹配失败。写策略时优先用稳定的容器
name;用 repo
前先确认节点上 ImageID 格式。
官方文档示例(container name: main;摘自
k8s-filtering.md):
spec:
containerSelector:
matchExpressions:
- key: name
operator: In
values:
- maincontainerSelector 是
podSelector 的第二层:先匹配
Pod,再匹配容器。
三、hostSelector:v1.7.0
仅 null 与 {}
hostSelector
决定策略是否作用于主机工作负载。v1.7.0
文档与 updatePolicyFilter 一致:
| 值 | 含义 |
|---|---|
null(省略 / 默认) |
不按「显式选中全部 host」解释;与其它字段组合见下表 |
{}(空 selector) |
匹配全部主机工作负载 |
带任意 matchLabels /
matchExpressions |
错误:spec.hostSelector does not support arbitrary labels... |
TracingPolicyNamespaced 上任何非 null
hostSelector 都会触发错误。
没有 nodeSelector。
按节点标签门控策略是 v1.7.0 之后的能力,本篇不列能力表。
文档给出的意图示例(摘自 k8s-filtering.md,非实测):
- 仅主机:
hostSelector: {},且保持 pod/container 为默认 null。 - 仅 Pod(任意 Pod):
podSelector: {}(空 selector 表示匹配全部 Pod 标签集合意义上的「全选」),从而排除 host(因 hostSelector 仍为 null)。 - 主机 + 某命名空间 Pod:
hostSelector: {}加上针对k8s:io.kubernetes.pod.namespace的podSelector.matchExpressions。
这些示例说明:「排除 host」靠的是保持
hostSelector 为 null,并显式写出 pod/container
侧选择器——而不是写一个「负向
host」表达式(本版本也不支持任意 host 标签)。
四、组合语义表(改编自 v1.7.0 文档)
下表改编自官方 k8s-filtering.md「Filtering
semantics」;口径为 v1.7.0 tag 文档。
hostSelector |
podSelector |
containerSelector |
结果 |
|---|---|---|---|
null(默认) |
null(默认) |
null(默认) |
特殊情形:工作负载过滤关闭,选中全部 host 与 pod/container |
null |
null |
{...} |
匹配所有 Pod 中符合 containerSelector 的容器;不含 host |
null |
{...} |
null |
匹配符合 podSelector 的 Pod 内全部容器;不含 host |
null |
{...} |
{...} |
pod 与 container 交集;不含 host |
{} |
null |
null |
仅全部 host 工作负载 |
{} |
null |
{...} |
全部 host + 匹配的容器 |
{} |
{...} |
null |
全部 host + 匹配 Pod 的全部容器 |
{} |
{...} |
{...} |
全部 host + pod/container 交集 |
文档 Note:三者皆为默认 null
时,不做工作负载过滤。这与「全部写成
{}」在 updatePolicyFilter
里都会走到「无需 AddPolicy」的路径(匹配一切),但 YAML
可读性不同——排障时把「省略」和「显式
{}」分开看日志与意图。
flowchart TD
start["Policy_load"] --> ns{"Namespaced"}
ns -->|"yes"| pods["Pods_in_metadata_namespace"]
ns -->|"no"| hs{"hostSelector"}
hs -->|"null_defaults"| maybeAll["May_match_all_or_pod_only"]
hs -->|"empty_braces"| host["Include_all_host_workloads"]
pods --> ps["Apply_podSelector"]
maybeAll --> ps
host --> ps
ps --> cs["Apply_containerSelector"]
cs --> bpf["policyfilter_cgroup_maps"]
五、无 OCI hook:API server best-effort
k8s-filtering.md Motivation:
To ensure that namespaced tracing policies are always correctly applied, Tetragon needs to perform actions before containers start executing. Tetragon supports this via OCI runtime hooks. If such hooks are not added, Tetragon will apply policies in a best-effort manner using information from the k8s API server.
含义:
- 有 hook:容器启动前把 cgroup / 身份信息推进 policyfilter,命名空间策略从第一指令起即可生效(机制展开见第 15 篇)。
- 无 hook:依赖 API server 可见的 Pod/容器状态;短生命周期容器、启动竞态、informer 延迟会造成窗口——策略可能晚于进程已开始执行。
本篇不展开 CRI-O / containerd NRI
接线;需要强制「容器第一拍就生效」时,把第 15
篇列入必读,而不是只加 podSelector。
六、policyfilter 关闭时的行为
实现分两层:
option.Config.EnablePolicyFilter:pkg/option/flags.go默认 false(注释:代码较新故默认关)。Helm chart @ v1.7.0 的tetragon.enablePolicyFilter为 True。policyfilter.DisabledState()(pkg/policyfilter/disabled.go):AddPolicy对需要过滤的策略返回policyfilter is disabled;对 Pod 增删事件则空操作。
updatePolicyFilter(pkg/sensors/k8s.go)写明:
- 仅当确实需要过滤时才调用
AddPolicy。 - 因此 policyfilter 关闭时:不需要过滤的全局策略仍可加载;需要 namespace / selector 过滤的策略会失败。
排障对照:
| 现象 | 优先检查 |
|---|---|
| Namespaced / 带 podSelector 的策略加载失败,日志含 policyfilter is disabled | Helm/flag 是否未打开
enable-policy-filter |
| 策略加载成功但不区分 Pod | 是否三者皆 null(过滤关闭);或 policyfilter map 未跟上 Pod 事件 |
| 仅部分新 Pod 未命中 | 无 OCI hook 的 API server 窗口(第五节);informer 是否落后 |
关闭 policyfilter 不是「静默变成全匹配 Namespaced」——需要过滤时会在加载路径上显式失败。
与第 5 篇安装表交叉:若在主机上裸跑 agent 并照抄集群级
YAML 里的 podSelector,却忘记
--enable-policy-filter,加载失败是预期行为。Helm
默认打开该开关,正好掩盖「二进制默认
false」——跨安装形态搬配置时必须显式对齐。
EnablePolicyFilterCgroupMap
等更细开关本篇不展开;需要时以
pkg/option/flags.go 与运行日志中的
Enabling policy filtering 为准(官方 demo
期望出现该日志行)。
七、学术谱系、争论与开放问题
谱系:Wright et al.(USENIX Security
2002)描述的是 LSM hook
框架——内核在安全相关操作点回调模块。Tetragon 可用
lsmhooks 消费部分 hook(第 3、6
篇);policyfilter 本身不是 LSM
框架的实现,而是用 eBPF map 按 cgroup /
工作负载裁剪 TracingPolicy
生效范围。二者同属「内核侧尽早裁决」谱系,职责不同。
Garfinkel(NDSS 2003)强调用户态拦截的竞态;官方 k8s-filtering 文档用「强制执行若在用户态则应用可能已跑完 syscall」对照内核过滤。无 OCI hook 时退回 API server best-effort,等于在身份绑定上重新打开一类竞态窗口——这是工程间隙,不是文档笔误。
争论:CR 级身份便利 vs 运行时 hook 成本
| 侧 | 主张 | 代价 |
|---|---|---|
| 只依赖 API server | 部署简单,无 OCI/NRI 改造 | 启动窗口;短命容器可能漏过滤 |
| 上 OCI hook / NRI | 容器启动前绑定策略 | 运行时矩阵、fail-open/closed 语义(第 15 篇) |
开放问题:
- 标签风暴下 policyfilter 更新延迟与可观测信号——后续运维篇。
containerSelector的repo在多运行时 ImageID 格式下的稳定性——以PodContainersInfo的@分割为前提,跨 CRI 是否一致需按环境核实。nodeSelector(>v1.7.0)与现有 host/pod/container 表如何组合——第 16 篇开放项。
八、小结
TracingPolicyNamespaced只打本 namespace Pod,且不能带hostSelector。podSelector/containerSelector分层过滤;name与repo在 policyfilter 中均可用,repo依赖 ImageID 形态。hostSelector在 v1.7.0 只有null与{};无 nodeSelector。- 无 OCI hook 时身份绑定是 API server best-effort(详见第 15 篇)。
- policyfilter 默认在裸二进制上关闭、在 Helm 上打开;关闭时「需要过滤的策略加载失败」,不是静默全匹配。
不写边界(本篇)
spec.nodeSelector能力表或 live 文档的 node 门控叙述。- 每个 CRI 发行版的 OCI hook 逐步截图(第 15 篇)。
- 伪造的 Sigkill demo 终端输出(官方文档已有示例叙事,本篇不复述为自测)。
九、参考资料
规范 / 官方文档(A)
docs/content/en/docs/concepts/tracing-policy/k8s-filtering.md@ v1.7.0 — namespaced、pod/container/host、组合表、OCI hook 动机。- Tetragon v1.7.0 Release notes —
spec.hostSelector(PR #4814)。
源码(A)
pkg/k8s/apis/cilium.io/v1alpha1/tracing_policy_types.go:PodSelector、ContainerSelector、HostSelector;Namespaced 的 XValidation @ v1.7.0pkg/sensors/k8s.go:updatePolicyFilter@ v1.7.0pkg/policyfilter/state.go:containerMatches(name/repo)@ v1.7.0pkg/policyfilter/disabled.go:AddPolicy→policyfilter is disabled@ v1.7.0pkg/podhelpers/podhelpers.go:PodContainersInfo@ v1.7.0pkg/option/flags.go:KeyEnablePolicyFilter默认 false @ v1.7.0install/kubernetes/tetragon/values.yaml:enablePolicyFilter: True@ v1.7.0
核心论文
- Wright et al., Linux Security Modules…, USENIX Security 2002 — LSM hook 谱系(非 policyfilter 本身)。
- Garfinkel, Traps and Pitfalls…, NDSS 2003 — 用户态拦截竞态;对照内核过滤与无 hook 窗口。
站内对照
实验台账
- 未跑 minikube/kind demo;组合表与 YAML 摘录来自 v1.7.0 官方文档并已标明。
→ 上一篇:选择器与内核过滤 · 系列目录 · 下一篇:文件 / 网络 / 能力观测面
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Tetragon / eBPF】排障坐标系:无事件、策略不生效、导出缺口、误阻断与血缘空洞
按五轴映射 Tetragon v1.7.0 故障:无事件落在 Hook/BTF/加载,策略不生效落在 collectionKey 撞名与 policyfilter,JSON allow/deny 不等于 gRPC,Override 依赖 CONFIG_BPF_KPROBE_OVERRIDE,血缘空洞来自 execve_map 未命中与 procFS 回填。
【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 类型时的失败表象。