本章目标
这一章要真正回答下面 4 个问题:
- 为什么 Kubernetes 默认网络策略看起来偏开放::cite[240]
- NetworkPolicy 的基本语义到底怎么理解::cite[240]
- 入站与出站控制应该怎么写、怎么验证::cite[240]
- 怎样用 NetworkPolicy 做微服务隔离,而不是只写一个“默认拒绝”::cite[240]
很多团队第一次接触 NetworkPolicy,会以为它像传统防火墙一样“默认拒绝,一条条放行”。但 Kubernetes 的默认行为并不是这样。官方文档写得非常明确:默认情况下,Pod 对 ingress 是非隔离的,所有入站都允许;对 egress 也是非隔离的,所有出站都允许。只有当某个 Pod 被至少一条带相应 policyTypes 的 NetworkPolicy 选中时,它才会在该方向进入“隔离”状态。::cite[240]
这就是为什么很多人会说:Kubernetes 默认网络策略偏开放。
1. 为什么默认偏开放
1.1 Kubernetes 优先保证“先连通,再治理”
Kubernetes 的设计首先要保证 Pod 能互相通信、Service 能工作、应用能跑起来。如果默认一启动就全部 deny,新手体验和系统可用性都会很差。于是 Kubernetes 把默认策略设成了“未被策略选中时不隔离”。::cite[240]
换句话说,Kubernetes 本身不会一上来就替你做零信任,它先给你一个可联通的基础网络,再由你通过 NetworkPolicy 把流量边界一点点收紧。::cite[240]
1.2 默认开放不等于不安全,而是“你要主动声明安全边界”
这点非常重要。Kubernetes 没有默认把所有东西拦掉,不代表它不支持隔离;而是把隔离定义成一种显式声明能力。只要某个 Pod 被策略选中,它在相应方向上就不再“全部放行”,而是只允许策略中明确写出来的流量。::cite[240]
所以真正的安全误区不是“Kubernetes 默认开放”,而是“以为装了 CNI 就自动有零信任”。没有 NetworkPolicy,很多集群里的 Pod 其实是天然互通的。::cite[240]
2. NetworkPolicy 的基本语义与匹配规则
2.1 先理解一个核心原则:策略是加法,不是减法
官方文档强调,NetworkPolicy 的规则是 allow-list 语义。也就是说,策略不是去写“拒绝什么”,而是写“允许什么”。一旦 Pod 在某个方向被隔离,那么只有匹配到任一允许规则的流量才可通过,回复流量会被隐式放行。::cite[240]
这意味着你写策略时应该问自己:
- 哪些 Pod 是被保护对象?
- 谁可以访问它?
- 它可以访问谁?
- 允许哪些端口?::cite[240]
2.2 podSelector:这条策略作用到谁
每条 NetworkPolicy 都有 podSelector。它不是“谁能访问我”,而是“这条策略应用到哪些 Pod 身上”。官方文档明确说明,空的 podSelector 表示当前命名空间内所有 Pod。::cite[240]
2.3 ingress 与 egress:允许谁进、允许谁出
ingress:允许哪些来源进入被选中的 Pod。::cite[240]egress:允许被选中的 Pod 对哪些目标发起连接。::cite[240]
如果被策略选中的 Pod 在某方向上处于隔离状态,那么该方向只允许命中这些规则的流量通过。::cite[240]
2.4 namespaceSelector、podSelector、ipBlock
官方文档给出了最常见的 3 类匹配器:
podSelector:匹配同命名空间中的 Pod。::cite[240]namespaceSelector:匹配某些命名空间中的所有 Pod。::cite[240]ipBlock:匹配某段 CIDR,可以配except排除子网。::cite[240]
一个 from / to 条目里同时写 namespaceSelector 和 podSelector,表示“某些命名空间里的某些 Pod”,不是二选一。::cite[240]
2.5 ingress 与 egress 是独立隔离的
官方文档对这一点写得非常明确:Ingress 隔离与 Egress 隔离是分别声明、彼此独立的。一个 Pod 可以只隔离 ingress,也可以只隔离 egress,也可以两个方向都隔离。::cite[240]
这是初学者非常容易忽略的点。很多人写了 Ingress 策略后,误以为出站也被限制了,其实完全不是。::cite[240]
3. 实战前准备:一个可演示的微服务场景
下面我们设计一个很典型的三层微服务调用链:
frontend:前端服务,对外入口backend:业务服务,只允许 frontend 访问db:数据库服务,只允许 backend 访问
我们希望达到的隔离目标是:
- frontend 不能直接访问 db;
- backend 不能被其他无关 Pod 随便访问;
- 所有 Pod 都仍然可以做 DNS 解析;
- 整个命名空间默认收紧,只按最小权限放行。::cite[240]
4. 业务 YAML:先部署业务,再上策略
apiVersion: v1
kind: Namespace
metadata:
name: unit4-policy
labels:
project: unit4-policy
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: unit4-policy
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: registry.k8s.io/e2e-test-images/agnhost:2.39
args:
- netexec
- --http-port=8080
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: unit4-policy
spec:
selector:
app: frontend
ports:
- port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: unit4-policy
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: registry.k8s.io/e2e-test-images/agnhost:2.39
args:
- netexec
- --http-port=8080
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: unit4-policy
spec:
selector:
app: backend
ports:
- port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
namespace: unit4-policy
spec:
replicas: 1
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: db
image: registry.k8s.io/e2e-test-images/agnhost:2.39
args:
- netexec
- --http-port=3306
ports:
- containerPort: 3306
---
apiVersion: v1
kind: Service
metadata:
name: db
namespace: unit4-policy
spec:
selector:
app: db
ports:
- port: 3306
targetPort: 3306
4.1 先验证“默认全通”
kubectl apply -f app-demo.yaml
kubectl get pod -n unit4-policy -o wide
然后找出 frontend Pod 名称,验证它是否可以直接访问 backend 和 db:
kubectl exec -n unit4-policy deploy/frontend -- wget -qO- http://backend:8080/hostname
kubectl exec -n unit4-policy deploy/frontend -- wget -qO- http://db:3306/hostname
在还没有策略时,这两次请求大概率都能成功,因为默认 ingress/egress 都不隔离。::cite[240]
5. 第一步:做一个命名空间内默认拒绝
5.1 默认拒绝策略 YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: unit4-policy
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
5.2 这条策略的含义
podSelector: {}表示选中当前命名空间所有 Pod。::cite[240]- 同时声明
Ingress和Egress,表示两个方向都进入隔离状态。::cite[240] - 由于没有写任何允许规则,所以最终效果就是“全拒绝”。::cite[240]
5.3 验证默认拒绝
kubectl apply -f default-deny.yaml
kubectl exec -n unit4-policy deploy/frontend -- wget -T 3 -qO- http://backend:8080/hostname
kubectl exec -n unit4-policy deploy/frontend -- wget -T 3 -qO- http://db:3306/hostname
此时请求应该超时或失败。做到这一步,你已经把这个命名空间从“天然互通”切换成了“默认不通,按需开洞”。
6. 第二步:先恢复 DNS,否则很多服务名都解析不了
默认拒绝以后,经常第一件坏掉的事不是业务调用,而是 DNS。因为 Pod 的出站也被封掉了,无法访问 CoreDNS。
6.1 允许 DNS 出站策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: unit4-policy
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
6.2 验证 DNS 恢复
kubectl apply -f allow-dns-egress.yaml
kubectl exec -n unit4-policy deploy/frontend -- nslookup backend
如果 nslookup 恢复正常,但 wget 仍然失败,说明你的 DNS 通了,而业务流量仍在被正确拦截。这是符合预期的。
7. 第三步:只允许 frontend 访问 backend
7.1 backend 入站放行策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: unit4-policy
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
7.2 frontend 出站放行策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-egress-backend
namespace: unit4-policy
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 8080
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
7.3 验证结果
kubectl apply -f allow-frontend-to-backend.yaml
kubectl apply -f frontend-egress-backend.yaml
kubectl exec -n unit4-policy deploy/frontend -- wget -qO- http://backend:8080/hostname
kubectl exec -n unit4-policy deploy/frontend -- wget -T 3 -qO- http://db:3306/hostname
预期结果应该是:
- frontend -> backend 成功
- frontend -> db 失败
这就是最典型的“最小权限”隔离效果。
8. 第四步:只允许 backend 访问 db
8.1 db 入站放行策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-db
namespace: unit4-policy
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3306
8.2 backend 出站放行策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress-db
namespace: unit4-policy
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: db
ports:
- protocol: TCP
port: 3306
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
8.3 验证结果
kubectl apply -f allow-backend-to-db.yaml
kubectl apply -f backend-egress-db.yaml
kubectl exec -n unit4-policy deploy/backend -- wget -qO- http://db:3306/hostname
kubectl exec -n unit4-policy deploy/frontend -- wget -T 3 -qO- http://db:3306/hostname
预期结果:
- backend -> db 成功
- frontend -> db 仍然失败
这时你就得到了一个最小可用的三层微服务隔离模型。
9. 读懂这组策略:它体现了哪些规则语义
通过刚才的例子,你应该已经把官方文档里的几个抽象概念真正落地了:
podSelector决定“保护对象是谁”。::cite[240]from/to决定“谁可以进、谁可以出”。::cite[240]ports决定“只能走哪些端口”。::cite[240]- ingress 与 egress 是独立声明的,所以双向限制时通常要成对写。::cite[240]
- 多条策略最终是并集效果,只要命中任一允许规则即可放通。::cite[240]
这也是为什么 NetworkPolicy 设计很适合微服务场景:它不要求你写复杂的拒绝链,而是按应用边界去描述“允许关系”。::cite[240]
10. 微服务隔离的实战建议
10.1 不要只写 default deny 就结束
很多团队做到“默认拒绝”就以为完成了安全治理,结果业务全部不可用。真正有价值的做法是:
- 先定义默认拒绝;
- 立即恢复 DNS;
- 再按调用链逐跳开放。::cite[240]
10.2 以“调用关系”而不是“机器列表”来写策略
在 Kubernetes 里,Pod 会漂移、IP 会变化,所以策略应该基于 label 和 namespace 写,而不是基于某个临时 IP。ipBlock 更适合处理集群外部网段或一些固定来源。::cite[240]
10.3 先做命名空间边界,再做服务边界
一个很实用的落地顺序是:
- 先给命名空间加默认拒绝;
- 再放开核心系统依赖(如 DNS);
- 然后按服务角色逐层放行。::cite[240]
这样你的网络策略会更可维护,也更接近真实微服务架构的权限边界。
10.4 别忘了前提:你的 CNI 必须支持 NetworkPolicy
官方文档特别提醒:只有当你使用的网络方案支持 NetworkPolicy 时,创建 NetworkPolicy 才会生效;如果底层没有对应实现,YAML 虽然能创建成功,但不会真正拦截流量。::cite[240]
这也是为什么前一章做 CNI 选型时,是否支持 NetworkPolicy 是非常关键的考量点。
11. 本章小结
这一章最重要的结论有 4 个:
- Kubernetes 默认网络策略偏开放,Ingress/Egress 默认都是不隔离。::cite[240]
- NetworkPolicy 是 allow-list 语义,重点是声明“允许谁”,不是“拒绝谁”。::cite[240]
- Ingress 与 Egress 是独立隔离的,做双向控制时通常要写成对策略。::cite[240]
- 微服务隔离的正确姿势是:默认拒绝 + 恢复基础依赖 + 按调用链逐步放行。::cite[240]
如果你把这一章的示例跑通,就已经跨过了“会写 NetworkPolicy YAML”和“真的理解网络隔离模型”之间最关键的一步。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!