深色模式
NetworkPolicy 网络策略
摘要:本文解释 Kubernetes 默认“全通”网络模型的风险,NetworkPolicy 的入站/出站语义,为什么它必须由 CNI 实现(Flannel 默认不支持),以及生产落地默认拒绝时的 DNS 放行与回滚要点。覆盖版本:Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(
networking.k8s.io/v1,GA)。 - 工具:
kubectl。 - 前提:理解标签选择器、Service(见
service.md)、CNI(见network.md)。
背景与问题:默认全通
Kubernetes 网络默认是 “所有 Pod 可以访问所有其他 Pod”(跨命名空间也如此)。这极大方便了开发,但也意味着:一旦某个 Pod 被攻破,攻击者可以在集群内自由横向移动到数据库、密钥服务等。零信任网络的做法是反过来——默认拒绝,显式允许。
生产危险
默认全通是多数 K8s 安全事件放大损失的根因。生产集群应在关键命名空间至少启用“默认拒绝入站”。但注意:盲目启用默认拒绝出站会直接断掉 DNS,导致大面积解析失败(见下文)。
核心概念:NetworkPolicy 是“叠加”而非“全局防火墙”
- NetworkPolicy 只定义允许的规则;没有匹配任何 policy 的 Pod 仍然全通(除非有“默认拒绝”策略显式选它)。
- 一个 Pod 同时匹配多条 policy 时,效果是 OR(取并集),不会互相抵消。
- 策略由 CNI 插件落地:Calico、Cilium 原生支持;Flannel 默认不支持,需额外策略控制器
[厂商特定]。 - NetworkPolicy 是四层(TCP/UDP/SCTP) 策略。对 ICMP、ARP 等行为因插件而异、未定义。
默认拒绝:先锁后放
最经典的起步是给命名空间加“默认拒绝入站”,再按需放开:
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 空选择器 = 选中该命名空间所有 Pod
policyTypes:
- Ingress
# 没有 ingress 规则 => 拒绝所有入站1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
对称地,默认拒绝出站(谨慎,会断 DNS):
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
关键陷阱:默认拒绝出站会断 DNS
一旦启用 default-deny-egress,Pod 连 CoreDNS(UDP/TCP 53)也被拒绝,所有域名解析失败。必须显式放行 DNS,否则业务全面异常:
yaml
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: 531
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
显式允许:分层放行
在默认拒绝之上,按“前端→API→数据库”逐层放开。注意 kubernetes.io/metadata.name 是控制面自动给每个命名空间打的不可变标签,可直接用于 namespaceSelector。
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 54321
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
也可限制出口目标,例如只允许 API 访问数据库与 DNS:
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to: # 放行 DNS(见上 allow-dns 亦可)
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 531
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
厂商实现差异
| CNI | NetworkPolicy 支持 | 说明 |
|---|---|---|
| Calico | 原生,业界标准 | iptables 或 eBPF 落地 |
| Cilium | 原生,能力强 | 基于 eBPF,支持更高级策略 [厂商特定] |
| Flannel | 默认不支持 | 需叠加 Calico 策略控制器或其他方案 |
验证你的集群是否支持
创建一条策略后,用临时调试 Pod 跨命名空间/同命名空间互 ping 或 curl 测试是否真的被限制。若行为无变化,先确认 CNI 是否真正落地了策略(Flannel 用户常见踩坑)。[未实测] 建议在预发环境实测后再全量。
生产实践
- 先默认拒绝入站,谨慎处理出站:出站默认拒绝收益高但风险也高(断 DNS、断外部依赖),建议先只做入站,再逐步收紧出站。
- 按命名空间分域:给
production、staging分别设策略,避免跨环境互访。 - 借助不可变标签:用
kubernetes.io/metadata.name选命名空间,避免手建标签遗漏。 - 端口范围:可用
endPort指定连续端口(需 CNI 支持,否则只生效单个port[厂商特定])。
验证
bash
kubectl get networkpolicy -n production
kubectl describe networkpolicy allow-frontend-to-api -n production
# 跨命名空间验证连通性(用 netshoot)
kubectl run test --rm -it --image=nicolaka/netshoot -- bash
curl -m 3 http://api.production.svc:8080/ # 应仅在被允许来源时成功1
2
3
4
5
2
3
4
5
回滚与清理
bash
kubectl delete networkpolicy default-deny-ingress -n production
# 逐条删除;注意:删除策略不会中断已有连接,但会移除限制1
2
2
故障排查
- 应用突然 DNS 失败:多半是默认拒绝出站且忘了放行 53 端口。先临时删除 egress 默认拒绝验证。
- 策略“不生效”:检查 CNI 是否支持;用
kubectl describe看策略是否被 API 接受、selector 是否匹配真实标签。 - 连通性时好时坏:可能多条策略的并集放大了允许范围,审查所有匹配该 Pod 的 policy(效果是 OR)。
安全与合规
- NetworkPolicy 提供网络层微隔离,但不是认证/授权:它不校验身份,只按标签/IP 放行。
- 与 RBAC、SecurityContext 组合才能构成纵深防御(见
security.md)。
常见坑
- 忘记放行 DNS 导致全面故障(最常见)。
- 误以为 NetworkPolicy 是全局防火墙:它只是“允许叠加”,没匹配到的 Pod 仍全通。
- 在 Flannel 集群期望策略生效:默认不支持。
替代方案与权衡
- CiliumNetworkPolicy / Calico GlobalNetworkPolicy:厂商扩展,支持 L7(如按 HTTP 路径/方法)策略,能力更强但锁定厂商
[厂商特定]。 - 服务网格 mTLS:在应用层做身份加密与授权,与 NetworkPolicy 互补而非替代。
参考资料
- Kubernetes 官方文档 - Network Policies,访问日期:2026-10-08。
- Kubernetes 官方文档 - Services, Load Balancing, and Networking(默认策略示例),访问日期:2026-10-08。
- OneUptime - Default Deny Network Policies,访问日期:2026-10-08。