第 43 篇 k8s之集群网络策略:NetworkPolicy 入门

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 关系 (任一匹配即放行),但选择器内部的多个条件(如 podSelectornamespaceSelector 同时出现)是 AND 关系(必须同时满足)。

三、实战:为 Flask + Redis 配置 NetworkPolicy

现在我们为贯穿案例的 Flask + Redis 应用配置网络隔离策略。核心目标是:

  1. Redis 只能被 Flask Pod 访问(拒绝其他所有 Pod)。

  2. Flask 只能被 Ingress Controller 访问(拒绝直接的 Pod-to-Pod 请求)。

  3. 禁止所有未声明的流量(默认拒绝)。

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 思维 !

相关推荐
Patrick_Wilson1 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生1 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭1 天前
运行你的第一个Docker容器
后端·docker·容器
Java之美2 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵3 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
武子康6 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
网络研究院9 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展
酣大智9 天前
ARP代理--工作原理
运维·网络·arp·arp代理
treesforest9 天前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全