Kubernetes 默认更强调网络连通性:如果网络插件没有额外限制,Pod 往往可以与集群中的其他 Pod 通信。NetworkPolicy 用于在三层和四层控制流量,让应用只访问真正需要的目标。
一、NetworkPolicy 能做什么
NetworkPolicy 可以控制选中 Pod 的入站(Ingress)和出站(Egress)连接,主要匹配 TCP、UDP,以及在实现支持时的 SCTP 流量。
允许来源或目标时,可以使用:
1. podSelector:按 Pod 标签选择。
2. namespaceSelector:按命名空间标签选择。
3. ipBlock:按 CIDR 网段选择。
4. ports:限制协议和端口。
它不是七层防火墙,不能原生按 HTTP URL、请求方法或用户身份授权;也不负责 TLS 加密、应用认证和内容审计。这些需求通常要结合 Ingress、Gateway、Service Mesh 或应用自身能力。
二、先确认 CNI 是否真正支持
NetworkPolicy 是 Kubernetes API,但真正执行规则的是网络插件。集群必须使用支持 NetworkPolicy 的 CNI 或策略引擎。
如果实现不支持,YAML 仍可能创建成功,却不会产生任何隔离效果。因此上线前必须查看所用 CNI 的官方文档,并通过真实连接测试验证,而不能只看:
kubectl get networkpolicy
三、默认行为与隔离触发条件
没有策略选中 Pod 时,Pod 在相应方向通常是非隔离的,也就是默认允许。
当某个 NetworkPolicy 选中 Pod,并在 policyTypes 中包含 Ingress 时,该 Pod 的入站流量开始受策略限制;包含 Egress 时,出站流量开始受限制。
多个策略不会按先后顺序覆盖,也不存在传统防火墙那种“第一条命中”。适用于同一 Pod 的允许规则会合并,最终结果是所有允许集合的并集。
一次 Pod 到 Pod 的连接要成功,源 Pod 的出站策略和目标 Pod 的入站策略都必须允许。任意一侧不允许,连接就不会建立。
四、建立命名空间默认拒绝
下面的策略选择当前命名空间内所有 Pod,并拒绝所有未明确允许的入站和出站连接:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
podSelector: {} 表示选中该命名空间中的全部 Pod。空的 ingress 和 egress 允许列表意味着没有业务流量被放行。
不要在生产命名空间里直接套用后就离开。默认拒绝可能同时阻断 DNS、监控、配置中心、数据库、外部 API 和健康检查,应先梳理依赖并准备回滚方案。
五、只允许前端访问后端
假设 backend 命名空间中的 api Pod 监听 TCP 8080,只允许 frontend 命名空间内带 app=web 标签的 Pod 访问:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: backend
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
namespaceSelector 和 podSelector 位于同一个 from 条目中,因此是 AND 关系:来源必须同时满足命名空间和 Pod 标签。
如果把它们写成两个独立的列表项,则会变成 OR 关系:允许整个 frontend 命名空间,或者允许当前策略语义下匹配 app=web 的另一组 Pod。缩进看似只差一点,授权范围可能扩大很多。
六、放行 DNS
启用默认拒绝出站后,应用经常首先出现 DNS 解析失败。需要根据集群 DNS 的实际命名空间、标签、协议和端口放行 UDP/TCP 53。
一个常见思路如下,但标签必须以你的集群为准:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
有些集群使用不同的 DNS 标签或实现,创建前应先执行:
kubectl get pods -n kube-system --show-labels
kubectl get service -n kube-system
七、使用 ipBlock 的注意事项
ipBlock 适合描述集群外网段,例如允许访问某个受控 API 网段:
ipBlock:
cidr: 203.0.113.0/24
except:
- 203.0.113.128/25
但 Service、负载均衡器和节点网络可能进行地址转换。NetworkPolicy 在 DNAT 或 SNAT 前后看到哪个地址,可能因网络插件、云环境和数据路径而不同。
不要用 Pod IP 范围替代 podSelector;Pod 是动态的,标签选择器更符合 Kubernetes 的管理方式。
八、安全上线步骤
1. 列出应用真实依赖,包括 DNS、数据库、缓存、监控、日志、镜像和外部 API。
2. 统一命名空间和 Pod 标签,避免选择器含义不清。
3. 先写允许规则,再逐步启用默认拒绝。
4. 在测试命名空间做正向与反向测试。
5. 小范围上线并观察连接失败、超时和网络插件日志。
6. 保存回滚清单,避免策略错误导致运维入口一起被阻断。
九、验证与排障
查看策略和目标标签:
kubectl get networkpolicy -A
kubectl describe networkpolicy -n production
kubectl get pod -n production --show-labels
kubectl get namespace --show-labels
从允许来源测试目标:
kubectl exec -n frontend <POD_NAME> -- \
wget -S -O- http://api.backend.svc.cluster.local:8080
再从未授权来源执行同样测试,确认它失败。只做“应该成功”的测试不够,还必须验证“应该失败”的路径确实被拒绝。
排障时按顺序检查:
1. 策略是否选中了预期 Pod。
2. namespaceSelector 与 podSelector 是 AND 还是 OR。
3. 源 Egress 和目标 Ingress 是否同时允许。
4. 端口是 Service port、targetPort 还是容器实际监听端口。
5. DNS 是否已放行。
6. CNI 是否支持并成功加载策略。
7. 云安全组、节点防火墙或 Service Mesh 是否还有额外限制。
十、NetworkPolicy 的边界
NetworkPolicy 主要解决 L3/L4 连通性,不等同于完整的零信任体系。它通常不能表达 HTTP 路径权限、用户身份、请求内容审计、数据加密或应用层速率限制。
更完整的防护还应包括:最小权限 RBAC、Pod Security、Secret 管理、镜像供应链、安全上下文、TLS、审计日志和运行时监控。
总结
NetworkPolicy 最适合采用“默认拒绝、按业务依赖精确放行”的模型。真正困难的不是 YAML 语法,而是识别真实通信关系、正确理解选择器逻辑,并通过正反测试证明策略确实生效。
参考资料:
https://kubernetes.io/docs/concepts/services-networking/network-policies/
https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/