技术博客

Cilium 零信任网络策略实战:L3/L4/L7 全栈访问控制

通过实际案例讲解如何用 CiliumNetworkPolicy 实现 Kubernetes 零信任网络,包括默认拒绝策略、基于标签的 L3/L4 控制、HTTP/gRPC 级别 L7 策略、DNS 策略和出口流量控制,覆盖微服务安全架构的核心场景。

Cilium零信任网络策略eBPFKubernetes安全

零信任网络的核心原则是“默认不信任,按需显式授权”。在 Kubernetes 中,Pod 默认可以与集群内任何其他 Pod 通信——这与零信任原则相悖。Cilium 的 CiliumNetworkPolicy 在 Kubernetes 原生 NetworkPolicy 之上,提供 L3(IP)、L4(端口/协议)、L7(HTTP/gRPC/DNS)三层联合控制能力,本文通过一个电商微服务架构讲解实战配置。

示例架构

本文使用以下微服务架构演示策略配置:

                  互联网

              [Ingress Controller]

    ┌───────────────────────────────────┐
    │  frontend (React)                 │
    │  labels: app=frontend,tier=web   │
    └───────────────┬───────────────────┘
                    ↓ :8080/api/*
    ┌───────────────────────────────────┐
    │  api-server (Go)                  │
    │  labels: app=api,tier=backend     │
    └──────┬─────────────┬──────────────┘
           ↓ :5432       ↓ :6379
    ┌──────────┐    ┌──────────┐
    │ postgres │    │  redis   │
    │ tier=db  │    │ tier=db  │
    └──────────┘    └──────────┘
           ↓ 集群外 SMTP
    ┌──────────────────────────────┐
    │  mailer (Python)              │
    │  labels: app=mailer          │
    └──────────────────────────────┘

目标:所有 Pod 默认隔离,只允许上图中的通信路径。

第一步:建立默认拒绝基线

不配置任何策略时,Kubernetes 的默认行为是“全部允许”。第一步是在目标命名空间建立“默认拒绝”基线:

# default-deny-all.yaml
# 对 production 命名空间的所有 Pod 默认拒绝入站和出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}        # 匹配所有 Pod
  policyTypes:
  - Ingress
  - Egress
  # 不写 ingress 和 egress 规则 = 拒绝所有
kubectl apply -f default-deny-all.yaml

# 验证:此时 Pod 间无法通信
kubectl exec -n production frontend-xxx -- curl -s http://api-server:8080/health
# curl: (7) Failed to connect to api-server port 8080: Connection refused
# ← 连接被策略拒绝,符合预期

注意:默认拒绝策略会同时阻断 DNS 查询(UDP 53)。需要在后续步骤中显式允许 DNS。

第二步:允许 DNS 解析

# allow-dns.yaml
# 允许所有 Pod 访问 kube-dns(不能缺少,否则服务发现失效)

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  endpointSelector: {}   # 匹配所有 Pod
  egress:
  - toEndpoints:
    - matchLabels:
        k8s:io.kubernetes.pod.namespace: kube-system
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: "53"
        protocol: UDP
      - port: "53"
        protocol: TCP
      rules:
        dns:
        - matchPattern: "*.cluster.local"      # 集群内 DNS
        - matchPattern: "*.svc.cluster.local"  # Service DNS
kubectl apply -f allow-dns.yaml

# 验证 DNS 恢复
kubectl exec -n production frontend-xxx -- nslookup api-server.production.svc.cluster.local
# Server: 10.96.0.10 (kube-dns IP)
# Name: api-server.production.svc.cluster.local
# Address: 10.96.45.123

第三步:L4 级策略——frontend → api

# policy-frontend-to-api.yaml
# 只允许 frontend 访问 api-server:8080,其他入站拒绝

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-ingress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api          # 策略应用到 api-server Pod
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend   # 只允许 frontend 访问
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
kubectl apply -f policy-frontend-to-api.yaml

# 测试:frontend -> api 应该成功
kubectl exec -n production frontend-xxx -- curl -s http://api-server:8080/health
# {"status":"ok"}  ← 成功

# 测试:mailer -> api 应该被拒绝
kubectl exec -n production mailer-xxx -- curl -s http://api-server:8080/health
# curl: (7) Failed to connect  ← 被策略拒绝,符合预期

第四步:L7 级策略——限制 HTTP 方法和路径

L4 策略只能控制端口,无法区分同端口上的不同 HTTP 请求。Cilium L7 策略可精确到 HTTP method 和 path:

# policy-api-to-db.yaml
# api-server 可以访问 postgres,但只允许特定操作(演示 L7 审计)

---
# 允许 api 写 postgres(应用层面的访问控制用 DB 角色,但这里演示 Cilium 审计能力)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: postgres-ingress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: postgres
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: api
    toPorts:
    - ports:
      - port: "5432"
        protocol: TCP

---
# redis 只允许 api 访问
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: redis-ingress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: redis
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: api
    toPorts:
    - ports:
      - port: "6379"
        protocol: TCP

现在来一个真正的 L7 HTTP 策略示例——假设 api-server 内部有管理接口需要只允许内部工具访问:

# policy-l7-http.yaml
# api-server HTTP 路径级别控制
# - frontend 只能访问 /api/* 路径
# - 内部 admin-tool 才能访问 /admin/*
# - 所有人都不能访问 /debug/*(健康检查除外 /health)

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-l7-ingress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  # frontend 访问规则:只允许 /api/* 和 /health
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "^/api/"
        - method: "POST"
          path: "^/api/"
        - method: "PUT"
          path: "^/api/"
        - method: "DELETE"
          path: "^/api/"
        - method: "GET"
          path: "^/health$"

  # admin-tool 访问规则:可以访问 /admin/*
  - fromEndpoints:
    - matchLabels:
        app: admin-tool
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "^/admin/"
        - method: "POST"
          path: "^/admin/"
        - method: "GET"
          path: "^/health$"

验证 L7 策略效果:

kubectl apply -f policy-l7-http.yaml

# frontend 访问 /api/ — 应该成功
kubectl exec -n production frontend-xxx -- curl -s http://api-server:8080/api/users
# [{"id":1,"name":"张三"}]  ← 成功

# frontend 访问 /admin/ — 应该被 L7 策略拒绝
kubectl exec -n production frontend-xxx -- curl -sv http://api-server:8080/admin/dashboard
# < HTTP/1.1 403 Forbidden
# Access denied  ← Cilium L7 代理返回 403,而不是 TCP reset

# Hubble 观察被拒绝的 L7 请求
hubble observe --namespace production --verdict DROPPED --protocol http
# TIMESTAMP  SOURCE              DESTINATION     TYPE      VERDICT   REASON
# 10:45:32   production/frontend production/api  http-monitor  DROPPED  Policy denied: HTTP method GET path /admin/

关键点:L4 策略拒绝时是 TCP reset,L7 策略拒绝时是 HTTP 403,客户端能收到有意义的错误信息。

第五步:出口流量控制(Egress)

控制 mailer 只能访问外部 SMTP 服务器:

# policy-mailer-egress.yaml
# mailer 只能向外访问 SMTP (25/587),不能访问任意外网

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: mailer-egress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: mailer
  egress:
  # 允许 DNS
  - toEndpoints:
    - matchLabels:
        k8s:io.kubernetes.pod.namespace: kube-system
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: "53"
        protocol: UDP

  # 只允许访问特定 SMTP 服务器
  - toFQDNs:
    - matchName: "smtp.aliyun.com"
    - matchName: "smtp.163.com"
    toPorts:
    - ports:
      - port: "587"
        protocol: TCP
      - port: "465"
        protocol: TCP
kubectl apply -f policy-mailer-egress.yaml

# 测试:mailer 发邮件应该成功
kubectl exec -n production mailer-xxx -- nc -zv smtp.aliyun.com 587
# Connection to smtp.aliyun.com 587 port [tcp/*] succeeded!

# 测试:mailer 访问其他外网应该被拒绝
kubectl exec -n production mailer-xxx -- curl -s https://google.com
# curl: (7) Failed to connect  ← 出口策略拒绝,符合预期

第六步:gRPC 服务策略

对于 gRPC 微服务,Cilium 可以精确控制 gRPC method:

# policy-grpc.yaml
# 只允许特定 gRPC 方法

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: grpc-service-ingress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: order-service
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: api
    toPorts:
    - ports:
      - port: "50051"
        protocol: TCP
      rules:
        http:   # gRPC 底层是 HTTP/2,Cilium 用 http 规则控制
        - method: "POST"
          path: "^/order.OrderService/CreateOrder$"
        - method: "POST"
          path: "^/order.OrderService/GetOrder$"
        # 不允许 DeleteOrder 方法

策略调试技巧

# 查看某个 Pod 的所有生效策略
kubectl -n kube-system exec ds/cilium -- cilium endpoint list
# 找到目标 Pod 的 Endpoint ID

kubectl -n kube-system exec ds/cilium -- cilium policy get

# 测试策略:模拟连接(不实际建立连接)
kubectl -n kube-system exec ds/cilium -- \
  cilium policy trace \
    --src-k8s-pod production:frontend-xxx \
    --dst-k8s-pod production:api-server-xxx \
    --dport 8080/tcp
# 输出策略判决结果(allow/deny)和原因

# 实时监控策略判决(类似 tcpdump)
hubble observe \
  --namespace production \
  --follow \
  --output compact
# 每行一条流量记录,包含 ALLOW/DROP 标记

策略矩阵总览

应用完所有策略后,访问矩阵如下:

源 → 目标 协议/端口 结果
frontend → api:8080/api/* HTTP GET/POST ✅ 允许
frontend → api:8080/admin/* HTTP ❌ L7 拒绝(403)
api → postgres:5432 TCP ✅ 允许
api → redis:6379 TCP ✅ 允许
mailer → smtp.aliyun.com:587 TCP ✅ 允许
mailer → 任意其他外网 TCP ❌ 出口策略拒绝
mailer → api:8080 TCP ❌ L4 拒绝
任意 → postgres:5432 TCP ❌ 非 api 拒绝

小结

Cilium CiliumNetworkPolicy 的核心价值在于将网络安全控制从 L4 提升到 L7,让安全策略能精确到 HTTP path、gRPC method、DNS FQDN 级别。结合 Hubble 的实时可观测性,安全团队可以先用“审计模式”(只观测不阻断)建立流量基线,再逐步收紧策略,避免一次性切换带来的业务中断风险。零信任网络不是一蹴而就的,从命名空间级默认拒绝开始,逐层添加最小权限策略,是最稳健的落地路径。