IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在第 38 篇中,我们用 RBAC 解决了"谁能操作什么资源"的授权问题。但安全防护还有另一条战线:网络层。默认情况下,Kubernetes 集群内的所有 Pod 之间可以自由通信。这带来了一个风险:一旦某个 Pod 被攻破,攻击者就能以它为跳板,访问集群内的所有其他服务。
在 Docker Compose 时代,这种风险相对可控------所有容器都在一台宿主机上,你可以通过绑定特定 IP、配置自定义网络来实现基本隔离。但在 K8s 集群中,Pod 可能分布在多台节点上,你需要的是以应用为粒度的网络防火墙。这就是 NetworkPolicy 的使命。
今天这篇,我们从"零信任"的安全理念讲起,拆解 NetworkPolicy 的核心三要素,然后把它应用到贯穿案例的 Flask + Redis 应用中------让 Redis 只接受 Flask 的访问,拒绝一切其他来源的流量。
一、从"全通"到"零信任":为什么需要 NetworkPolicy?
1.1 K8s 的默认网络模型:扁平全通
K8s 的网络模型有一条基本原则:所有 Pod 之间可以不经过 NAT 直接通信,无论它们是否在同一节点上。这极大简化了微服务间的通信------你不用配置任何路由规则,Flask 就能直接访问 Redis 的 Pod IP。
但这种"全通"模式也带来了安全隐患。假如你的集群中运行着以下 Pod:
-
Flask 应用(处理用户请求,暴露在 Ingress 之后)
-
Redis(存储缓存数据,仅应被 Flask 访问)
-
后台批处理任务(连接外部 API,不应访问 Redis)
默认情况下,这三个 Pod 之间可以任意互通。 如果 Flask 应用存在安全漏洞(比如 RCE 远程代码执行),攻击者就能通过 Flask Pod 直接连接到 Redis,读取或删除所有缓存数据。
1.2 Docker 网络隔离 vs K8s NetworkPolicy
回顾第 8 篇 Docker 网络入门,我们通过自定义 bridge 网络实现了容器间的隔离------不在同一网络的容器无法互相访问。但这种隔离粒度太粗了:它基于"网络"而非"应用标签"。
NetworkPolicy 提供了更精细的控制:你可以基于 Pod 的标签(Label)和命名空间(Namespace),精确控制哪些 Pod 可以访问哪些 Pod,以及允许哪些端口和协议。这就是"零信任"安全模型在 K8s 中的实现------默认拒绝一切流量,只显式允许必要的通信。
1.3 NetworkPolicy 的前置条件
重要提醒:NetworkPolicy 只是"规则定义",它需要**网络插件(CNI)**来实际执行。不是所有 CNI 插件都支持 NetworkPolicy:
Minikube 默认使用 Flannel,不支持 NetworkPolicy。要让本篇的配置生效,你需要先启用 Calico:
bash
# 删除旧集群并重建(Calico 需要从集群创建时配置)
minikube delete
minikube start --cni=calico --driver=docker
二、NetworkPolicy 的核心三要素
NetworkPolicy 通过三个核心字段定义规则:
2.1 podSelector:规则应用给谁?
podSelector 通过标签选择一组 Pod,作为规则的目标 。例如 podSelector: matchLabels: app: redis 表示"对带有 app=redis 标签的 Pod 应用此规则"。
2.2 policyTypes:控制哪个方向的流量?
如果只写 policyTypes: [Ingress],则只控制入站流量,不限制出站流量(出站全放通)。
2.3 ingress/egress:具体规则
规则的 from 字段定义流量来源(入站),to 字段定义流量目的(出站),每个来源/目的可以通过三种方式指定:
规则生效逻辑 :from 中的多个选择器是 OR 关系 (任一匹配即放行),但选择器内部的多个条件(如 podSelector 和 namespaceSelector 同时出现)是 AND 关系(必须同时满足)。
三、实战:为 Flask + Redis 配置 NetworkPolicy
现在我们为贯穿案例的 Flask + Redis 应用配置网络隔离策略。核心目标是:
-
Redis 只能被 Flask Pod 访问(拒绝其他所有 Pod)。
-
Flask 只能被 Ingress Controller 访问(拒绝直接的 Pod-to-Pod 请求)。
-
禁止所有未声明的流量(默认拒绝)。
3.1 场景:只允许 Flask 访问 Redis
先配置 Redis 的安全策略:
bash
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-allow-flask-only
namespace: default
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: flask-counter
ports:
- protocol: TCP
port: 6379
规则解读:
-
podSelector: app: redis:本规则应用于 Redis Pod。 -
policyTypes: [Ingress]:控制入站流量。 -
from: podSelector: app: flask-counter:只放行来自app=flask-counter标签的 Pod 的流量。 -
ports: 6379:进一步限制只能访问 Redis 的默认端口。
效果 :部署此规则后,任何没有 app=flask-counter 标签的 Pod 都无法连接 Redis。
3.2 验证隔离效果
部署一个测试 Pod 来验证规则是否生效:
bash
# 启动一个 alpine 测试 Pod
kubectl run test-pod --image=alpine --rm -it -- sh
# 在测试 Pod 内安装 curl 和 redis
apk add curl redis
# 测试 1:直接访问 Redis(应该被拒绝)
redis-cli -h redis-service -p 6379 PING
# 如果规则生效,连接会超时或拒绝
# 测试 2:访问 Flask(Flask 没有 Ingress 规则,应被拒绝)
curl http://flask-service:5000/health
# 连接超时或拒绝
现在从 Flask Pod 内部访问 Redis(应该成功):
bash
kubectl exec deploy/flask-deployment -- redis-cli -h redis-service PING
# PONG
3.3 默认拒绝所有流量
NetworkPolicy 的规则是累加 的------多条规则的效果合并,任一规则的 from 匹配即可放行。如果没有匹配任何 NetworkPolicy,流量默认被放行。要实现"默认拒绝",需要创建一条"拒绝所有"的兜底规则:
bash
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
podSelector: {} 表示选择命名空间中所有 Pod ,而空的 ingress 规则意味着没有任何来源被允许。这条规则与 redis-allow-flask-only 等显式规则共同作用------显式规则打开特定通道,兜底规则关闭其他所有入口。
3.4 允许 Ingress Controller 访问 Flask
默认拒绝后,外部的 Ingress Controller 也无法访问 Flask。需要显式放行:
bash
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: flask-allow-ingress
namespace: default
spec:
podSelector:
matchLabels:
app: flask-counter
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 5000
这里使用 namespaceSelector 选择了 ingress-nginx 命名空间中的所有 Pod,允许它们访问 Flask 的 5000 端口。
3.5 完整的安全策略
现在我们的应用安全策略包括:
应用所有策略:
bash
kubectl apply -f redis-networkpolicy.yaml
kubectl apply -f flask-networkpolicy.yaml
kubectl apply -f default-deny.yaml
kubectl get networkpolicy
# NAME POD-SELECTOR AGE
# default-deny-all <none> 30s
# flask-allow-ingress app=flask-counter 20s
# redis-allow-flask-only app=redis 10s
四、Egress 规则:控制 Pod 的出站流量
除了入站流量,NetworkPolicy 还可以限制 Pod 的出站流量------Pod 可以访问哪些外部服务。
例如,只允许 Flask Pod 访问 Redis 和 CoreDNS(否则 Pod 无法解析域名):
bash
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: flask-egress
namespace: default
spec:
podSelector:
matchLabels:
app: flask-counter
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
规则解读:
-
允许 Flask 访问 Redis 的 6379 端口(TCP)
-
允许 Flask 访问
kube-system命名空间中的 CoreDNS(UDP 53),确保 DNS 解析正常工作 -
其他所有出站流量(如访问外部 API)将被拒绝
五、NetworkPolicy 排错指南
NetworkPolicy 排错的核心是隔离问题范围------确认是 CNI 不支持、标签选择器写错了、还是端口/协议不对:
bash
# 1. 检查 CNI 插件是否支持 NetworkPolicy
kubectl get pods -n kube-system | grep -E "calico|cilium|weave"
# 如果有输出,说明支持;如果只看到 flannel,需要更换 CNI
# 2. 查看 NetworkPolicy 规则
kubectl describe networkpolicy <策略名>
# 3. 检查 Pod 标签是否匹配
kubectl get pods --show-labels | grep <关键词>
# 如果 Pod 的实际标签与 NetworkPolicy 的 podSelector 不匹配,规则不会生效
# 4. 用测试 Pod 验证连通性
kubectl run test --image=alpine --rm -it -- sh
apk add curl
curl http://<service-name>:<port>/health --connect-timeout 3
# 根据超时或被拒判断策略是否生效
六、对比 Docker Compose 的网络隔离
Docker Compose 通过不同网络实现粗粒度隔离,而 NetworkPolicy 提供了更灵活、更细粒度的网络防火墙。在 Compose 中要限制某个容器只能被特定容器访问,需要精细规划网络拓扑;在 K8s 中,只需要用标签和选择器声明规则即可。
七、命令速查表
八、本篇总结
-
NetworkPolicy 的定位:K8s 原生的网络防火墙,通过标签和选择器精确控制 Pod 间的流量,实现"零信任"网络模型。但它只是规则定义,实际执行依赖 CNI 插件(Calico、Cilium 等)。
-
核心三要素 :
podSelector(目标 Pod)、policyTypes(Ingress/Egress)、ingress/egress(具体规则),规则中通过 podSelector、namespaceSelector、ipBlock 三种方式指定流量来源或目的。 -
实战成果:为 Flask + Redis 应用配置了最小权限的网络策略------Redis 只接受 Flask 的连接,Flask 只接受 Ingress Controller 的流量,并设置默认拒绝兜底规则。
-
安全最佳实践:生产环境应为每个命名空间配置默认拒绝策略,然后逐项放行必要的流量。这对应了 RBAC 中的"最小权限原则",两者共同构成 K8s 安全的两道防线。
下一篇------第 44 篇:实战:将 Web 应用迁移到 Kubernetes(上),我们将把贯穿案例的 Flask + Redis 计数器应用从 Compose 彻底迁移到 K8s 集群,用 Deployment、Service、ConfigMap、Secret、PVC 等核心对象完成完整的应用部署。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !