Kubernetes 网络策略
Kubernetes 网络策略
什么是 NetworkPolicy
在默认的 Kubernetes 集群中,所有 Pod 之间、Pod 与外部网络之间的网络流量是完全开放的。任何一个 Pod 都可以向集群中任何其他 Pod 发起连接,无论它们属于哪个命名空间、哪个服务。这种"默认允许一切"的网络模型虽然简化了初始部署,但在生产环境中带来了严重的安全隐患:
- 攻击面过大:一旦攻击者控制了某个 Pod,可以自由探测和攻击集群中的所有其他服务
- 横向移动风险:缺乏网络分段使得攻击者能够在集群内部自由横向移动
- 合规违规:金融、医疗等行业的安全合规要求(如 PCI-DSS、HIPAA)通常强制要求网络分段
- 数据泄露:没有网络隔离意味着开发环境的 Pod 可能意外访问生产数据库
Kubernetes NetworkPolicy 是解决这些问题的原生资源。它相当于集群内部的软件定义防火墙,通过声明式 YAML 配置定义 Pod 间的网络访问规则,实现零信任网络架构的基础组件。
NetworkPolicy 的核心限制
在深入 NetworkPolicy 之前,必须理解一个关键前提:NetworkPolicy 需要 CNI 插件支持才能生效。不是所有 CNI 插件都实现了 NetworkPolicy:
| CNI 插件 | NetworkPolicy 支持 | 备注 |
|---|---|---|
| Calico | 完整支持 | 支持 L3/L4/L7 策略,推荐 |
| Cilium | 完整支持 | 基于 eBPF,支持 L7 策略 |
| Weave Net | 完整支持 | 基本策略支持 |
| Flannel | 不支持 | 策略被静默忽略 |
| AWS VPC CNI | 部分支持 | 1.8+ 支持基础策略 |
| Canal | 完整支持 | Flannel + Calico 组合 |
如果在不支持 NetworkPolicy 的 CNI 上创建策略,Kubernetes API 不会报错,但策略不会产生任何效果。这是最容易让人掉以轻心的陷阱。
NetworkPolicy 核心概念
策略结构解析
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: example-policy
namespace: production
spec:
podSelector: {} # 选择哪些 Pod 受此策略影响
policyTypes: # 策略类型:Ingress、Egress 或两者
- Ingress
- Egress
ingress: # 入站规则(允许哪些流量进入)
- from:
- podSelector: {} # 来源 Pod 选择器
namespaceSelector: {} # 来源命名空间选择器
ipBlock: {} # 来源 IP 段
ports:
- port: 8080
protocol: TCP
egress: # 出站规则(允许哪些流量出去)
- to:
- podSelector: {}
ports:
- port: 53
protocol: UDP策略作用域
- podSelector:选择受策略影响的 Pod。留空
{}表示命名空间内所有 Pod - policyTypes:声明策略类型。如果指定了 Ingress 但 ingress 字段为空,则拒绝所有入站流量
- ingress/egress:规则之间是或的关系(并集),取任意一条规则匹配即放行
- 规则内部:from/to 中的多个选择器之间是与的关系(交集),必须同时满足
策略叠加规则
当多个 NetworkPolicy 作用于同一个 Pod 时,它们的规则取并集:
# 策略 A:允许 frontend 访问 api 的 8080 端口
---
# 策略 B:允许 monitoring 访问 api 的 9090 端口
# 最终效果:api 同时允许 frontend:8080 和 monitoring:9090
# 两个策略的允许规则合并,形成更大的允许集合实战场景与配置示例
场景一:默认拒绝所有流量(安全基线)
这是零信任网络架构的起点。为每个命名空间设置默认拒绝策略,然后按需放行:
# 默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 匹配命名空间内所有 Pod
policyTypes:
- Ingress
ingress: [] # 空 ingress 规则 = 拒绝所有入站
---
# 默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress: [] # 空 egress 规则 = 拒绝所有出站应用默认拒绝策略后,所有服务都会中断。因此需要逐条放行必要的流量。这种"白名单"模式虽然初期配置成本高,但安全性远超"黑名单"模式。
场景二:放行 DNS(必须!)
在设置 Egress 策略时,最容易犯的错误就是忘记放行 DNS。如果 Pod 无法访问 kube-dns/CoreDNS,所有基于服务名的通信都会失败:
# 放行 DNS 出站流量(通常每个需要 Egress 限制的 Pod 都需要)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
# 放行 CoreDNS 的 UDP 和 TCP 53 端口
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP场景三:微服务间精确访问控制
# 只允许 frontend 访问 api-server 的 8080 端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
protocol: TCP
---
# api-server 的出站控制:只允许访问数据库和 Redis
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress-control
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Egress
egress:
# 规则 1:允许访问 PostgreSQL
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- port: 5432
protocol: TCP
# 规则 2:允许访问 Redis
- to:
- podSelector:
matchLabels:
app: redis
ports:
- port: 6379
protocol: TCP
# 规则 3:允许 DNS(必须)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP场景四:跨命名空间访问控制
跨命名空间的 NetworkPolicy 需要注意一个关键点:namespaceSelector 只能匹配命名空间的标签,不能匹配命名空间名称。因此需要确保命名空间有正确的标签:
# 为命名空间添加标签
kubectl label namespace production kubernetes.io/metadata.name=production
kubectl label namespace monitoring kubernetes.io/metadata.name=monitoring
kubectl label namespace kube-system kubernetes.io/metadata.name=kube-system# 允许 monitoring 命名空间的 Prometheus 访问所有 Pod 的 metrics 端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-prometheus-metrics
namespace: production
spec:
podSelector:
matchLabels:
expose-metrics: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- port: 9090
protocol: TCP
---
# 允许 istio-system 命名空间的 Ingress Gateway 访问前端服务
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-gateway
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: istio-system
podSelector:
matchLabels:
app: istio-ingressgateway
ports:
- port: 8080
protocol: TCP场景五:基于 IP 段的访问控制
# 限制 Pod 只能访问特定外部 IP 段
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-external-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Egress
egress:
# 允许访问外部数据库(IP 段)
- to:
- ipBlock:
cidr: 10.0.5.0/24
ports:
- port: 5432
protocol: TCP
# 允许访问外部 API(特定 IP)
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- port: 443
protocol: TCP
# 注意:ipBlock 的 except 字段可以排除特定 IP
# - ipBlock:
# cidr: 10.0.0.0/8
# except:
# - 10.0.5.100/32 # 排除某个特定 IP场景六:多租户命名空间隔离
# 租户 A 的命名空间:允许 intra-namespace 通信
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: tenant-a
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: {} # 同一命名空间内的 Pod
egress:
- to:
- podSelector: {} # 同一命名空间内的 Pod
# 不要忘记 DNS!
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP网络策略排查与验证
排查工具
# 查看命名空间中的所有 NetworkPolicy
kubectl get networkpolicy -n production
# 查看策略详情
kubectl describe networkpolicy api-allow-frontend -n production
# 查看命名空间标签(namespaceSelector 依赖这些标签)
kubectl get namespaces --show-labels
# 检查 Pod 标签(podSelector 依赖这些标签)
kubectl get pods -n production --show-labels连通性测试
# 方法 1:使用临时 Pod 测试连通性
kubectl run test-client --image=nicolaka/netshoot --rm -it -- bash
# 在 test-client 中:
curl -v http://api-server:8080/health
nslookup api-server
telnet postgres-service 5432
# 方法 2:使用 nsenter 从节点层面检查
kubectl debug node/<node-name> --image=nicolaka/netshoot -it -- bash
# 进入节点的网络命名空间检查 iptables 规则
# 方法 3:检查 CNI 插件的策略实现
# Calico
calicoctl get networkpolicy -n production -o yaml
# Cilium
cilium policy list
cilium policy get <policy-id>常见策略不生效的原因
# 1. CNI 不支持 NetworkPolicy
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
# 确认 CNI 插件正在运行
# 2. 命名空间缺少标签
kubectl get namespace production --show-labels
# 确保 namespaceSelector 能匹配到
# 3. Pod 标签不匹配
kubectl get pods -n production -l app=api-server --show-labels
# 确保 podSelector 的 matchLabels 与 Pod 的实际标签一致
# 4. 策略叠加导致意外的放行
# 当多个策略作用于同一 Pod 时,规则取并集
# 需要检查所有作用于目标 Pod 的策略策略实施路线图
在生产环境中实施 NetworkPolicy 应该循序渐进,避免一次性部署导致大面积服务中断:
第一阶段:审计与规划
# 使用网络策略审计工具分析当前流量
# 项目:https://github.com/ahmetb/kubectx/tree/master/kubens (不适用)
# 推荐:Kubernetes 网络策略推荐器(如 Cyclops、Fairwinds Insights)
# 记录当前所有服务间的调用关系
# 可以通过 Service Mesh 或 APM 工具获取
# 生成服务依赖矩阵
# | 服务 | 需要访问的服务 | 被哪些服务访问 | 外部依赖 |
# |------|--------------|---------------|---------|
# | frontend | api-server, static-cdn | ingress-gateway | - |
# | api-server | postgres, redis | frontend, monitoring | ext-api |第二阶段:非生产环境测试
# 1. 在 staging 环境应用默认拒绝策略
kubectl apply -f network-policies/default-deny-all.yaml -n staging
# 2. 观察哪些服务中断
kubectl logs -n staging deployment/frontend --tail=100
# 3. 逐条添加放行规则
kubectl apply -f network-policies/allow-dns.yaml -n staging
kubectl apply -f network-policies/frontend-to-api.yaml -n staging
kubectl apply -f network-policies/api-to-db.yaml -n staging
# 4. 验证所有服务恢复正常
kubectl run test -n staging --image=curlimages/curl --rm -it -- curl -s http://api-server:8080/health第三阶段:生产环境灰度
# 1. 选择低风险命名空间先行实施
kubectl apply -f network-policies/default-deny-all.yaml -n production-tools
# 2. 监控 24 小时,确认无异常
# 3. 逐步推广到其他命名空间
kubectl apply -f network-policies/default-deny-all.yaml -n production
# 4. 配置告警,监控策略丢弃的流量策略管理最佳实践
文件组织结构
network-policies/
├── base/
│ ├── default-deny-ingress.yaml # 默认拒绝入站
│ ├── default-deny-egress.yaml # 默认拒绝出站
│ └── allow-dns.yaml # 全局 DNS 放行
├── production/
│ ├── frontend/
│ │ ├── allow-ingress-gateway.yaml
│ │ └── allow-api-server.yaml
│ ├── api-server/
│ │ ├── allow-frontend.yaml
│ │ ├── allow-monitoring.yaml
│ │ └── egress-db-redis.yaml
│ └── db/
│ └── allow-api-server.yaml
├── monitoring/
│ └── allow-scrape-all.yaml
└── kustomization.yaml将策略纳入 Helm Chart
# templates/network-policies.yaml
{{- range $policy := .Values.networkPolicies }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ $policy.name }}
namespace: {{ $.Release.Namespace }}
spec:
podSelector:
matchLabels:
{{- range $key, $value := $policy.podSelector }}
{{ $key }}: {{ $value }}
{{- end }}
policyTypes:
{{- range $type := $policy.policyTypes }}
- {{ $type }}
{{- end }}
{{- if $policy.ingress }}
ingress:
{{- toYaml $policy.ingress | nindent 4 }}
{{- end }}
{{- if $policy.egress }}
egress:
{{- toYaml $policy.egress | nindent 4 }}
{{- end }}
{{- end }}优点
缺点
总结
NetworkPolicy 是 Kubernetes 集群内部网络安全的核心机制。生产环境建议从默认拒绝开始,逐步按需放行。配合 Calico 或 Cilium 等 CNI 插件,并使用策略审计工具定期检查规则覆盖范围。
关键原则:
- 默认拒绝,按需放行:建立零信任网络基线
- 放行 DNS:Egress 策略中必须包含 DNS 放行规则
- 标签一致性:确保命名空间和 Pod 的标签与策略选择器匹配
- 渐进式实施:先在非生产环境验证,再逐步推广到生产
关键知识点
- NetworkPolicy 的策略作用是叠加的:多条策略的允许规则取并集
- 必须放行 DNS(kube-dns)出站流量,否则服务名解析将失败
- podSelector 留空表示匹配命名空间内所有 Pod
- 需要命名空间有正确的标签(namespaceSelector 才能匹配)
- 标准 NetworkPolicy 只支持 L3/L4 控制,L7 需要使用 Calico 或 Cilium 的扩展策略
项目落地视角
- 每个命名空间设置默认拒绝策略,然后按服务间依赖关系逐条放行
- 将 NetworkPolicy 纳入 Helm Chart 管理,随应用部署一起生效
- 建立服务间访问关系文档,作为 NetworkPolicy 规则的依据
- 策略变更需要经过安全评审,并建立变更审批流程
常见误区
- 配了 Egress 策略但忘记放行 DNS,导致所有服务名访问失败
- 以为 NetworkPolicy 可以拒绝同一 Pod 内的容器间通信(实际不行)
- 在不支持 NetworkPolicy 的 CNI(如 flannel)上创建策略,策略被静默忽略
- 多个策略叠加后产生意外的放行效果,以为策略互斥
- namespaceSelector 使用 name 字段而不是 labels 字段(标准 API 不支持按名称匹配)
进阶路线
- 学习 Calico 的 GlobalNetworkPolicy,支持更丰富的匹配条件和动作
- 掌握 Cilium 的 L7 NetworkPolicy,实现基于 HTTP 方法的访问控制
- 了解 Service Mesh(Istio AuthorizationPolicy)在 L7 层的访问控制能力
- 学习 Gatekeeper/Kyverno 策略引擎,强制要求命名空间必须配置默认拒绝策略
适用场景
- 多租户集群中不同团队需要网络隔离
- 金融、医疗等合规要求严格的行业,需要实现网络分段
- 微服务架构中需要精确控制服务间的调用关系
- 需要限制特定 Pod 访问外部网络或特定 IP 段
落地建议
- 先在测试环境实施默认拒绝策略,观察并记录所有被阻断的流量
- 使用 kubectl exec 配合 curl/telnet 验证策略是否按预期生效
- 定期用策略审计工具(如 Calico Policy Audit)检查是否存在过于宽松的规则
- 建立网络策略的版本控制和变更审批流程
排错清单
- 网络不通时,先检查是否有 NetworkPolicy 阻断,用 kubectl get networkpolicy -n [namespace]
- 确认 CNI 插件是否支持 NetworkPolicy(kubectl describe node 查看 CNI 类型)
- 跨命名空间策略不生效,检查目标命名空间是否有正确的标签
- Egress 策略生效后服务名解析失败,检查是否放行了 DNS
- 策略变更后未生效,检查是否有其他策略的并集导致意外放行
复盘问题
- 当前集群是否有默认拒绝策略?是否有未受 NetworkPolicy 保护的命名空间?
- 服务间访问是否有明确的白名单文档?NetworkPolicy 是否与文档一致?
- 上次 NetworkPolicy 变更是什么时候?是否有废弃的规则需要清理?
- 是否有自动化测试验证 NetworkPolicy 的正确性?
