K8s 系列 | 第 12 天:NetworkPolicy:K8s 网络安全策略与微隔离


title: K8s 系列 | 第 12 天:NetworkPolicy:K8s 网络安全策略与微隔离
tags:
– Kubernetes
– K8s系列
– DevOps
– NetworkPolicy
– 网络安全
– 微隔离
– 容器网络


第 12/30 天


引言

在 Kubernetes 集群中,默认情况下所有 Pod 之间是可以相互通信的——这是一个扁平的、全连通网络模型。虽然这降低了服务发现的复杂度,但在生产环境中却带来了严重的安全隐患:一旦某个 Pod 被攻陷,攻击者可以自由横向移动,访问集群中的任意其他服务。

NetworkPolicy 是 Kubernetes 原生的网络安全控制机制,它允许你通过声明式规则来控制 Pod 层面的入站(Ingress)和出站(Egress)流量,实现微隔离(Micro-segmentation),将零信任安全模型落地到容器化环境中。

本文将深入解析 NetworkPolicy 的核心原理、实战配置以及生产最佳实践。


一、核心概念

1.1 什么是 NetworkPolicy?

NetworkPolicy 是 Kubernetes 的命名空间级别资源,它通过标签选择器(Label Selector)来定义哪些 Pod 允许与哪些端点通信。其本质是一组有状态防火墙规则,工作在 OSI 模型第 3/4 层(IP + 端口)。

1.2 关键特性

特性 说明
命名空间范围 NetworkPolicy 仅作用于同一命名空间中的 Pod
标签驱动 通过 Pod 标签选择目标,而非 IP 地址
双向控制 支持 Ingress(入站)和 Egress(出站)流量控制
白名单模型 默认拒绝所有流量,仅允许显式放行的流量
需要 CNI 支持 非所有 CNI 插件都实现 NetworkPolicy

1.3 先决条件:CNI 插件支持

并非所有集群都支持 NetworkPolicy! 它需要 CNI 网络插件具备 NetworkPolicy 引擎。以下是常见 CNI 的支持情况:

CNI 插件 支持 NetworkPolicy 备注
Calico 企业级策略引擎,支持全局网络策略
Cilium 基于 eBPF,支持 L3-L7 策略
Weave Net 内置策略控制器
Flannel 极简网络模型,无策略引擎
Antrea VMware 出品,支持 K8s 原生策略

关键提示:如果使用 Flannel,你需要额外部署 Calico 的 NetworkPolicy 组件(以策略-only 模式运行),或直接切换为 Calico / Cilium。


二、NetworkPolicy 资源定义详解

2.1 资源结构

一个完整的 NetworkPolicy 包含以下核心字段:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: demo-network-policy
  namespace: default
spec:
  # 1. 目标 Pod 选择器(必选)
  podSelector:
    matchLabels:
      app: web
  # 2. 策略类型(可选,默认为 Ingress)
  policyTypes:
    - Ingress
    - Egress
  # 3. 入站规则
  ingress:
    - from:
        - ipBlock:
            cidr: 10.0.0.0/24
            except:
              - 10.0.0.5/32
        - namespaceSelector:
            matchLabels:
              project: production
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 80
        - protocol: TCP
          port: 443
  # 4. 出站规则
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.100.0.0/16
      ports:
        - protocol: TCP
          port: 53
        - protocol: UDP
          port: 53

2.2 字段详解

podSelector(目标选择)

  • {} 空选择器匹配命名空间中所有 Pod
  • 这是策略作用的目标 Pod(被保护的 Pod)

policyTypes

  • 如果不指定,默认仅启用 Ingress
  • 一旦显式指定了 Egress,必须同时为 Egress 流量配置出站规则
  • 强烈建议始终显式声明 policyTypes

ingress 规则

  • from 来源匹配(三者之间是关系):
  • podSelector:同一命名空间中的 Pod
  • namespaceSelector:其他命名空间中的 Pod
  • ipBlock:集群外部的 CIDR 地址段
  • ports 目标端口
  • 多个 from 条目之间是关系
  • 同一个 from 内的 podSelectornamespaceSelector 之间是关系

egress 规则

  • to 目标匹配,结构与 from 相同
  • ports 目标端口

2.3 关键行为语义

多条 ingress/egress 规则 → OR(任一匹配即放行)
同一规则内的多个来源   → AND(必须同时满足)
podSelector + namespaceSelector 在同一 from/to 条目内 → AND
多个 from/to 条目 → OR

三、实战配置案例

3.1 场景:三层微服务架构

我们有经典的 Web → API → DB 三层架构,需要实现以下安全策略:

┌─────────┐     ┌─────────┐     ┌─────────┐
│  Web    │────▶│  API    │────▶│   DB    │
│  app=web │     │ app=api  │     │ app=db   │
└─────────┘     └─────────┘     └─────────┘
  port 80         port 8080       port 3306

安全要求
1. Web Pod:仅允许从 Ingress Controller 访问 80 端口
2. API Pod:仅允许 Web Pod 访问 8080 端口
3. DB Pod:仅允许 API Pod 访问 3306 端口,且自身不能发起对外连接
4. 所有 Pod 可以访问 kube-dns(UDP 53)用于服务发现

部署测试应用

# 创建命名空间
kubectl create namespace network-demo

# 部署 Web 服务
kubectl create deployment web --image=nginx -n network-demo
kubectl expose deployment web --port=80 --target-port=80 -n network-demo

# 部署 API 服务
kubectl create deployment api --image=nginx -n network-demo
kubectl expose deployment api --port=8080 --target-port=80 -n network-demo

# 部署 DB 服务
kubectl create deployment db --image=mysql:8.0 -n network-demo
kubectl expose deployment db --port=3306 -n network-demo

策略 1:DB 层隔离——仅允许 API 访问 3306

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-ingress-policy
  namespace: network-demo
spec:
  podSelector:
    matchLabels:
      app: db
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 3306
  egress:
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

策略 2:API 层隔离——仅允许 Web 访问 8080

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-ingress-policy
  namespace: network-demo
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
    - to:
        - podSelector:
            matchLabels:
              app: db
      ports:
        - protocol: TCP
          port: 3306

策略 3:Web 层隔离——仅允许 Ingress 流量

实际生产环境中,Web 层应该只允许从 Ingress Controller 过来的流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-ingress-policy
  namespace: network-demo
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 80
  egress:
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
    - to:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 8080

3.2 验证网络策略

# 部署测试客户端
kubectl run test-pod --image=busybox -n network-demo -- sleep 3600

# 测试连接 DB(应该被拒绝——test-pod 没有 app=api 标签)
kubectl exec -it test-pod -n network-demo -- wget -q --timeout=3 db:3306

# 测试 DNS 解析(应该成功——所有 Pod 允许访问 kube-dns)
kubectl exec -it test-pod -n network-demo -- nslookup kubernetes.default

# 检查 NetworkPolicy 状态
kubectl describe networkpolicy -n network-demo

四、高级场景

4.1 默认拒绝所有流量

这是生产环境的推荐起点:先封锁一切,再逐步放行必要流量。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

应用此策略后,命名空间中所有 Pod 只能通过显式创建的其他 NetworkPolicy 来放行流量。

4.2 白名单——仅允许特定命名空间

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9090

注意:kubernetes.io/metadata.name 是 K8s 1.22+ 自动添加的标签,值为命名空间名称。

4.3 跨命名空间通信

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend-ns
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: payment-service
  ingress:
    # 来自 frontend 命名空间中标签为 tier=web 的 Pod
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: frontend
          podSelector:
            matchLabels:
              tier: web
      ports:
        - protocol: TCP
          port: 8080

这里的 namespaceSelectorpodSelector 在同一个 from 条目中,它们是关系:必须同时满足来自 frontend 命名空间且 Pod 标签为 tier=web。

4.4 允许来自集群外部的 IP

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: metrics-exporter
  ingress:
    - from:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - protocol: TCP
          port: 9100

五、常见问题与陷阱

5.1 “我创建了 NetworkPolicy,但流量没被拦截?”

可能原因
1. CNI 插件不支持:运行 kubectl describe pod <pod-name> 查看网络插件,Flannel 不支持
2. 策略未正确选择 Pod:检查 podSelector 的标签是否匹配目标 Pod
3. 缺少 policyTypes: [Egress]:默认只控制 Ingress,出站不受影响
4. Pod 在 istio-proxy sidecar 模式下:Envoy 会绕过底层 NetworkPolicy

5.2 “我只想允许访问 kube-dns,但写了一天规则”

# 正确做法:使用命名空间选择器匹配 kube-system
- to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
      - protocol: UDP
        port: 53
      - protocol: TCP
        port: 53

5.3 “NetworkPolicy 性能会受影响吗?”

  • 每个命名空间创建大量 NetworkPolicy 会增加 CNI 的策略计算开销
  • Cilium 基于 eBPF 的实现性能最优,适合大规模集群
  • 建议:单个命名空间不超过 20 条 NetworkPolicy
  • 使用 kubectl get networkpolicy -A | wc -l 监控总数

5.4 调试 NetworkPolicy

# 1. 查看当前策略
kubectl get networkpolicy -n <namespace>
kubectl describe networkpolicy <name> -n <namespace>

# 2. 使用 netshoot 诊断工具
kubectl run netshoot --image=nicolaka/netshoot -n <namespace> -- sleep 3600
kubectl exec -it netshoot -n <namespace> -- curl -v http://target-service:port

# 3. 查看 iptables 规则(Calico 下)
kubectl exec <calico-node-pod> -n kube-system -- iptables -L -n -t filter | grep <namespace>

六、生产最佳实践

6.1 推荐策略模板

# 1. 默认拒绝所有
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# 2. 允许 DNS 解析
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

6.2 实施策略

  1. 先审计再实施:使用 kubectl trace 或 Cilium Hubble 观察现有流量模式
  2. 渐进式封锁:先只启用 Ingress 策略,确认无误后再启用 Egress
  3. 使用 namespace 标签命名规范:为每个命名空间打上清晰的元数据标签
  4. 配合 Service Mesh:Cilium + NetworkPolicy 可实现 L7 层策略控制
  5. 启用审计日志:通过 CNI 插件的策略审计日志监控被拒绝的流量

6.3 使用 CiliumNetworkPolicy 实现 L7 策略

对于更细粒度的控制(HTTP 路径、方法等),可以升级到 Cilium:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-api-routes
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: web
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/users/.*"
              - method: POST
                path: "/api/v1/orders"

CiliumNetworkPolicy 是自定义资源(CRD),需要预先安装 Cilium CNI 插件。


总结

NetworkPolicy 是 Kubernetes 生产环境中不可或缺的安全组件。通过本文的实战演练,你已经掌握了:

  1. ✅ NetworkPolicy 的核心概念与工作原理
  2. ✅ 三层微服务架构的完整 NetworkPolicy 配置
  3. ✅ 默认拒绝 + 白名单放行的安全最佳实践
  4. ✅ 各种常见使用场景(命名空间隔离、IP 白名单、DNS 放行)
  5. ✅ 调试与故障排查方法

记住三条黄金法则:
先拒绝一切,再显式放行
始终同时声明 Ingress 和 Egress
测试、测试、再测试——用调试 Pod 验证每条策略

安全无小事,每一层隔离都是生产环境中防御纵深的关键一环。


下期预告:第 13 天我们将继续深入 K8s 服务发现领域,全面解析 Headless Service 与服务发现机制——为什么有时候你不需要 ClusterIP?StatefulSet 如何与 Headless Service 配合实现稳定的网络标识?敬请期待!

系列目录K8s 30 天系列文章目录

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容