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跨节点直接通信的路径:
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逻辑关系:
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段