写了策略,和策略被遵守,是两回事
一句话总结
kubectl apply 成功只说明策略已被保存。没有数据平面,什么都不会被阻断。
为什么必须是真正的 Cilium
前面的模块中写了好几个 CiliumNetworkPolicy。但那些实验运行的集群只加载了 CRD,所以除了 kubectl apply 成功之外,什么也没有得到验证。
本模块在 VM 中搭建真正的 Cilium,再次编写同样的策略。这样就能看到三件新东西。
1. 身份是真实的编号
用 kubectl get ciliumendpoint 同时查看身份编号和与安全相关的标签集合。即使 app 标签相同,只要 namespace 等其他安全标签不同,身份也可能不同。拥有相同安全标签集合的 endpoint 共享同一个身份。与其在策略中直接写死会变化的 Pod IP,或不保证永久不变的数字 ID,不如写入想要允许的标签所代表的含义。
基础 Kubernetes NetworkPolicy 同样支持 Pod 和命名空间选择器。并不是只有 Cilium 才提供基于标签的策略;真正值得比较的是,API 所表达的意图由哪种数据平面来强制执行,以及如何把范围扩展到 FQDN、L7 等。
2. L7 拒绝表现为 403
Cilium 要查看 HTTP,必须把数据包交给 Envoy。因此在 L7 被拒绝时,不是连接被断开,而是返回 403。
这比 L3 阻断更好。超时无法区分“是网络异常、服务器挂了,还是防火墙”,而 403 是明确的拒绝。客户端可以立刻判断是否重试,日志里也会留下原因。
3. Hubble 会说明“为什么”
hubble observe --type policy-verdict 显示每个 flow 在哪个方向得到了什么判定。没有它,就只能把二十条策略逐条删掉来找罪魁祸首。
策略不生效时的检查顺序
写了策略却没有阻断,或者不该被阻断的被阻断了,这种情况是存在的。按顺序检查。
1. 策略是否选中了该 Pod。
kubectl get cep -n <ns> <pod> -o jsonpath='{.status.identity.labels}'
kubectl describe cnp <정책> | sed -n '/Endpoint Selector/,/Ingress/p'
选择器中的标签与 endpoint 的标签必须连一个字符都不能差。app: api 与 app: api 并不相同。
2. 方向是否设对了。一次通信同时存在出站一侧和入站一侧的策略,两者都要有。必须在源端允许 egress,在目的端允许 ingress,通信才能通。只开一侧,然后困惑于“写了策略却不通”,是最常见的情况。
3. 默认阻断是否已开启。对 Cilium 来说,只要出现一条选中该 endpoint 的策略,该方向的默认行为就会变为阻断。如果挂上只写了 ingress 规则的策略,egress 保持开放;但只要写下一条 egress 规则,其余 egress 就会全部被阻断。遗漏 DNS(端口 53),导致域名解析一开始就失败,是典型例子。
egress:
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s:k8s-app: kube-dns
toPorts:
- ports: [{port: "53", protocol: UDP}]
rules:
dns: [{matchPattern: "*"}]
4. 用 Hubble 查看判定。
hubble observe --type policy-verdict --verdict DROPPED --last 20
DROPPED 那一行会显示是哪条策略在哪个方向阻断的。走到这一步,通常会回到 1–3 中的某一项。
L7 策略的代价
添加 L7 规则(rules.http)后,该流量会经过 Envoy。得失很清楚。
| L3/L4 策略 | L7 策略 | |
|---|---|---|
| 处理位置 | eBPF(内核) | Envoy(用户空间) |
| 增加的延迟 | 几乎没有 | 数百 µs 到数 ms |
| 拒绝方式 | 丢弃数据包(超时) | 403 响应 |
| 可见内容 | IP、端口 | 方法、路径、请求头 |
所以不要对所有通信都启用 L7。只放在从外部进入的边界或敏感服务前面,内部服务之间使用 L3/L4。
实际工作中真正重要的事
策略依据身份而不是 IP 来判断。所以即使重启 Pod 导致 IP 变化,策略也不会动摇,规则数量取决于身份数量而不是 Pod 数量。标签设计就是策略规模设计。
L7 拒绝以 403 的形式返回,在运维中是很大的好处。超时无法区分网络、服务器和防火墙,而 403 是明确的拒绝,客户端可以立刻判断是否重试,日志里也会留下原因。
被阻断时不要逐条删除策略,而是去问 Hubble。hubble observe --type policy-verdict 会显示在哪个方向得到了什么判定。没有它,就只能在二十条策略的集合中对罪魁祸首做二分查找。
下一项实验将亲自确认这三点。