LabHub

博客

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

한국어English日本語中文

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 的核心组件如下。

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 路由与策略引擎。主要组件如下。

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

主要的监控指标如下。

故障案例与恢复流程

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

状况:运维团队未经测试就在生产命名空间应用了 Default Deny 策略。由于遗漏了 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 代理,导致策略未能生效。

症状

恢复流程

# 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 被评估,安全策略因此失效。

症状

恢复流程

# 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

生产环境部署检查清单

事前准备

策略应用顺序

监控必备项

运维注意事项

回滚计划

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

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 策略为基础的零信任思路,并在应用策略前务必通过审计模式和预发布环境进行充分验证。网络策略并非一次应用就万事大吉,而是需要随着服务演进持续管理和更新的、活的安全要素,这一点绝不能忘记。

参考资料

评论

还没有评论。

登录后即可发表评论