3. 默认行为与隔离模型
理解 NetworkPolicy,最关键的一步不是记住 YAML 字段,而是先建立一个清晰的心智模型:Kubernetes 网络默认是“全通”的,而 NetworkPolicy 是叠加在 Pod 之上的“选择性隔离”机制。只有先弄清“没有策略时会发生什么”“策略一旦选中 Pod 会怎样”“如何构造默认拒绝”,才能正确设计后续的允许规则。本节围绕这三个问题展开,并给出可直接套用的默认拒绝模板。
一、未应用任何策略时的全通行为
在标准 Kubernetes 集群中,只要 CNI 插件支持 NetworkPolicy(如 Calico、Cilium、Antrea、Weave Net 等),并且命名空间内没有任何 NetworkPolicy 选中某个 Pod,那么这个 Pod 的网络行为就是完全开放的:
- 入向(Ingress)全通:集群内任意命名空间的任意 Pod、任意节点,都可以访问该 Pod 暴露的端口;如果 Service 类型为 NodePort/LoadBalancer,集群外部流量也能进入。
- 出向(Egress)全通:该 Pod 可以访问任意目标 IP、任意端口,包括其他命名空间的 Pod、集群 Service、节点网络,以及集群外部的互联网地址。
- 无状态、无日志:默认情况下不会因为“没有策略”而产生拒绝日志,流量被静默放行。
这种“全通”是 Kubernetes 的默认约定,也是很多安全事件的根源:一个被攻陷的前端 Pod 可以横向扫描并访问数据库、缓存、消息队列等后端服务。因此,生产环境通常不会停留在“无策略”状态,而是先建立隔离基线,再按需放行。
二、隔离模型:策略选中即“默认拒绝”
NetworkPolicy 的隔离语义可以概括为一句话:一旦某个 Pod 在某个方向上被至少一条策略选中,该方向就进入“默认拒绝”状态,只有策略中显式允许的流量才能通过。这里有两个容易混淆的要点:
- 方向独立:Ingress 和 Egress 分别判断。一条只写了
policyTypes: [Ingress]的策略,只隔离入向,出向仍然全通。 - 策略叠加是“并集”:如果多个策略同时选中同一个 Pod,允许的流量是所有策略允许规则的并集,不会互相覆盖或“取交集”。
因此,实现“默认拒绝”并不是靠某个特殊字段,而是靠用一条策略选中命名空间内所有 Pod,并且不写任何 allow 规则。这条策略本身不包含任何 ingress 或 egress 规则,于是所有方向都被拒绝,形成隔离基线。
三、创建命名空间级默认拒绝策略
下面给出一个可直接使用的“默认拒绝全部入向与出向”的模板。它通过 podSelector: {} 选中命名空间内所有 Pod,并通过 policyTypes 同时声明 Ingress 和 Egress:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod
spec:
podSelector: {} # 空选择器 = 选中本命名空间所有 Pod
policyTypes:
- Ingress
- Egress
# 不写 ingress / egress 规则 = 不显式允许任何流量
应用方式:
kubectl apply -f default-deny-all.yaml
kubectl get networkpolicy -n prod
kubectl describe networkpolicy default-deny-all -n prod
如果只想先隔离入向、保留出向(例如让 Pod 仍能访问外部依赖),可以把 policyTypes 改为只保留 Ingress,得到“默认拒绝入向”策略:
spec:
podSelector: {}
policyTypes:
- Ingress
反过来,只写 Egress 则只隔离出向。实际落地时,建议先上“默认拒绝入向”,观察业务是否受影响,再逐步收紧出向,避免一次性切断 DNS、镜像拉取、日志上报等必要流量。
四、默认拒绝生效后的影响与验证
策略生效后,命名空间内所有 Pod 的对应方向流量都会被拒绝,典型现象包括:
- 其他命名空间的 Pod 无法再访问本命名空间的服务端口,连接超时或被重置。
- 本命名空间 Pod 无法访问集群 DNS(kube-dns/CoreDNS),域名解析失败,表现为
no such host或解析超时。 - 本命名空间 Pod 无法访问外部 API、数据库、对象存储等,出现连接超时。
- 健康检查、探针如果来自 kubelet 所在节点,通常不受 Pod 网络策略影响,但跨节点访问可能受影响,需要结合 CNI 实现确认。
验证时不要只看“服务是否报错”,而应做方向明确的连通性测试。例如在 prod 命名空间启动一个临时 Pod,分别测试 DNS、同命名空间服务、跨命名空间服务:
kubectl run nettest -n prod --rm -it --image=busybox:1.36 -- sh
# 容器内执行:
nslookup kubernetes.default.svc.cluster.local
wget -qO- --timeout=3 http://backend.prod.svc.cluster.local:8080/healthz
wget -qO- --timeout=3 http://api.other.svc.cluster.local:8080/healthz
如果 DNS 解析失败、同命名空间服务也超时,说明默认拒绝已生效;此时需要按业务依赖逐条补充允许规则,例如先放行 DNS 出向,再放行必要的服务端口。
五、隔离模型的可视化理解
下图用状态对比的方式呈现“无策略全通”与“默认拒绝隔离”两种状态,并标出策略选中 Pod 后各方向的默认行为,帮助建立方向独立的判断直觉。
六、默认拒绝策略的落地建议
把默认拒绝作为基线,而不是一次性“封死”。推荐按以下顺序推进:
- 先观察:在非生产命名空间应用
default-deny-all,用连通性测试确认哪些依赖被切断。 - 补 DNS:几乎所有 Pod 都需要访问 CoreDNS,先放行到
kube-system命名空间的 UDP/TCP 53。 - 补必要服务:按“谁访问谁、哪个端口”逐条添加 allow 规则,例如前端到后端 8080、后端到数据库 5432。
- 再收紧出向:如果一开始只做了入向默认拒绝,确认业务稳定后再补出向默认拒绝。
- 持续验证:每次新增策略后,用临时 Pod 做正反两向测试,确保“该通的通、不该通的不通”。
需要特别强调的是:默认拒绝策略本身不区分“合法流量”和“恶意流量”,它只是把默认放行改为默认拒绝。真正的安全能力来自“默认拒绝 + 最小化显式允许”的组合。因此,default-deny-all 是起点,而不是终点;后续每一条 allow 规则,都应当能回答“哪个 Pod、访问哪个 Pod、哪个端口、为什么必须允许”这四个问题。
最后给出一个可直接复用的检查清单,用于确认默认拒绝是否按预期落地:
| 检查项 | 预期结果 | 判断方式 |
|---|---|---|
| 策略是否选中全部 Pod | podSelector 为空对象 | kubectl describe networkpolicy |
| 隔离方向是否正确 | policyTypes 含 Ingress/Egress | 查看 spec.policyTypes |
| DNS 是否可用 | 能解析集群域名 | 临时 Pod 执行 nslookup |
| 跨命名空间访问 | 未放行则超时/拒绝 | wget 目标 Service 端口 |
| 同命名空间访问 | 未放行则超时/拒绝 | 同命名空间临时 Pod 互测 |
| 出向外部访问 | 未放行则超时 | 访问外部 IP 或域名 |
掌握默认行为与隔离模型后,后续的允许规则设计就有了明确的参照系:先默认拒绝,再按业务依赖逐条放行,最终形成可审计、可维护的网络治理基线。