3. 网络策略(NetworkPolicy)基础:Pod网络模型、Ingress与Egress规则、策略选择器

在Kubernetes多租户隔离集群中,NetworkPolicy是控制Pod间流量、实现微隔离的核心资源。理解其底层网络模型与规则语法,是构建安全租户边界的前提。本章从Pod网络通信模型出发,逐步拆解Ingress/Egress规则与选择器机制。

3.1 Pod网络模型:扁平IP与无NAT通信

Kubernetes要求所有Pod在集群内拥有唯一IP地址,且Pod之间可以直接通信,无需NAT转换。这一模型由CNI(Container Network Interface)插件实现,常见实现包括Calico、Cilium、Flannel等。

特性 说明
Pod IP 每个Pod分配一个集群内可路由的IP,节点间Pod可直接访问
无NAT Pod到Pod的通信不经过源地址或目标地址转换
CNI插件 负责分配IP、配置路由、实现网络策略(部分插件支持)
Service抽象 通过ClusterIP/NodePort/LB暴露Pod,但NetworkPolicy作用于Pod IP层面
关键理解:NetworkPolicy工作在L3/L4层,直接作用于Pod的原始IP和端口。即使Pod通过Service暴露,策略仍基于后端Pod IP生效。

下图展示了Kubernetes扁平网络模型中,Pod跨节点直接通信的路径:

Kubernetes 扁平网络模型 — Pod 跨节点直接通信 Node 1 (10.0.1.0/24) Pod A 10.0.1.5:8080 cni0 bridge / veth pair Node 2 (10.0.2.0/24) Pod B 10.0.2.9:9090 cni0 bridge / veth pair 直接路由(无NAT) Pod IP 在整个集群内唯一且可路由,CNI 负责跨节点转发

3.2 NetworkPolicy 核心概念与规则结构

NetworkPolicy 是一个白名单机制:未显式允许的流量默认被拒绝。策略通过 spec.podSelector 选定目标Pod,再通过 ingress 和 egress 规则定义允许的流量。

默认行为:如果命名空间中没有任何NetworkPolicy,所有Pod间流量均被允许。一旦存在至少一条策略,未匹配的流量将被拒绝(取决于CNI实现,Calico/Cilium均支持默认拒绝)。

3.2.1 策略结构总览

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:              # 目标Pod:app=backend
    matchLabels:
      app: backend
  policyTypes:              # 声明生效方向(可选,默认根据规则推断)
    - Ingress
    - Egress
  ingress:                  # 入站规则
    - from:
        - podSelector:      # 来源:app=frontend
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 3306
  egress:                   # 出站规则
    - to:
        - namespaceSelector: # 目标命名空间:name=database
            matchLabels:
              name: database
      ports:
        - protocol: TCP
          port: 5432

3.3 选择器详解:PodSelector / NamespaceSelector

选择器决定了策略的作用范围和流量来源/目标。理解选择器的组合逻辑是编写精准策略的关键。

选择器类型 作用域 匹配方式 典型场景
podSelector 当前命名空间 按Pod标签匹配 微服务间白名单
namespaceSelector 跨命名空间 按命名空间标签匹配 多租户隔离、共享服务
ipBlock 集群外CIDR 按IP范围匹配 外部监控、CI/CD接入
常见误区:namespaceSelector 匹配的是命名空间对象上的标签(如 name: production),而不是Pod的标签。如果需要同时限制命名空间和Pod,必须使用 namespaceSelector + podSelector 嵌套组合。

3.3.1 选择器组合逻辑

在 from 或 to 列表中,多个选择器之间是OR关系;同一个选择器内的 matchLabels 多个键值对是AND关系。

# 示例:允许来自两个不同来源的流量
ingress:
  - from:
      - podSelector:           # 来源A:app=frontend(当前ns)
          matchLabels:
            app: frontend
      - namespaceSelector:     # 来源B:整个monitoring命名空间
          matchLabels:
            name: monitoring
    ports:
      - port: 8080

下图展示了选择器组合的OR/AND逻辑关系:

NetworkPolicy 选择器组合逻辑(OR / AND) NetworkPolicy 目标:app=backend from[0]: podSelector app=frontend (AND) from[1]: namespaceSelector name=monitoring (AND) OR 匹配结果:允许来自 frontend Pod 或 monitoring 命名空间的流量 同一 from 列表内多个选择器是 OR;同一 matchLabels 内多个标签是 AND 例如:app=frontend AND version=v2 必须同时满足

3.4 Ingress 与 Egress 规则详解

3.4.1 Ingress(入站规则)

Ingress 规则控制谁可以访问目标Pod。每个 from 条目可以包含以下选择器之一或组合:

  • podSelector:允许同一命名空间内特定标签的Pod访问
  • namespaceSelector:允许特定命名空间中的所有Pod访问
  • namespaceSelector + podSelector:允许特定命名空间中特定标签的Pod访问(跨命名空间精细控制)
  • ipBlock:允许来自集群外特定CIDR的流量(如监控系统、CI/CD)
# 跨命名空间精细控制:只允许monitoring命名空间中app=prometheus的Pod访问
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            name: monitoring
        podSelector:
          matchLabels:
            app: prometheus
    ports:
      - port: 9090

3.4.2 Egress(出站规则)

Egress 规则控制目标Pod可以访问谁。语法与Ingress对称,使用 to 字段:

  • podSelector:允许访问同一命名空间内特定标签的Pod
  • namespaceSelector:允许访问特定命名空间中的所有Pod
  • namespaceSelector + podSelector:允许访问特定命名空间中特定标签的Pod
  • ipBlock:允许访问集群外特定CIDR(如外部API、DNS)
最佳实践:在多租户场景中,建议为每个租户的命名空间设置默认拒绝出站策略,然后显式放行必要的DNS(端口53 UDP/TCP)和租户内通信。这可以防止数据外泄。

3.5 策略生效顺序与常见陷阱

陷阱 说明 解决方案
默认允许 无策略时所有流量允许,容易忽略安全基线 部署默认拒绝策略(选择器:{} 匹配所有Pod)
选择器空值 podSelector: {} 匹配命名空间内所有Pod 明确使用 matchLabels 或空选择器
端口未指定 不指定ports时,允许所有端口的流量 始终显式声明端口,最小权限原则
Egress遗漏DNS 出站策略未放行DNS导致域名解析失败 添加 kube-dns 的Egress规则(端口53)
策略顺序无关 多条策略之间是OR关系,不是顺序匹配 任何一条策略允许即放行,无需担心顺序

3.6 实战:编写一个完整的隔离策略

以下示例为 production 命名空间中的 app=web Pod 定义精细化的入站和出站规则:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-isolation
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
        - podSelector:
            matchLabels:
              app: health-check
      ports:
        - protocol: TCP
          port: 80
        - protocol: TCP
          port: 443
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: database
          podSelector:
            matchLabels:
              app: mysql
      ports:
        - protocol: TCP
          port: 3306
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - protocol: UDP
          port: 53
策略解读:
• 入站:只允许 ingress-nginx 命名空间和本命名空间内 app=health-check 的Pod访问80/443端口
• 出站:只允许访问 database 命名空间中 app=mysql 的Pod的3306端口,以及集群外DNS(UDP 53),但禁止访问私有IP段