Skip to content
Published on

Kubernetes Network Policy 完全指南:用 Cilium 与 Calico 构建零信任网络安全

分享
Authors
Kubernetes Network Policy with Cilium and Calico

前言

在 Kubernetes 集群中,Pod 默认可以 与其他所有 Pod 自由通信。这在开发初期很方便,但在生产环境中会成为严重的安全威胁。因为只要有一个 Pod 被攻破,攻击者就能向集群内的所有服务横向移动。实际上,2024 年 CNCF Security Audit 调查的 Kubernetes 安全事故中,有 67% 源于内部网络隔离不足。

零信任网络(Zero Trust Network) 架构正是针对这一问题的答案,其原则是即便处于网络内部的流量也一律不予信任,只放行被明确允许的通信。在 Kubernetes 中实现这一点的核心工具就是 NetworkPolicy

不过,原生的 Kubernetes NetworkPolicy 只支持 L3/L4(IP、端口)层面的控制,并不提供基于 DNS 的策略或基于 HTTP 路径的过滤等高级能力。为了突破这一局限,CiliumCalico 这类 CNI 插件应运而生。Cilium 基于 eBPF 提供精细到 L7 的策略,Calico 则通过 BGP 路由与 GlobalNetworkPolicy 实现企业级的网络安全。

本文从 Kubernetes NetworkPolicy 的基础概念讲起,综合介绍 Cilium 与 Calico 的高级策略实现、对比分析、监控与排障、真实故障案例与恢复流程,以及生产环境部署检查清单。

Kubernetes NetworkPolicy 架构

NetworkPolicy 基本结构

Kubernetes NetworkPolicy 是在 Pod 级别控制网络流量的命名空间级资源。实际策略由 CNI 插件执行,在不支持 NetworkPolicy 的 CNI(例如 Flannel)上,即使创建了该资源也不会产生任何效果。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              environment: production
          podSelector:
            matchLabels:
              role: frontend
        - ipBlock:
            cidr: 10.0.0.0/8
            except:
              - 10.0.1.0/24
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53

这条策略的行为如下。

  1. 目标 Pod 选择:通过 podSelector 应用于带有 app: api-server 标签的 Pod
  2. 入站规则:仅允许来自 production 命名空间的 frontend Pod,以及 10.0.0.0/8 网段(排除 10.0.1.0/24)访问 TCP 8080 端口
  3. 出站规则:仅允许访问 database Pod 的 TCP 5432 以及 DNS(UDP 53)流量

Default Deny 策略

零信任的基础是 先阻断所有流量,再仅显式放行必要的通信。Default Deny 策略会阻断命名空间内所有 Pod 的入站与出站流量。

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

空的 podSelector 会选中命名空间内的所有 Pod。虽然 policyTypes 中同时指定了 Ingress 与 Egress,但由于没有任何放行规则,所有流量都会被阻断。必须单独放行 DNS,服务发现才能正常工作。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

命名空间隔离模式

在大规模集群中,命名空间之间的隔离必不可少。下面是只允许同一命名空间内通信的模式。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: namespace-isolation
  namespace: team-alpha
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector: {}

podSelector: {} 没有搭配 namespaceSelector 时,只会匹配当前命名空间中的所有 Pod。借此可以简洁地实现命名空间之间的隔离。

Cilium CiliumNetworkPolicy 深度解析

Cilium 架构与 eBPF

Cilium 利用 Linux 内核的 eBPF(extended Berkeley Packet Filter) 技术,在内核层面执行网络策略。与传统基于 iptables 的方案不同,eBPF 提供可编程的数据平面,因此即使策略数量增加,性能也几乎不会下降。

Cilium 的核心组件如下。

  • Cilium Agent:在每个节点上以 DaemonSet 运行,负责编译 eBPF 程序并加载到内核
  • Cilium Operator:负责整个集群范围的资源管理
  • Hubble:实时观测网络流的监控工具
  • Envoy Proxy:应用 L7 策略时作为透明代理工作

L3-L7 过滤实现

Cilium 的 CiliumNetworkPolicy 涵盖原生 NetworkPolicy 的全部能力,并额外支持 L7(HTTP、gRPC、Kafka)层面的精细控制。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-api-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
          rules:
            http:
              - method: GET
                path: '/api/v1/products'
              - method: POST
                path: '/api/v1/orders'
                headers:
                  - 'Content-Type: application/json'
              - method: GET
                path: '/healthz'

这条策略在 frontend Pod 访问 api-server 的流量中,只放行 GET /api/v1/products、POST /api/v1/orders(必须带 JSON Content-Type 头)以及 GET /healthz 请求。PUT、DELETE 等其他 HTTP 方法都会被阻断。

基于 DNS 的策略

Cilium 可以基于 FQDN(Fully Qualified Domain Name)控制出站流量。在需要把外部 API 调用限制到特定域名时非常有用。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: dns-based-egress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: payment-service
  egress:
    - toEndpoints:
        - matchLabels:
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: '53'
              protocol: ANY
          rules:
            dns:
              - matchPattern: '*.stripe.com'
              - matchPattern: '*.amazonaws.com'
    - toFQDNs:
        - matchPattern: '*.stripe.com'
      toPorts:
        - ports:
            - port: '443'
              protocol: TCP
    - toFQDNs:
        - matchPattern: '*.amazonaws.com'
      toPorts:
        - ports:
            - port: '443'
              protocol: TCP

这条策略把 payment-service 限制为只能与 stripe.com 和 amazonaws.com 域名进行 HTTPS 通信。DNS 查询本身也只对这些域名放行。

CiliumClusterwideNetworkPolicy

需要在整个集群范围生效的策略要使用 CiliumClusterwideNetworkPolicy。

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: block-metadata-access
spec:
  endpointSelector: {}
  egressDeny:
    - toCIDR:
        - 169.254.169.254/32
      toPorts:
        - ports:
            - port: '80'
              protocol: TCP

这条策略阻断集群内所有 Pod 访问云元数据服务(169.254.169.254)。这是防止通过 IMDS 窃取凭据攻击的重要安全措施。

Calico GlobalNetworkPolicy 深度解析

Calico 架构

Calico 是 Tigera 开发的网络方案,提供基于 BGP(Border Gateway Protocol)的 L3 路由与策略引擎。主要组件如下。

  • Felix:运行在每个节点上的代理,管理路由表与 iptables/eBPF 规则
  • BIRD:BGP 客户端,负责在节点之间交换路由信息
  • Typha:位于 Felix 与 Kubernetes API Server 之间的代理,用于降低 API Server 负载
  • calicoctl:管理 Calico 资源的 CLI 工具

GlobalNetworkPolicy 实现

Calico 的 GlobalNetworkPolicy 是不依附于命名空间的集群级策略,其优先级高于 Kubernetes 标准 NetworkPolicy。

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-egress-to-internet
spec:
  selector: environment == 'production'
  types:
    - Egress
  egress:
    - action: Allow
      destination:
        nets:
          - 10.0.0.0/8
          - 172.16.0.0/12
          - 192.168.0.0/16
    - action: Allow
      protocol: UDP
      destination:
        ports:
          - 53
    - action: Deny
      destination:
        notNets:
          - 10.0.0.0/8
          - 172.16.0.0/12
          - 192.168.0.0/16

这条策略让 production 环境的所有 Pod 只能访问 RFC 1918 私有 IP 网段与 DNS 流量,阻断直接连接互联网。

Calico 分层(Tier)体系

在 Calico Enterprise 中,可以使用策略分层来明确管理策略的执行顺序。

apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: security
spec:
  order: 100

---
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: platform
spec:
  order: 200

---
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: security.block-known-threats
spec:
  tier: security
  order: 10
  selector: all()
  types:
    - Ingress
    - Egress
  ingress:
    - action: Deny
      source:
        nets:
          - 198.51.100.0/24
    - action: Pass
  egress:
    - action: Deny
      destination:
        nets:
          - 198.51.100.0/24
    - action: Pass

由于安全团队的策略会先于平台团队的策略被评估,因此可以在不受平台策略影响的前提下阻断已知威胁 IP。

Cilium vs Calico vs 原生 NetworkPolicy 对比表

下表对比了各方案的主要功能与特性。

项目Kubernetes NetworkPolicyCiliumCalico
策略范围命名空间命名空间 + 集群命名空间 + 集群
L3/L4 支持OOO
L7 支持XO (HTTP, gRPC, Kafka, DNS)部分(仅 Enterprise)
基于 DNS 的策略XO(FQDN 匹配)O (Calico Enterprise)
策略引擎依赖 CNIeBPF 原生iptables / eBPF 可选
性能(策略 1000+)基于 iptables 性能下降eBPF 保持稳定性能eBPF 模式下良好
监控需要额外工具内置 Hubblecalicoctl + Prometheus
FQDN 出站XOO (Enterprise)
策略分层XXO (Enterprise)
Host 防火墙XOO
加密XWireGuard/IPsecWireGuard
多集群XCluster MeshCalico Federation
CNCF 等级标准Graduated-(Tigera 商用)
GUI 管理XHubble UICalico Enterprise UI

实施指南

分阶段落地 Default Deny

在生产环境中一次性应用 Default Deny 可能引发大规模故障。下面是安全的分阶段实施流程。

阶段 1:以审计模式起步(Cilium)

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: audit-default-deny
  namespace: staging
  annotations:
    policy.cilium.io/audit-mode: 'true'
spec:
  endpointSelector: {}
  ingress:
    - fromEndpoints:
        - matchLabels:
            reserved:host: ''
  egress:
    - toEndpoints:
        - matchLabels:
            reserved:host: ''

在审计模式下,策略不会真正阻断流量,而是把本应被阻断的流量通过 Hubble 记录成日志。

阶段 2:用 Hubble 分析流量

# 用 Hubble CLI 查看被策略审计(audit)的流量
hubble observe --namespace staging --verdict AUDIT --output json | \
  jq '.flow | {src: .source.labels, dst: .destination.labels, port: .l4.TCP.destination_port}'

# 掌握命名空间内的通信模式
hubble observe --namespace staging --type trace:to-endpoint \
  --output compact --last 1000

阶段 3:编写放行策略后启用 Default Deny

根据审计日志的分析结果编写好全部必要的放行策略后,再移除审计模式的 annotation,使策略正式生效。

微服务策略模式

下面是真实微服务环境中常用的策略模式。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: microservice-pattern
  namespace: ecommerce
spec:
  endpointSelector:
    matchLabels:
      app: order-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: api-gateway
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
          rules:
            http:
              - method: POST
                path: '/orders'
              - method: GET
                path: '/orders/[0-9]+'
  egress:
    - toEndpoints:
        - matchLabels:
            app: inventory-service
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            app: payment-service
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: '53'
              protocol: ANY

这条策略把 order-service 限制为只接收来自 api-gateway 的订单相关 HTTP 请求,并且只能与 inventory-service 和 payment-service 通信。

监控与排障

用 Hubble 监控 Cilium

Hubble 是 Cilium 内置的网络观测工具,基于 eBPF 实时采集所有网络流。

# 启用 Hubble(Cilium Helm 安装时)
helm upgrade cilium cilium/cilium \
  --namespace kube-system \
  --set hubble.enabled=true \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true

# 查看特定命名空间中被丢弃的流量
hubble observe --namespace production --verdict DROPPED \
  --output json --last 100

# 追踪特定 Pod 之间的通信
hubble observe --from-pod production/api-server-7d9f8b6c5d-x2k4m \
  --to-pod production/database-5f7b9c8d6e-m3n7p --output compact

# 查看策略生效状态
cilium policy get --endpoint 12345

# 查看 Cilium 端点状态
cilium endpoint list -o json | \
  jq '.[] | select(.status.policy.realized.denied > 0) | {id: .id, labels: .status.labels}'

用 calicoctl 排查 Calico

# 查看 Calico 节点状态
calicoctl node status

# 查看已生效的策略列表
calicoctl get networkpolicy --all-namespaces -o wide
calicoctl get globalnetworkpolicy -o wide

# 模拟特定工作负载的策略评估
calicoctl get workloadendpoint --all-namespaces -o yaml | \
  grep -A 5 "api-server"

# 在 Felix 日志中确认策略生效情况
kubectl logs -n calico-system -l k8s-app=calico-node -c calico-node \
  --tail=100 | grep -i "policy"

# 查看 BGP 邻居状态
calicoctl node status | grep -A 10 "BGP"

Prometheus 指标采集

Cilium 与 Calico 都会暴露 Prometheus 指标。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: cilium-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      k8s-app: cilium
  namespaceSelector:
    matchNames:
      - kube-system
  endpoints:
    - port: metrics
      interval: 15s
      path: /metrics

主要的监控指标如下。

  • cilium_policy_verdict_total:策略判定结果(FORWARDED、DROPPED、AUDIT)计数器
  • cilium_drop_count_total:被策略丢弃的数据包数量
  • cilium_policy_import_errors_total:策略解析错误数量
  • calico_felix_active_local_policies:Felix 正在活跃管理的策略数量
  • calico_felix_iptables_save_errors:iptables 规则保存错误数量

故障案例与恢复流程

案例 1:一次性应用 Default Deny 导致全部服务中断

状况:运维团队未经测试就在生产命名空间应用了 Default Deny 策略。由于遗漏了 DNS 放行策略,所有服务的服务发现失败,进而连锁导致所有微服务之间无法互相通信。

症状

  • 所有 Pod 的 readiness probe 失败
  • 服务之间的 HTTP 请求超时
  • DNS 查询无响应

恢复流程

# 1. 紧急处置:移除引发问题的 Default Deny 策略
kubectl delete networkpolicy default-deny-all -n production

# 2. 确认状态
kubectl get pods -n production -o wide
kubectl get endpoints -n production

# 3. 确认 DNS 通信
kubectl exec -n production deploy/api-server -- nslookup kubernetes.default

# 4. 分析根本原因后重新应用正确的策略集合
# - 先应用 DNS 放行策略
kubectl apply -f allow-dns-egress.yaml
# - 应用服务间通信策略
kubectl apply -f service-communication-policies/
# - 最后再应用 Default Deny
kubectl apply -f default-deny-all.yaml

教训:Default Deny 必须在放行策略全部生效之后再最后应用,并且务必先在预发布环境完成验证。

案例 2:Cilium DNS 策略与 CoreDNS 缓存不一致

状况:应用了 Cilium 基于 FQDN 的出站策略,但 CoreDNS 缓存的 DNS 响应绕过了 Cilium 的 DNS 代理,导致策略未能生效。

症状

  • 特定 FQDN 策略间歇性失效
  • 在 Hubble 中观测不到针对该 FQDN 的 DNS 查询
  • CoreDNS 缓存 TTL 过期后策略恢复正常

恢复流程

# 1. 确认 CoreDNS 缓存
kubectl exec -n kube-system deploy/coredns -- \
  curl -s http://localhost:9153/metrics | grep 'coredns_cache'

# 2. 确认 Cilium DNS 代理日志
cilium monitor --type drop --related-to fqdn

# 3. 重新应用 DNS 策略并重启 Cilium Agent
kubectl rollout restart daemonset/cilium -n kube-system

# 4. 确认 CoreDNS 配置中经由 Cilium DNS 代理的转发
kubectl get configmap coredns -n kube-system -o yaml

案例 3:Calico 策略顺序冲突

状况:两个团队各自独立编写 GlobalNetworkPolicy,生成了 order 值相同的策略,导致非预期的 Allow 规则先于 Deny 被评估,安全策略因此失效。

症状

  • 本应被阻断的流量被放行
  • 用 calicoctl 查看策略顺序时发现冲突

恢复流程

# 1. 确认所有 GlobalNetworkPolicy 的 order
calicoctl get globalnetworkpolicy -o yaml | grep -E "name:|order:"

# 2. 修正冲突策略的 order
calicoctl apply -f - <<EOF
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: security-block-external
spec:
  order: 50
  selector: all()
  types:
    - Egress
  egress:
    - action: Deny
      destination:
        notNets:
          - 10.0.0.0/8
EOF

# 3. 验证策略生效顺序
calicoctl get globalnetworkpolicy -o wide | sort -k3 -n

生产环境部署检查清单

事前准备

  • 确认 CNI 插件(Cilium/Calico)已安装并正常运行
  • 确认所有命名空间都设置了合适的标签
  • 确认所有工作负载都带有 app、role 等可用于选择的标签
  • 为 kube-system 命名空间的核心服务(CoreDNS、metrics-server)准备放行策略

策略应用顺序

  • 阶段 1:向所有命名空间应用 DNS 放行策略
  • 阶段 2:放行与 kube-system 命名空间之间的必要通信
  • 阶段 3:应用服务间通信放行策略
  • 阶段 4:应用外部通信放行策略
  • 阶段 5:先在预发布环境测试 Default Deny 策略
  • 阶段 6:在生产环境应用 Default Deny(同时进行流量监控)

监控必备项

  • 用 Hubble 或 calicoctl 搭建被丢弃流量的实时监控
  • 在 Prometheus + Grafana 仪表盘中加入策略相关指标
  • 配置策略违规时的 PagerDuty/Slack 告警
  • 搭建周期性策略审计(Policy Audit)的自动化流程

运维注意事项

  • 变更策略时始终使用 --dry-run=client 选项做事前验证
  • 基于 CIDR 的策略易受 IP 变动影响,优先使用基于标签的策略
  • Headless Service 会直接使用 Pod IP,需要单独考虑策略
  • 确认 NodePort、LoadBalancer 服务的入站流量路径
  • 使用 Cilium Host Policy 时注意不要阻断节点间通信(kubelet、etcd)
  • 策略备份与 Git 仓库管理(建议接入 GitOps)

回滚计划

  • 准备好紧急情况下可立即移除 Default Deny 策略的脚本
  • 建立策略变更前后 Hubble/calicoctl 快照对比流程
  • 搭建可从 Git 立即恢复回滚目标策略旧版本的流水线

高级模式:多集群网络策略

Cilium Cluster Mesh

使用 Cilium Cluster Mesh 可以跨多个集群应用网络策略。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: cross-cluster-policy
  namespace: shared-services
spec:
  endpointSelector:
    matchLabels:
      app: shared-database
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: backend
            io.cilium.k8s.policy.cluster: cluster-east
      toPorts:
        - ports:
            - port: '5432'
              protocol: TCP
    - fromEndpoints:
        - matchLabels:
            app: backend
            io.cilium.k8s.policy.cluster: cluster-west
      toPorts:
        - ports:
            - port: '5432'
              protocol: TCP

Calico Federation

在 Calico 中,可以通过 Federation 为远程集群的服务配置网络策略。

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: federated-service-access
spec:
  selector: app == 'frontend'
  types:
    - Egress
  egress:
    - action: Allow
      destination:
        selector: app == 'api-gateway'
        namespaceSelector: global()
      protocol: TCP

结语

Kubernetes 网络策略是集群安全的核心组成部分。仅靠原生 NetworkPolicy 也能实现 L3/L4 层面的隔离,但在真实生产环境中,Cilium 或 Calico 这类 CNI 插件的高级能力是必不可少的。

Cilium 的优势在于基于 eBPF 的高性能数据平面、L7 策略、基于 DNS 的出站控制以及 Hubble 可观测性。Calico 则凭借 BGP 路由集成、GlobalNetworkPolicy 与策略分层体系,更适合企业级环境。

无论选择哪种方案,都应采用以 Default Deny 策略为基础的零信任思路,并在应用策略前务必通过审计模式和预发布环境进行充分验证。网络策略并非一次应用就万事大吉,而是需要随着服务演进持续管理和更新的、活的安全要素,这一点绝不能忘记。

参考资料