IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在第 38 篇中我们用 RBAC 解决了“谁能操作什么资源”的授权问题。但安全防护还有另一条战线网络层。默认情况下Kubernetes 集群内的所有 Pod 之间可以自由通信。这带来了一个风险一旦某个 Pod 被攻破攻击者就能以它为跳板访问集群内的所有其他服务。在 Docker Compose 时代这种风险相对可控——所有容器都在一台宿主机上你可以通过绑定特定 IP、配置自定义网络来实现基本隔离。但在 K8s 集群中Pod 可能分布在多台节点上你需要的是以应用为粒度的网络防火墙。这就是 NetworkPolicy 的使命。今天这篇我们从“零信任”的安全理念讲起拆解 NetworkPolicy 的核心三要素然后把它应用到贯穿案例的 Flask Redis 应用中——让 Redis 只接受 Flask 的访问拒绝一切其他来源的流量。一、从“全通”到“零信任”为什么需要 NetworkPolicy1.1 K8s 的默认网络模型扁平全通K8s 的网络模型有一条基本原则所有 Pod 之间可以不经过 NAT 直接通信无论它们是否在同一节点上。这极大简化了微服务间的通信——你不用配置任何路由规则Flask 就能直接访问 Redis 的 Pod IP。但这种“全通”模式也带来了安全隐患。假如你的集群中运行着以下 PodFlask 应用处理用户请求暴露在 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 插件都支持 NetworkPolicyMinikube 默认使用 Flannel不支持 NetworkPolicy。要让本篇的配置生效你需要先启用 Calico# 删除旧集群并重建Calico 需要从集群创建时配置minikube delete minikube start--cnicalico--driverdocker二、NetworkPolicy 的核心三要素NetworkPolicy 通过三个核心字段定义规则2.1 podSelector规则应用给谁podSelector通过标签选择一组 Pod作为规则的目标。例如podSelector: matchLabels: app: redis表示“对带有appredis标签的 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 的安全策略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只放行来自appflask-counter标签的 Pod 的流量。ports: 6379进一步限制只能访问 Redis 的默认端口。效果部署此规则后任何没有appflask-counter标签的 Pod 都无法连接 Redis。3.2 验证隔离效果部署一个测试 Pod 来验证规则是否生效# 启动一个 alpine 测试 Podkubectl run test-pod--imagealpine--rm-it--sh# 在测试 Pod 内安装 curl 和 redisapkaddcurlredis# 测试 1直接访问 Redis应该被拒绝redis-cli-hredis-service-p6379PING# 如果规则生效连接会超时或拒绝# 测试 2访问 FlaskFlask 没有 Ingress 规则应被拒绝curlhttp://flask-service:5000/health# 连接超时或拒绝现在从 Flask Pod 内部访问 Redis应该成功kubectlexecdeploy/flask-deployment -- redis-cli-hredis-service PING# PONG3.3 默认拒绝所有流量NetworkPolicy 的规则是累加的——多条规则的效果合并任一规则的from匹配即可放行。如果没有匹配任何 NetworkPolicy流量默认被放行。要实现“默认拒绝”需要创建一条“拒绝所有”的兜底规则apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: default spec: podSelector:{}policyTypes: - IngresspodSelector: {}表示选择命名空间中所有 Pod而空的ingress规则意味着没有任何来源被允许。这条规则与redis-allow-flask-only等显式规则共同作用——显式规则打开特定通道兜底规则关闭其他所有入口。3.4 允许 Ingress Controller 访问 Flask默认拒绝后外部的 Ingress Controller 也无法访问 Flask。需要显式放行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 完整的安全策略现在我们的应用安全策略包括应用所有策略kubectl apply-fredis-networkpolicy.yaml kubectl apply-fflask-networkpolicy.yaml kubectl apply-fdefault-deny.yaml kubectl get networkpolicy# NAME POD-SELECTOR AGE# default-deny-all none 30s# flask-allow-ingress appflask-counter 20s# redis-allow-flask-only appredis 10s四、Egress 规则控制 Pod 的出站流量除了入站流量NetworkPolicy 还可以限制 Pod 的出站流量——Pod 可以访问哪些外部服务。例如只允许 Flask Pod 访问 Redis 和 CoreDNS否则 Pod 无法解析域名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命名空间中的 CoreDNSUDP 53确保 DNS 解析正常工作其他所有出站流量如访问外部 API将被拒绝五、NetworkPolicy 排错指南NetworkPolicy 排错的核心是隔离问题范围——确认是 CNI 不支持、标签选择器写错了、还是端口/协议不对# 1. 检查 CNI 插件是否支持 NetworkPolicykubectl get pods-nkube-system|grep-Ecalico|cilium|weave# 如果有输出说明支持如果只看到 flannel需要更换 CNI# 2. 查看 NetworkPolicy 规则kubectl describe networkpolicy策略名# 3. 检查 Pod 标签是否匹配kubectl get pods --show-labels|grep关键词# 如果 Pod 的实际标签与 NetworkPolicy 的 podSelector 不匹配规则不会生效# 4. 用测试 Pod 验证连通性kubectl runtest--imagealpine--rm-it--shapkaddcurlcurlhttp://service-name:port/health --connect-timeout3# 根据超时或被拒判断策略是否生效六、对比 Docker Compose 的网络隔离Docker Compose 通过不同网络实现粗粒度隔离而 NetworkPolicy 提供了更灵活、更细粒度的网络防火墙。在 Compose 中要限制某个容器只能被特定容器访问需要精细规划网络拓扑在 K8s 中只需要用标签和选择器声明规则即可。七、命令速查表八、本篇总结NetworkPolicy 的定位K8s 原生的网络防火墙通过标签和选择器精确控制 Pod 间的流量实现“零信任”网络模型。但它只是规则定义实际执行依赖 CNI 插件Calico、Cilium 等。核心三要素podSelector目标 Pod、policyTypesIngress/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 思维