1. NetworkPolicy治理全景

在Kubernetes里,Pod之间默认是“全通”的。只要两个Pod的IP可达,无论它们属于哪个命名空间、承载什么业务,网络层都不会主动阻拦。这种设计让服务发现和横向扩展变得简单,但也意味着一旦某个Pod被攻破,攻击者可以沿着集群内网自由探测其他工作负载。NetworkPolicy正是为这种“默认开放”模型补上的一道策略闸门。

Kubernetes网络模型:先看清默认行为

Kubernetes网络模型有三条基本约定:每个Pod拥有独立IP;所有Pod之间无需NAT即可直接通信;节点与Pod之间也无需NAT即可互通。这三条约定由CNI插件(如Calico、Cilium、Flannel等)落地实现。需要特别注意的是,Kubernetes本身不负责实施网络隔离,它只定义了NetworkPolicy这种API对象,真正的包过滤动作由支持NetworkPolicy的CNI插件完成。如果集群使用的CNI插件不支持NetworkPolicy,那么即使创建了策略对象,也不会有任何实际效果。

因此,理解治理全景的第一步,是区分“策略声明”和“策略执行”两个层面:API Server负责存储和校验NetworkPolicy对象,CNI插件负责把它翻译成iptables、eBPF或其它数据面规则。二者缺一不可。

NetworkPolicy 治理全景:声明层 → 执行层 → 数据面 声明层(控制面) NetworkPolicy API 对象 podSelector / namespaceSelector ingress / egress 规则 执行层(CNI 插件) Calico / Cilium / Antrea 策略翻译与下发 iptables / eBPF 规则 数据面(Pod 网络) Pod A → Pod B 流量 允许 / 拒绝 判定 连接建立或超时 未应用 NetworkPolicy:默认全通 Pod A Pod B 任意端口 / 任意协议 Pod C 横向移动风险:攻击者可自由探测 应用 NetworkPolicy:按需放行 Pod A Pod B 仅 TCP 8080 放行 Pod C 未匹配流量被拒绝,攻击面收敛

NetworkPolicy的定位:不是防火墙,而是“Pod级访问契约”

NetworkPolicy常被类比为防火墙,但它和传统防火墙有本质区别。传统防火墙通常绑定在物理接口或网段上,而NetworkPolicy的选择器(selector)直接指向Pod标签。这意味着策略的粒度可以细到“只允许带有 app=frontend 标签的Pod访问带有 app=backend 标签的Pod的TCP 8080端口”。

另一个关键定位是:NetworkPolicy是命名空间级别的对象。它只能选择同一命名空间内的Pod作为策略作用对象,但可以通过 namespaceSelector 允许来自其他命名空间的流量。这种设计让多租户隔离成为可能:每个团队在自己的命名空间内定义策略,互不干扰。

还有一点容易被忽略:NetworkPolicy是白名单模型。一旦某个Pod被至少一条策略选中,该Pod在策略指定的方向上就变为“默认拒绝”,只有显式允许的流量才能通过。如果没有任何策略选中它,它仍然保持默认全通。这个“选中即收紧”的语义,是理解所有策略行为的基础。

治理目标:从“能通”到“该通才通”

NetworkPolicy治理的核心目标可以归纳为四个层次:

  • 最小权限:每个工作负载只接受业务必需的入站流量,只发起业务必需的出站流量。例如前端Pod只接受来自Ingress Controller的流量,后端Pod只接受来自前端的流量。
  • 租户隔离:不同命名空间之间默认不互通,只有明确声明的跨命名空间依赖才被放行。这能有效防止一个租户的故障或攻击波及另一个租户。
  • 爆炸半径控制:即使某个Pod被攻破,攻击者能触达的其他Pod也被限制在策略允许的范围内,无法在集群内横向扫描。
  • 合规与审计:策略以声明式YAML存储在etcd中,可以纳入GitOps流程,实现变更可追溯、可审查、可回滚。

核心挑战:策略落地时真正难在哪里

理解了目标和定位之后,实际落地时还会遇到几类典型挑战。

第一,CNI插件能力差异。并非所有CNI都完整支持NetworkPolicy的全部字段。例如,某些插件不支持 ipBlock 中的 except 子句,或者对 endPort 的支持不完整。在编写策略前,必须确认集群CNI的兼容性矩阵。

第二,策略语义的“隐式拒绝”容易误伤。假设命名空间中有三条策略分别选中了Pod A、Pod B、Pod C,但Pod D没有被任何策略选中。此时Pod D仍然全通,而Pod A却因为被选中而只允许策略中声明的流量。这种“部分收紧”的状态如果缺乏全局视图,很容易造成“为什么A能通D,D却连不上A”的困惑。

第三,DNS与出站策略的冲突。如果为Pod设置了严格的egress策略,只允许访问特定后端,却忘记放行kube-dns的UDP/TCP 53端口,Pod内的域名解析会直接失败。这类问题在排障时往往表现为“连接超时”,而不是“连接被拒绝”,增加了定位难度。

第四,策略数量增长后的可维护性。当命名空间内策略达到几十条时,选择器之间的交叉、优先级(Kubernetes中NetworkPolicy是并集关系,没有优先级)和命名规范会迅速变得难以管理。此时需要引入命名约定、标签规范和自动化校验。

实践提示:在正式应用策略前,先用 kubectl describe networkpolicy 确认策略的选择器匹配到了预期的Pod。可以先用“默认拒绝所有入站”的策略在测试命名空间验证,再逐步添加允许规则,避免一次性收紧导致业务中断。

一个最小可用的策略示例

下面这条策略的作用是:在 production 命名空间中,选中带有 app=api 标签的Pod,只允许来自同一命名空间中带有 app=frontend 标签的Pod访问其TCP 8080端口,其余入站流量全部拒绝。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

应用后,可以用一个带有 app=frontend 标签的临时Pod执行 curl api:8080 验证放行,再用一个不带该标签的Pod执行同样命令,预期会看到连接超时或被拒绝。这种“正反双向验证”是确认策略生效的基本方法。

总结来看,NetworkPolicy治理不是简单地写几条YAML,而是要在理解Kubernetes默认网络模型的基础上,明确“声明层—执行层—数据面”的完整链路,认清白名单语义和命名空间边界,再结合CNI能力、DNS依赖和策略规模,逐步构建起可维护、可验证的Pod级访问控制体系。