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

【Cilium / eBPF】策略编译:NetworkPolicy 到 BPF 与默认拒绝窗口

文章导航

分类入口
kubernetesnetwork
标签入口
#cilium#ebpf#networkpolicy#ciliumnetworkpolicy#identity#policy-map#default-deny#v1.20.0

目录

「策略已经 apply,为什么还会通 / 为什么突然全断?」在 iptables 实现里,答案常落在规则顺序与 IP 集合刷新;在 Cilium 里,答案落在 selector 解析 → 数字 Identity → per-endpoint BPF policy map 这条编译链,以及 按方向切入 default-deny 的时机。本篇只钉编译与窗口,不写策略 DSL 教程全书。

本文是「Cilium / eBPF 数据面内核」系列第 7 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 2 篇 · Endpoint 与 Identity 数字身份与标签
第 6 篇 · kube-proxy 替代 Service BPF 路径
第 7 篇 · 策略编译 NP/CNP → BPF;default-deny 窗口
第 8 篇 · Hubble 信号 flow / verdict 语义

版本锚定:Cilium v1.20.0。策略语义以 docs.cilium.io/en/v1.20/security/policy/ 与源码 pkg/policy/pkg/maps/policymap/ @ v1.20.0 为准。无集群不伪造 Hubble deny 截图。


一、编译链全景:从声明到 map 条目

一次可强制执行的策略,最少穿过四层:

对象 职责
API NetworkPolicy / CiliumNetworkPolicy / CiliumClusterwideNetworkPolicy 声明式意图
解析 agent 把 K8s/Cilium 规则译成内部 PolicyEntry 统一模型、标 default-deny 等
解析与蒸馏 selector → 当前 Identity 集合;生成 MapState 把「标签选择」变成「数字键」
数据面 per-endpoint policy map(如 cilium_policy_* TC 路径上 O(查表) 判定
flowchart LR
  api["NetworkPolicy / CNP"] --> parse["Parse to PolicyEntry"]
  parse --> resolve["Resolve selectors\nto identities"]
  resolve --> mapstate["MapState + precedence"]
  mapstate --> bpf["Endpoint policy BPF map"]
  bpf --> verdict["ALLOW / DENY / proxy_port"]

相对「标签 → IP → iptables」链,Cilium 的关键省略是:策略规则本身尽量不绑定 Pod IP。IP 仍出现在 ipcache(IP→Identity)里;策略 map 的键是 对端 Identity + 方向 + L4(协议/端口前缀)。因此同标签集合的 Pod 扩缩容时,策略条目不必为每个新 IP 重写一遍——这是 Identity 模型相对 IP 策略的核心收益(产品叙述见 k8s-network/13;机制钉在第 2 篇)。

源码锚点(tag v1.20.0):


二、Kubernetes NetworkPolicy 如何进 Cilium 模型

2.1 允许语义与 default-deny 触发

K8s NP 只有「允许」语义:被选中的 Pod 在某一方向一旦有策略,该方向进入默认拒绝,只放行显式允许的流量。Cilium 解析时把这一契约映射过来:官方 Policy Intro 写明——

ParseNetworkPolicy 路径上,K8s 规则的 verdict 为 Allow,并带 DefaultDeny = true 一类标记(与空 ingress/egress 列表如何合成 deny 的细节以源码与 K8s 语义为准)。工程含义:第一条「看起来很宽」的 NP,也可能瞬间把命名空间切进默认拒绝——若允许列表不完整,Hubble 会看到大量 Policy denied,而不是「策略没生效」。

2.2 CiliumNetworkPolicy 的增量能力

CNP 在同一编译框架上扩展:

EnableDefaultDeny 对 L7 策略的适用边界以官方 Layer-7 章为准;不要假设「所有 CNP 字段对 L3/L4/L7 同质」。


三、蒸馏:selector 变成 Identity 键

编译的中段是 CachedSelector → NumericIdentity 集合。agent 维护「哪些 Identity 满足该 selector」的缓存;标签变化、Pod 起停、ClusterMesh 远端 Identity 同步,都会使集合变动,从而触发相关 endpoint 的 map 重算。

伪过程(语义,非完整源码):

  1. 对 endpoint \(E\),收集所有选中它的规则,按方向合并为 L4Policy。
  2. 对每个 L4 filter,把对端 selector 解析成 Identity 集合 \(I\)
  3. 为每个 \(id \in I\) 生成 map 键:方向、协议、端口(可带前缀长度)、Identity。
  4. 插入 MapState 时应用优先级:Deny 压过 Allow;更具体端口压过通配;被更高优先级宽规则覆盖的窄规则可被抑制(mapstate 叙述)。

数据面查找不再扫 YAML,而是:对端 Identity(经 ipcache 由源/目的 IP 解析)+ L4 → 条目 → ALLOW / DENY / 重定向代理。

与 Service 的交界:到 ClusterIP 的策略经常要表达「允许访问某 Service」。编译必须把 Service 选择器展开为后端 Identity 或特殊实体;Service 后端滚动时,允许集合跟着 Identity 变,而不是跟着临时 IP 变。若只按 IP 写 CIDR 例外,会重新引入 IP 策略的脆弱性。


四、默认拒绝窗口:三类时间缝

「窗口」不是玄学,是控制面与数据面的时序差。

4.1 切入 default-deny 的瞬间

时刻 \(t_0\):命名空间无策略,流量全通。
时刻 \(t_1\):第一条含 ingress 的 NP 被 agent 接受并开始计算。
在 map 写完之前,可能出现:

第二种更常见于「先 apply 窄策略、后补例外」的操作顺序。工程纪律:先在非生产验证完整允许集,或使用不触发 default-deny 的可见性策略作第一刀EnableDefaultDeny)。

4.2 Identity 分配与标签风暴

Pod 创建或标签变更时,要经过:标签 → Security Identity 分配(本地或 kvstore/CRD 路径)→ ipcache 传播 → 策略 map 重算。在 Identity 尚未稳定前,对端可能被看成 reserved:world / init / 错误 Identity,策略结果与稳态不一致。

这对应系列关键问题 2:Identity 消除了 IP 刷新税,但把一致性问题移到「身份分配与传播窗口」。标签频繁抖动(控制器乱改 label)会放大窗口,表现为「偶发拒绝」。

4.3 远端与 ClusterMesh

跨集群时,远端 Identity 与 ipcache 依赖 ClusterMesh 控制面同步(第 11 篇)。同步滞后时,本地策略已引用远端 selector,但 map 中尚无对应 Identity → 该通的跨集群流被拒。这不是「策略写错」,而是 编译输入集合不完整


五、Identity 与策略的交互:值班归因表

现象 更可能机制 核对方向
apply 后立刻大面积 DROPPED 切入 default-deny,允许集不全 策略 YAML 是否覆盖 DNS/指标刮取等
仅新 Pod 被拒,旧 Pod 正常 新 Identity 未进对端 map / init Identity 第 2 篇生命周期;cilium identity get 类语义
改标签后间歇失败 旧 Identity 残留与新 Identity 并存窗口 谁还在用旧 ID 发流
同标签 Pod 行为不一致 实际安全标签集并不相同(含命名空间标签) 打印 endpoint 的 security labels
L7 规则「L4 通但业务 403」 流量进 Envoy,L7 拒绝 Hubble L7 字段;第 10 篇
CIDR 放行但 Identity 策略仍拒 多规则优先级 / Deny 优先 查是否另有 deny CNP

无伪造输出前提下,常用命令语义:

# 语义:看 endpoint 策略是否 enforcing、identity、策略来源
cilium endpoint list
# cilium endpoint get <id>
# 语义:策略相关 dump(以本机 agent 为准)
# cilium bpf policy get <endpoint-id>

六、L7 编译出口:proxy_port 不是 ALLOW

当规则含 L7 匹配时,policy map 条目可携带 proxy port:BPF 不在内核解析 HTTP,而是把连接重定向到节点上的 Envoy,由用户态做路径/方法判定。这改变了失败面:

因此「策略编译」在 L7 边界上分裂成 BPF 编译Envoy 配置下发 两条管线;只盯一张 policy map 会误判。


七、谱系、争论与开放问题

谱系:主机防火墙 / netfilter 按地址端口过滤 → SDN 与标签化策略(多系统并行演进)→ Kubernetes NetworkPolicy 以 Pod selector 表达意图但实现常退回 IP → Cilium 把 Security Identity 抬成数据面一等公民,策略编译成 Identity 键的 BPF map。学术上「基于身份的访问控制」谱系很长;工程上这里的 Identity 是 集群内安全标签等价类的数字编号,不是 SPIFFE 全文。

争论

  1. IP 策略简单性 vs Identity 一致性窗口:IP 规则 intermediate 状态好懂;Identity 稳态更稳,但分配与传播窗口是新故障类。
  2. 默认允许起步 vs 默认拒绝起步:Cilium 对齐 K8s「无策略则允许、有策略则按方向 deny」;零信任偏好者抱怨窗口过大,平台偏好者抱怨第一条策略太容易误杀——EnableDefaultDeny 是产品层缓解,不是形式化证明。
  3. Deny 优先与可理解性:显式 Deny 增强表达力,也让「为何被拒」更依赖 precedence 实现细节。

开放问题

  1. 标签风暴下 Identity 分配延迟的可观测 SLO:能否把「pending identity」暴露成与调度延迟同级的平台指标?
  2. 编译结果的差分可解释性:一次 CNP 变更影响哪些 endpoint map 条目、是否触发 default-deny 切换——仍高度依赖 agent 日志与手工 dump。

八、实体、世界流量与「看不见的依赖」

策略编译不只处理 Pod selector。Cilium 还把若干 实体(entity) 与保留 Identity(如 hostworldremote-nodeinit 等,具体集合以 v1.20 文档为准)编进同一套 map 键空间。出站访问公网、节点进程访 Pod、健康检查路径,常常依赖这些实体是否被显式允许——尤其在 egress 已切入 default-deny 之后。

典型误伤清单:

这些不是编译器 bug,而是 允许集相对真实依赖不完整。变更策略前应列出进程的实际对等体(DNS、身份提供者、旁路 sidecar),再翻译成 Identity/实体规则。toFQDN 规则还会引入 DNS 代理与 IP 缓存侧效应:解析结果写入可匹配集合的时序,本身又是一类窗口——细则见官方 FQDN 章,本篇只强调「FQDN 规则的数据面依赖 DNS 路径活着」。

审计模式(audit)下,编译仍可产生「本应拒绝」的判定,但数据面放行并记日志。它适合演练 default-deny,不能当成生产强制;把 audit 面板上的 deny 计数直接当「已拦截攻击」会高估安全水位。

同一 endpoint 上多层策略(命名空间 NP、CNP、clusterwide CNP)合并时,最终 map 是 蒸馏后的并集与优先级结果,不是「文件里 sal 靠前的规则先匹配」。排障应 dump 生效 map 或 endpoint 策略视图,而不是只 diff 最新一次 YAML。


九、本篇边界

不写:每一种 toFQDN/DNS 代理细节、Tetragon、完整 CNP 字段百科。
下一篇把 Hubble flow 字段与 metrics 分工钉死,回答「指标全绿为何仍丢包」。

一句话收束:策略编译的本质是 selector→Identity→per-endpoint map;default-deny 按方向切入,窗口来自写 map 时序、Identity 传播与跨集群同步,而不是 YAML 里多写了一个空格。

参考资料

规范 / 官方文档(A)

源码(A)

论文 / 对照(A/B)

站内

上一篇:kube-proxy 替代 · 系列目录 · 下一篇:Hubble 信号

读完这篇,下一步读什么

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

2026-08-16 · kubernetes / network

Cilium / eBPF 数据面内核:从 Identity 到包路径

补齐站内 Cilium 产品单篇与 eBPF 内核实现之上的数据面内核层:Endpoint/Identity、BPF 挂载与 Map、东西向包路径、kube-proxy 替代、策略编译、Hubble、加密与 Mesh/ClusterMesh 边界,并以排障与选型收束。


By .