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

【Tetragon / eBPF】K8s 身份感知策略:Namespaced、选择器与 hostSelector

文章导航

分类入口
kubernetessecurity
标签入口
#tetragon#ebpf#policyfilter#podselector#hostselector#tracingpolicynamespaced#v1.7.0

目录

选择器 在 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.mdpkg/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 篇):

文档动机写得很硬:身份感知过滤在 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 篇撞名问题在身份轴上的投影。


二、podSelectorcontainerSelector

podSelector

标准 label selector,选择策略生效的 Pod。文档示例用 matchLabels: app: lseek-test;也可使用 matchExpressions(例如按 k8s:io.kubernetes.pod.namespace 之类键——以你集群实际标签为准)。

pkg/sensors/k8s.go:若只写了 podSelectorcontainerSelector == nil,会把 containerSelector 填成空 {},表示「匹配 Pod 内全部容器」。

containerSelector

文档(v1.7.0 k8s-filtering.md)写明:当前支持字段为 namerepo(容器仓库)。

源码核对结果:

位置 说法
TracingPolicySpec.ContainerSelector 注释 「Currently, only the “name” field is supported.」
pkg/policyfilter/state.go containerMatches 构造标签映射含 namerepo,再交给 selector
pkg/podhelpers.PodContainersInfo Name ← 容器名;Repo ← 从 ImageID@ 分割取前半(digest 形式)

结论(v1.7.0):运行时 policyfilter 同时支持 namerepo;CRD 结构体注释滞后于实现与概念文档。repo 依赖 ImageID 能拆出 @ 前缀——若运行时给出的 ImageID 形态不含 @repo 可能为空字符串,选择器会匹配失败。写策略时优先用稳定的容器 name;用 repo 前先确认节点上 ImageID 格式。

官方文档示例(container name: main;摘自 k8s-filtering.md):

spec:
  containerSelector:
    matchExpressions:
      - key: name
        operator: In
        values:
        - main

containerSelectorpodSelector 的第二层:先匹配 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,非实测):

这些示例说明:「排除 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.

含义:

  1. 有 hook:容器启动前把 cgroup / 身份信息推进 policyfilter,命名空间策略从第一指令起即可生效(机制展开见第 15 篇)。
  2. 无 hook:依赖 API server 可见的 Pod/容器状态;短生命周期容器、启动竞态、informer 延迟会造成窗口——策略可能晚于进程已开始执行。

本篇不展开 CRI-O / containerd NRI 接线;需要强制「容器第一拍就生效」时,把第 15 篇列入必读,而不是只加 podSelector


六、policyfilter 关闭时的行为

实现分两层:

  1. option.Config.EnablePolicyFilterpkg/option/flags.go 默认 false(注释:代码较新故默认关)。Helm chart @ v1.7.0 的 tetragon.enablePolicyFilterTrue
  2. policyfilter.DisabledState()pkg/policyfilter/disabled.go):AddPolicy 对需要过滤的策略返回 policyfilter is disabled;对 Pod 增删事件则空操作。

updatePolicyFilterpkg/sensors/k8s.go)写明:

排障对照:

现象 优先检查
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 篇)

开放问题

  1. 标签风暴下 policyfilter 更新延迟与可观测信号——后续运维篇。
  2. containerSelectorrepo 在多运行时 ImageID 格式下的稳定性——以 PodContainersInfo@ 分割为前提,跨 CRI 是否一致需按环境核实。
  3. nodeSelector(>v1.7.0)与现有 host/pod/container 表如何组合——第 16 篇开放项。

八、小结

  1. TracingPolicyNamespaced 只打本 namespace Pod,且不能带 hostSelector
  2. podSelector / containerSelector 分层过滤;namerepo 在 policyfilter 中均可用,repo 依赖 ImageID 形态。
  3. hostSelector 在 v1.7.0 只有 null{}无 nodeSelector
  4. 无 OCI hook 时身份绑定是 API server best-effort(详见第 15 篇)。
  5. policyfilter 默认在裸二进制上关闭、在 Helm 上打开;关闭时「需要过滤的策略加载失败」,不是静默全匹配。

不写边界(本篇)


九、参考资料

规范 / 官方文档(A)

源码(A)

核心论文

站内对照

实验台账


上一篇:选择器与内核过滤 · 系列目录 · 下一篇:文件 / 网络 / 能力观测面

读完这篇,下一步读什么

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


By .