LabHub
学习 学习路径 课程

CCA — Cilium 认证助理

为什么添加策略反而扩大了访问范围

在 LabHub 中继续学习

一句话总结

创建更多策略文件,并不意味着通信会更加安全。必须确认限制的是哪些 Pod、哪个方向,多条 allow 规则如何合并,以及名称解析所需的独立通信是否仍然存在。本模块会通过真实请求和 Cilium 观测记录,区分安全作业后发生的两种故障。

概念图: 一句话总结 · 为什么需要它 · 工作原理 · 1. 分离 API 表达的意图与执行器

为什么需要它

蓝队负责公司内部订单 API。安全人员要求减少外部连接,于是团队把 client 的 egress 限制到 API 的 8080 端口。策略成功应用,API Pod 也处于 Ready,但应用中的订单处理停止了。直接请求 API Pod IP 能获得响应,使用 service 名称请求却会一直等待到结束。

最初,团队认为 API 故障并重启了 server,但更换 server 后结果仍然相同。这里,control group 十分重要:从同一来源访问同一 server 的同一端口,只改变地址解析路径,结果就出现差异。不要一次修改多个设置,而应先问这种差异暴露了哪项依赖。

另一个团队为了向合作组织开放 API,添加了 allow 策略。他们以为原有策略仍然存在,所以新策略只是附加审核条件。但两条 allow 策略可能变成两个入口,相当于需求写成 AND,实现却做成 OR。安全评审不是统计文件数量,而是确认谁能通过哪扇门。

工作原理

1. 分离 API 表达的意图与执行器

基础 Kubernetes NetworkPolicy 也支持 podSelector 和 namespaceSelector,并非只会列出 IP 地址。策略顶层 podSelector 选择要保护的目标,ingress 的 from 选择入站对端,egress 的 to 选择出站对端。在同一 peer 中放置 namespaceSelector 与 podSelector,表示同时符合两个条件的对端;若拆成不同列表项,则形成不同的 allow 路径。

真正控制 packet 的,是支持相应策略的网络 plugin。本实验中,Cilium 同时处理标准 NetworkPolicy 与 CiliumNetworkPolicy。因此,不能只检查 API object 已经保存,还要确认被选中的 endpoint 和真实请求结果。selector 的详细含义请与 Kubernetes 官方说明对照。

2. 名称相同的 client 也不一定具有相同 identity

Cilium identity 对应一组安全相关 label。即使蓝队 client 与红队 client 都带有 role=client,namespace 相关 label 仍然不同。不要只记数字 ID,也不要只比较一个 app label。应并排查看 CEP 的 identity.id 与 identity.labels,确认它们区分的是哪一组信息。也不能像保存应用永久客户编号那样保存该数字。Cilium 术语说明是理解这一概念的起点。

CNP 的 endpointSelector 选择该策略所在 namespace 中的目标。若要允许来自其他 namespace 的来源,必须明确表达这一边界。本模块使用注册到 Kubernetes 的 namespaced CNP,不能把这里的范围直接泛化到直接写入策略 API 的 object 或 cluster-wide 策略。请同时阅读命名空间边界指南

3. 阻断 ingress 与阻断 egress 是两件事

本环境的 policyEnforcementMode 为 default。必须区分未被任何策略选中的方向,与被限制规则选中的方向。缩小 server ingress 并不会自动把 client egress 限制到相同范围;反过来,允许访问 API 的出站通信,也不表示已经允许访问名称解析 server 的通信。

本实验会分别保留只允许 API 8080 的 client,以及同时允许 DNS 的 client。前者应继续能够直接请求 IP,但名称请求失败;后者则两种请求都应成功。恢复 DNS 时,需要指定 kube-system 中的 kube-dns endpoint,以及 UDP/TCP 53。通过开放所有 egress 来消除错误,会偏离任务范围。除了 default 之外,还存在 always、never、enableDefaultDeny 与 L7 例外,因此不要把这里看到的现象原样套用到所有设置。请在策略强制执行模式中确认适用条件。

4. 区分 allow 并集与显式 deny

union server 上已有允许蓝队 client 的 CNP。再添加一条允许红队 client 的标准 NetworkPolicy 后,就可能形成两个团队都能通过的状态。不能把新策略理解成进一步收窄原有 allow 的 filter,而应展开查看应用于同一 Pod 的全部 allow 路径。

deny server 保留上述 allow 路径,同时添加显式拒绝红队的 CNP。Cilium 的显式 deny 优先于包括标准 NetworkPolicy 在内的 allow。这与仅因没有 allow 规则而阻断的 default-deny 是不同概念。也不要扩大解释为可使用该功能 deny 特定 URL 或 FQDN。请参考显式拒绝的优先级与限制

目标 蓝队 client 蓝队 stranger 红队 client 比较目的
baseline 允许 允许 允许 确认 server 自身能够响应的 control group
native 允许 拒绝 拒绝 标准 NP 的同 namespace 选择
ingress 允许 拒绝 拒绝 CNP 的来源选择
union 允许 拒绝 允许 不同 API 的 allow 并集
deny 允许 拒绝 拒绝 优先于红队 allow 的 deny

该表是接下来要构建的隔离实验目标状态,并不表示初始状态已经应用了所有正确策略。应阅读实际策略并发出请求,逐步定位与目标不符的单元格。

现场会遇到的情况

HTTP 000 不是 server 发送的 status code。curl 未能获得 HTTP 响应码时也会显示它。不要仅凭 timeout 就断定由网络策略造成;server 未启动、地址错误和连接路径问题也会呈现相同形态。因此,应一起检查 server Ready、IP 直接访问 control group、client 位置、策略 spec 和 Hubble verdict。

只找到一行 Hubble DROPPED 还不够。观测 buffer 中也包含先前实验和其他 Pod 的请求。对齐来源 Pod 与 namespace、IP、目标端口、唯一 source port 及发生时间,就能缩小调查范围;再对照 CEP identity,还能降低混淆同名 endpoint 的风险。必须区分 TCP SYN 已转发,与实际收到 HTTP 200:前者是对传输路径的观察,后者才是应用响应。请使用 Hubble CLI 指南中的 filter 自行缩小范围。

在实际预探测中,22 个请求里有一个停在 DNS 解析阶段,因此没有任何 TCP flow。如果把它判定为 TCP 日志缺失,就是观测方式错误;该请求是在建立 API 连接之前停止的。必须结合 DNS UDP 53 drop 与直接请求 IP 的成功来判断。当时的 DNS 证据只是时间段与来源 Pod 的对照,并不是连 query ID 都关联起来的 packet 级证明。说明证据覆盖范围,也是故障报告的一部分。

下一项实验要做什么

先在准备好的隔离 VM 中确认 identity 与 control group。然后,分别为不同目标配置标准 NP 和 CNP selector、缺少 DNS 的 egress 与修复后的 egress、allow 并集和 deny 优先级。完成某一步后,也要保留之前的比较对象。最后,对照策略与观测结果编写 incident report。

实验中故意保持开放的 union 与 baseline 是用于比较的隔离目标,不能原样复制到生产环境。若要保留作业成果,必须在结束 session 前导出。本模块实验仅验证单个 VM 内的 Cilium 策略行为,并不验证 multi-cluster 连接、BGP 路由或 L7 URL 限制。