土法炼钢兴趣小组的算法知识备份

Cilium 身份识别与零信任网络

目录

上一篇我们拆解了 Kubernetes NetworkPolicy 的标准模型 – 基于 Pod selector 和 namespace selector 的 L3/L4 访问控制。标准 NetworkPolicy 的局限性在那篇文章的结尾已经提到:它只能做到 IP + 端口级别的过滤,无法理解 HTTP 方法、gRPC 服务名、Kafka topic 这些应用层语义。更关键的是,它的底层实现依赖 iptables 规则,而 iptables 规则是以 IP 地址为匹配条件的 – 在 Pod IP 随时变化的 Kubernetes 环境中,这从根本上就是一个脆弱的安全模型。

Cilium 的回答是:抛弃 IP,用身份(Identity)作为安全原语。

这篇文章是 Cilium 深度拆解的安全专题续篇。那篇文章从整体架构角度介绍了 Cilium 的 eBPF 数据面和 Identity 模型的基本概念。本文会专注于安全维度,深入拆解 Identity 的分配机制、eBPF datapath 中的身份传递、L7 协议级访问控制、透明 mTLS 的实现,以及这些技术如何组合成一个完整的零信任网络架构。

如果你还没读过 eBPF 安全机制的基础知识,建议先看 eBPF 安全机制,那篇文章覆盖了 eBPF verifier、capability 检查和安全边界的核心概念。

本文基于 Cilium v1.16.x,Linux 6.x 内核。部分内部实现可能随版本演进而调整。 实验环境:Ubuntu 22.04, kernel 6.5, kind 集群 + Cilium Helm 安装。


一、为什么 IP-Based 安全在 Kubernetes 中不够用

IP 地址的短暂性

传统网络安全的基石是 IP 地址:防火墙规则、ACL、安全组,全部以”源 IP / 目标 IP + 端口”作为匹配条件。这在物理网络或虚拟机环境中是合理的 – 一台服务器的 IP 通常是长期稳定的。但在 Kubernetes 中,IP 地址是短暂的(ephemeral):

$ kubectl get pod -o wide
NAME                    READY   IP            NODE
frontend-abc123-xk9z2   1/1    10.244.1.47   node-1

# 滚动更新后,IP 完全变化
$ kubectl rollout restart deploy/frontend
$ kubectl get pod -o wide
NAME                    READY   IP            NODE
frontend-abc123-j7q4n   1/1    10.244.1.52   node-1

同一个 Deployment,滚动更新一次,Pod IP 就变了。如果安全策略写死了 10.244.1.47,更新后就失效了。更糟糕的是,这个 IP 可能被分配给一个完全不同的 Pod – 安全策略不仅失效,还可能错误地放行了不该放行的流量。

SNAT 导致的源信息丢失

即使 IP 没变,Kubernetes 的网络模型也会在多个环节丢失源 IP 信息。当流量经过 kube-proxy 的 NodePort 或 LoadBalancer 时,源 IP 被 SNAT 成节点 IP。后端 Pod 看到的 src_ip 是节点地址,无法区分实际的客户端。基于源 IP 的安全策略在这条路径上完全无法生效。

externalTrafficPolicy: Local 可以保留源 IP,但它限制了流量只能路由到本地 Pod,牺牲了负载均衡能力。这是一个典型的安全与功能的 trade-off。

IP-Based 策略的规模问题

在大规模集群中,IP-based 策略还有规则膨胀的问题:

集群规模 Pod 数量 iptables 规则数(估算) 规则更新延迟
小型 100 ~500 <1s
中型 1,000 ~5,000 2-5s
大型 10,000 ~50,000 10-30s
超大型 50,000 ~250,000 >60s

每个 NetworkPolicy 规则最终被翻译成一组 iptables 规则,规则数量与 Pod 数量成正比。当集群有上万个 Pod 时,iptables-restore 的全量刷新可能需要几十秒,在这个窗口期内安全策略是不一致的。

Cilium 的 Identity 模型从根本上解决了这个问题:策略规则数量与 Identity 数量成正比,而 Identity 数量远小于 Pod 数量。一万个相同标签的 Pod 共享同一个 Identity,策略规则只需要一条。


二、Cilium Identity 模型深度拆解

什么是 Cilium Identity

Cilium Identity 是一个 32 位无符号整数,代表一组具有相同安全相关标签(security-relevant labels)的工作负载。两个 Pod 如果拥有完全相同的安全标签集合,就会被分配相同的 Identity。

$ kubectl get cep -n default frontend-abc123-xk9z2 -o yaml
apiVersion: cilium.io/v2
kind: CiliumEndpoint
metadata:
  name: frontend-abc123-xk9z2
status:
  identity:
    id: 48372
    labels:
      - k8s:app=frontend
      - k8s:env=prod
      - k8s:io.kubernetes.pod.namespace=default

Identity 48372 代表的不是某一个 Pod,而是所有具有 app=frontend, env=prod 标签、运行在 default namespace 的 Pod。无论这些 Pod 的 IP 怎么变化,Identity 始终是 48372。

安全相关标签的筛选

不是所有 Pod 标签都参与 Identity 计算。Cilium 会自动排除 pod-template-hashcontroller-revision-hash 等频繁变化的标签,只保留安全相关的标签。可以通过配置显式控制:

# Cilium ConfigMap
labels: "k8s:io.kubernetes.pod.namespace k8s:app k8s:env"

这个筛选逻辑确保了 Identity 的稳定性:Pod 滚动更新时,pod-template-hash 变化不会导致 Identity 重新分配。

Label 到 Identity 的映射:kvstore Allocation

Identity 的分配是全局的,需要集中式存储保证唯一性。Cilium 支持两种后端:

方式一:etcd(kvstore 模式)

# etcd 中的存储结构
/cilium/state/identities/v1/id/48372
  -> {"labels": ["k8s:app=frontend", "k8s:env=prod", ...]}
/cilium/state/identities/v1/labels/
  SHA256("k8s:app=frontend;k8s:env=prod;...") -> 48372

分配流程:Cilium Agent 发现新 Pod,提取安全标签,计算 SHA256 哈希,在 etcd 中查找是否已有对应 Identity。如果有就复用;如果没有,分配新的数字 ID 并写回 CiliumEndpoint CRD。

方式二:CRD-based(Kubernetes CRD 模式)

$ kubectl get ciliumidentity 48372 -o yaml
apiVersion: cilium.io/v2
kind: CiliumIdentity
metadata:
  name: "48372"
security-labels:
  k8s:app: frontend
  k8s:env: prod
  k8s:io.kubernetes.pod.namespace: default

CRD 模式不需要额外的 etcd 集群,直接使用 Kubernetes API Server 存储 Identity 数据,降低了运维复杂度。

特殊保留 Identity

Identity 含义 用途
0 unknown 无法识别身份的流量
1 host 节点本身的流量
2 world 集群外部的流量
3 unmanaged 非 Cilium 管理的 Endpoint
4 health Cilium 健康检查
6 remote-node 其他节点的流量
7 kube-apiserver API Server

这些保留 Identity 允许你编写策略,例如”只允许来自 kube-apiserver 的流量访问 webhook endpoint”,而不需要知道 API Server 的具体 IP。

Identity 在 eBPF Datapath 中的传递

Identity 分配完成后,如何在数据面中使用?这是 Cilium 最精妙的设计之一。

同节点通信:ipcache Map 查找

// bpf/lib/identity.h(简化)
static __always_inline __u32 lookup_ip4_remote_identity(__be32 ip)
{
    struct ipcache_key key = {
        .lpm_key.prefixlen = 32,
        .family = ENDPOINT_KEY_IPV4,
        .ip4 = ip,
    };
    struct remote_endpoint_info *info;
    info = map_lookup_elem(&IPCACHE_MAP, &key);
    if (info)
        return info->sec_identity;
    return WORLD_ID;
}

IPCACHE_MAP 是 BPF LPM Trie Map,存储 IP 到 Identity 的映射。每个节点的 Cilium Agent 持续同步这个 Map。

跨节点通信(隧道模式):Identity 嵌入 VXLAN GBP 字段

封装后的包:
  [ETH][Outer IP][VXLAN Header + GBP: Identity=48372][Inner ETH][IP][TCP][Payload]

发送端 eBPF 程序将源 Identity 写入 VXLAN 的 GBP(Group-Based Policy)扩展字段,接收端直接读取,无需再查 ipcache。

跨节点通信(直接路由模式):依赖 ipcache

直接路由模式没有隧道头携带 Identity,接收端通过 ipcache Map 反查源 Pod IP 对应的 Identity。

CT Entry 与身份缓存

Cilium 的 Connection Tracking 表不仅记录连接状态,还缓存了 Identity:

// bpf/lib/conntrack.h(简化)
struct ct_entry {
    __u64 rx_packets;
    __u64 tx_packets;
    __u32 lifetime;
    __u16 flags;
    __u32 src_sec_id;     // 源 Identity 缓存
    __u32 ifindex;
};

连接建立后,源 Identity 被缓存在 ct_entry.src_sec_id 中,后续数据包直接从 CT 表获取 Identity,不需要每次查 ipcache – 这是关键的性能优化。


三、跨集群 Identity 同步:ClusterMesh

在多集群环境中,不同集群的 Identity 编号可能冲突。ClusterMesh 通过 clustermesh-apiserver 解决这个问题。

ClusterMesh 架构

$ cilium clustermesh enable --service-type LoadBalancer
$ cilium clustermesh connect --destination-context cluster-2

每个集群运行一个 clustermesh-apiserver(轻量级 etcd 代理),暴露本集群的 Identity 和 Service 信息。远端集群的 Cilium Agent 连接到这个 apiserver,将远端的 Identity 和 ipcache 数据合并到本地。

Identity 编号的全局唯一性

通过 cluster ID 前缀避免编号冲突:

cluster:
  name: cluster-1
  id: 1        # 范围:1-255

相同标签的 Pod 在不同集群中可能有不同的 Identity 编号,但 ClusterMesh 确保策略评估时正确匹配。

跨集群策略

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-cross-cluster-frontend
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
            io.cilium.k8s.policy.cluster: cluster-1
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP

策略基于 Identity(标签)而非 IP,即使跨集群 CIDR 完全不同,策略依然生效。


四、L7 Policy:协议级别的访问控制

超越 L3/L4 的需求

标准 NetworkPolicy 只能做”允许访问 80 端口”。但现实中的安全需求远比这复杂:“只允许 GET,禁止 DELETE”、“只允许访问 /api/v1/health”、“只允许向 Kafka 的 events topic 发布消息”。Cilium 通过 per-node Envoy proxy 实现 L7 策略。

当 CiliumNetworkPolicy 包含 L7 规则时,eBPF 程序将匹配的流量重定向到 Envoy。Envoy 解析应用层协议并执行策略检查。通过的流量转发到目标 Pod,被拒绝的返回 HTTP 403 或 TCP RST。

HTTP 策略

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

只允许 app=frontendapp=api-server 发起 GET /api/v1/* 和 POST /api/v1/orders(需 JSON Content-Type)。其他请求(DELETE、PUT、/admin 等)全部拒绝。

gRPC 策略

gRPC 在 HTTP/2 之上运行,Cilium 将 gRPC 方法映射为 HTTP/2 的 POST + path,因此复用 HTTP 规则语法:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-grpc-policy
spec:
  endpointSelector:
    matchLabels:
      app: user-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: api-gateway
      toPorts:
        - ports:
            - port: "9090"
              protocol: TCP
          rules:
            http:
              - method: POST
                path: "/user.UserService/GetUser"
              - method: POST
                path: "/user.UserService/ListUsers"

Kafka 策略

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-kafka-policy
spec:
  endpointSelector:
    matchLabels:
      app: kafka-broker
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: event-producer
      toPorts:
        - ports:
            - port: "9092"
              protocol: TCP
          rules:
            kafka:
              - apiKey: "produce"
                topic: "user-events"
              - apiKey: "metadata"
    - fromEndpoints:
        - matchLabels:
            app: event-consumer
      toPorts:
        - ports:
            - port: "9092"
              protocol: TCP
          rules:
            kafka:
              - apiKey: "fetch"
                topic: "user-events"
              - apiKey: "offsets"
              - apiKey: "metadata"

实现了 Kafka 读写分离:event-producer 只能写入,event-consumer 只能读取 – L3/L4 策略根本无法实现。

L7 策略的性能影响

场景 额外延迟 说明
纯 L3/L4(eBPF only) ~0 不经过 proxy
L7 HTTP/gRPC(Envoy redirect) 0.5-2ms 按需 redirect
L7 Kafka 1-3ms 协议解析较重

Cilium 的关键优势:只有需要 L7 策略的流量才经过 Envoy。没有 L7 策略的流量完全在 eBPF 内处理。这与 Istio 的 sidecar 模式不同 – Istio 中所有流量都要经过 sidecar proxy。


五、Mutual TLS:Cilium 的透明 mTLS

为什么需要 mTLS

Identity 解决了”你是谁”,但还需要解决”我怎么验证你确实是你声称的那个人”。没有加密和身份验证,攻击者可以伪造 IP 或 Identity。mTLS 提供加密、服务端认证和客户端认证三重保护。

Cilium 的透明 mTLS(基于 SPIFFE)

Cilium 1.14+ 支持透明 mTLS,基于 SPIFFE 标准:

$ helm upgrade cilium cilium/cilium \
    --set authentication.mutual.spire.enabled=true \
    --set authentication.mutual.spire.install.enabled=true

启用后:SPIRE Agent 为 Cilium 提供 SPIFFE SVID 证书;当策略要求 authentication.mode: required 时,eBPF 将流量重定向到 mTLS handshake 组件;双方交换证书完成握手;后续流量加密传输。对应用完全透明。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: require-mtls
spec:
  endpointSelector:
    matchLabels:
      app: payment-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: order-service
      authentication:
        mode: required
      toPorts:
        - ports:
            - port: "8443"
              protocol: TCP

SPIFFE Identity 与 Cilium Identity 的关系

SPIFFE ID 格式:spiffe://cluster.local/ns/default/sa/frontend(trust domain / namespace / service account)。

两者互补:Cilium Identity 是基于标签的数字编号,用于 eBPF 快速策略查找;SPIFFE Identity 是基于 X.509 证书的加密身份,用于 mTLS 验证。eBPF 做快速路径过滤,mTLS 做慢速路径验证和加密。

Cilium mTLS 与 Istio mTLS 的区别

维度 Cilium mTLS Istio mTLS
架构 透明,无 sidecar Sidecar proxy(Envoy)
性能开销 较低(内核态 + 按需) 较高(每 Pod 一个 Envoy)
粒度 按策略启用 全局或 namespace 级别
证书管理 SPIRE Istio CA (istiod)
协议支持 TCP/TLS HTTP/gRPC/TCP
资源消耗 低(共享 per-node Envoy) 高(每 Pod 一个 sidecar)
成熟度 较新(1.14+) 成熟(生产验证多年)

核心区别:Istio 为每个 Pod 注入 Envoy sidecar,所有流量经 sidecar 加解密。Cilium 在 eBPF 层完成拦截,只在需要 mTLS 时触发加密。千 Pod 集群中,Istio 需要运行 1000 个 sidecar(各约 50-100MB 内存),Cilium 只需每节点一个共享 Envoy。


六、零信任网络架构在 Kubernetes 中的落地

零信任的核心原则

零信任模型的核心是”永不信任,始终验证”。在 Kubernetes 中具体化为四点:默认拒绝(无明确允许则拒绝)、最小权限(只允许必需的资源访问)、身份感知(基于工作负载身份而非网络位置)、持续验证(每次请求都验证)。

Identity-based 安全模型与零信任决策流程

第一步:默认拒绝策略

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: default-deny-all
spec:
  endpointSelector: {}
  ingress:
    - fromEntities:
        - cluster
  egress:
    - toEntities:
        - cluster
    - toEndpoints:
        - matchLabels:
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
            - port: "53"
              protocol: TCP

默认拒绝所有流量,只保留集群内基础通信和 DNS 访问。

第二步:按需开放最小权限

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-policy
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      authentication:
        mode: required
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/.*"
              - method: POST
                path: "/api/v1/orders"
  egress:
    - toEndpoints:
        - matchLabels:
            app: database
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP

第三步:持续验证

当 Pod 标签变化时,Cilium Agent 重新计算安全标签,分配新 Identity,更新 eBPF Map,已有连接会被重新评估。如果新 Identity 不再被允许,连接立即断开。不存在”先通过验证,后续免检”的漏洞。

零信任成熟度模型

等级 能力 Cilium 对应功能
L0 无网络策略 默认状态
L1 L3/L4 默认拒绝 + 白名单 CiliumNetworkPolicy (L3/L4)
L2 Identity-based 策略 Cilium Identity + eBPF
L3 L7 协议感知策略 L7 Policy (HTTP/gRPC/Kafka)
L4 mTLS 加密 + 身份验证 SPIFFE + Cilium mTLS
L5 持续验证 + 可观测性 Hubble + Policy Audit Mode

七、Network Policy Editor:可视化策略管理

Cilium Policy Editor

Cilium 提供了 Web 版 Network Policy Editor(https://editor.networkpolicy.io/),支持可视化创建和调试网络策略。上传集群的 CiliumEndpoint 和 CiliumNetworkPolicy 资源后,Editor 自动绘制交互式网络拓扑图,展示所有 Endpoint、Identity、允许/拒绝的流量方向。

命令行策略调试

# 查看 Endpoint 策略状态
$ cilium endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS
2847       Enabled            Enabled           48372      k8s:app=frontend
3912       Enabled            Enabled           99001      k8s:app=api-server

# 模拟策略评估
$ cilium policy trace \
    --src-identity 48372 \
    --dst-identity 99001 \
    --dport 8080/TCP
Final verdict: ALLOWED

cilium policy trace 是调试利器,模拟数据包从源 Identity 到目标 Identity 的策略评估过程。

Hubble 策略可观测性

# 实时观察被拒绝的流量
$ hubble observe --verdict DROPPED
Oct 10 14:23:01: default/unknown-pod:54321 (ID:0)
  -> default/api-server-xyz:8080 (ID:99001)
  http-request DROPPED (HTTP/1.1 DELETE /api/v1/users)

# 按 Identity 过滤
$ hubble observe --from-identity 48372 --to-identity 99001
Oct 10 14:23:05: default/frontend-abc:38291 (ID:48372)
  -> default/api-server-xyz:8080 (ID:99001)
  http-request FORWARDED (HTTP/1.1 GET /api/v1/health)

八、实验:配置 Cilium L7 Policy

环境准备

# 创建 kind 集群
$ kind create cluster --name cilium-lab --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  disableDefaultCNI: true
  kubeProxyMode: none
nodes:
  - role: control-plane
  - role: worker
  - role: worker
EOF

# 安装 Cilium
$ helm install cilium cilium/cilium --version 1.16.4 \
    --namespace kube-system \
    --set kubeProxyReplacement=true \
    --set hubble.enabled=true \
    --set hubble.relay.enabled=true
$ cilium status --wait

部署测试应用

# test-app.yaml -- api-server(nginx 模拟三个端点)
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-nginx-conf
data:
  default.conf: |
    server {
        listen 80;
        location /api/v1/health { return 200 '{"status":"ok"}'; }
        location /api/v1/users  { return 200 '{"users":["alice","bob"]}'; }
        location /api/v1/admin  { return 200 '{"admin":true}'; }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 1
  selector: { matchLabels: { app: api-server } }
  template:
    metadata: { labels: { app: api-server, env: prod } }
    spec:
      containers:
        - name: api
          image: nginx:1.25
          ports: [{ containerPort: 80 }]
          volumeMounts: [{ name: conf, mountPath: /etc/nginx/conf.d }]
      volumes: [{ name: conf, configMap: { name: api-nginx-conf } }]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector: { matchLabels: { app: frontend } }
  template:
    metadata: { labels: { app: frontend, env: prod } }
    spec:
      containers:
        - { name: curl, image: "curlimages/curl:8.5.0", command: ["sleep","infinity"] }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: attacker
spec:
  replicas: 1
  selector: { matchLabels: { app: attacker } }
  template:
    metadata: { labels: { app: attacker } }
    spec:
      containers:
        - { name: curl, image: "curlimages/curl:8.5.0", command: ["sleep","infinity"] }
$ kubectl apply -f test-app.yaml
$ kubectl wait --for=condition=Ready pod -l app=api-server --timeout=60s
$ kubectl wait --for=condition=Ready pod -l app=frontend --timeout=60s

验证无策略时的访问

$ API_IP=$(kubectl get pod -l app=api-server -o jsonpath='{.items[0].status.podIP}')

# frontend 和 attacker 都能访问所有路径
$ kubectl exec deploy/frontend -- curl -s http://$API_IP/api/v1/admin
{"admin":true}
$ kubectl exec deploy/attacker -- curl -s http://$API_IP/api/v1/admin
{"admin":true}

应用 L7 策略:只允许 GET /api/v1/health

# l7-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-l7-policy
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "80"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/health"
$ kubectl apply -f l7-policy.yaml

验证 L7 策略效果

# frontend -> GET /api/v1/health:允许(200)
$ kubectl exec deploy/frontend -- curl -s -o /dev/null -w "%{http_code}" \
    http://$API_IP/api/v1/health
200

# frontend -> GET /api/v1/users:L7 拒绝(403)
$ kubectl exec deploy/frontend -- curl -s -o /dev/null -w "%{http_code}" \
    http://$API_IP/api/v1/users
403

# frontend -> DELETE /api/v1/health:方法不匹配,L7 拒绝(403)
$ kubectl exec deploy/frontend -- curl -s -o /dev/null -w "%{http_code}" \
    -X DELETE http://$API_IP/api/v1/health
403

# attacker -> GET /api/v1/health:Identity 不匹配,L3/L4 直接丢弃
$ kubectl exec deploy/attacker -- curl -s --connect-timeout 5 \
    http://$API_IP/api/v1/health
# 超时 -- 流量在 eBPF 层面被丢弃,连接无法建立

用 Hubble 观察策略决策

$ hubble observe --namespace default --follow

# 允许的请求:
#   default/frontend-xxx (ID:48372) -> default/api-server-xxx:80 (ID:99001)
#   http-request FORWARDED (HTTP/1.1 GET /api/v1/health)

# L7 拒绝:
#   default/frontend-xxx (ID:48372) -> default/api-server-xxx:80 (ID:99001)
#   http-request DROPPED (HTTP/1.1 GET /api/v1/admin)

# L3/L4 拒绝:
#   default/attacker-xxx (ID:67890) -> default/api-server-xxx:80 (ID:99001)
#   Policy denied DROPPED (TCP Flags: SYN)

三种场景清晰可见:L7 放行、L7 拒绝、L3/L4 拒绝。注意 L3/L4 拒绝发生在 TCP SYN 阶段,连接根本没有建立。


九、生产环境最佳实践

渐进式策略部署

不要一次性在生产环境启用默认拒绝。推荐的渐进路径:

# 阶段 1:审计模式 -- 只记录不拦截
$ cilium config set policy-audit-mode enabled

# 用 Hubble 收集流量模式
$ hubble observe --verdict AUDIT --output json > traffic-baseline.json

# 阶段 2:分析基线,生成策略
# 阶段 3:测试环境验证
# 阶段 4:关闭审计模式,启用强制执行
$ cilium config set policy-audit-mode disabled

Identity 规模管理

Identity 膨胀的常见原因:过多标签被纳入计算,或标签值组合过多(如 version=v1.2.3)。通过 labels 配置项精确控制参与 Identity 计算的标签。

$ cilium identity list | wc -l   # 监控 Identity 数量

监控与告警

# Prometheus 告警规则
groups:
  - name: cilium-identity
    rules:
      - alert: CiliumIdentityAllocationFailure
        expr: rate(cilium_identity_allocation_errors_total[5m]) > 0
        for: 5m
        annotations:
          summary: "Cilium Identity 分配失败"
      - alert: CiliumPolicyDeniedSpike
        expr: rate(cilium_drop_count_total{reason="Policy denied"}[5m]) > 100
        for: 2m
        annotations:
          summary: "策略拒绝流量突然增加"
      - alert: CiliumL7ProxyLatencyHigh
        expr: histogram_quantile(0.99, rate(cilium_proxy_upstream_reply_seconds_bucket[5m])) > 0.5
        for: 5m
        annotations:
          summary: "L7 Proxy 延迟过高"

十、深入对比:Cilium Identity vs. 传统安全模型

与 Kubernetes 标准 NetworkPolicy 的对比

维度 标准 NetworkPolicy Cilium Identity
安全原语 IP 地址 数字 Identity
匹配复杂度 O(n) iptables 线性匹配 O(1) BPF Map 哈希查找
规则更新 全量 iptables-restore 原子 Map 更新
L7 支持 HTTP/gRPC/Kafka
mTLS SPIFFE-based
跨集群 ClusterMesh
可观测性 无内置 Hubble

与 Istio 安全模型的对比

Istio 路径:
  App -> [iptables redirect] -> Envoy sidecar -> [mTLS] -> Envoy sidecar -> App
  每个 Pod 两次 iptables redirect + 两次 Envoy 处理

Cilium 路径:
  App -> [eBPF redirect] -> (按需) per-node Envoy -> [按需 mTLS] -> App
  一次 eBPF redirect + 按需 Envoy 处理

Cilium 优势:更低资源消耗(无 per-Pod sidecar)、更低延迟(纯 L3/L4 不经 proxy)、更简单运维。Istio 优势:更成熟的 L7 流量管理(故障注入、超时重试)、更丰富的可观测性(分布式追踪)、更广泛的协议支持、与 CNI 解耦。


十一、总结

本文从 IP-based 安全的局限性出发,深入拆解了 Cilium 的 Identity-based 安全模型和零信任网络架构。核心要点:

第一,Identity 替代 IP 是 Kubernetes 网络安全的必然方向。 Pod IP 是短暂的、不可靠的,基于 IP 的安全策略在 Kubernetes 中从根本上就是脆弱的。Cilium 用标签驱动的数字 Identity 替代 IP 作为安全原语,实现了与 Pod 生命周期解耦的安全模型。

第二,eBPF 数据面让 Identity 检查接近零开销。 Identity 在 CT 表、ipcache Map、VXLAN GBP 字段中流转,策略评估通过 O(1) 的 BPF Map 查找完成。这不是在 iptables 上面加一层抽象,而是在内核数据路径上实现了 Identity-aware 的安全。

第三,L7 策略将安全从端口级别提升到协议级别。 HTTP 方法、URL 路径、gRPC 方法名、Kafka topic 可以直接出现在安全策略中。只有需要 L7 策略的流量才经过 Envoy,其他流量在 eBPF 内完成。

第四,透明 mTLS 补齐了零信任的最后一环。 Identity 解决”你是谁”,mTLS 解决”我怎么验证你”。基于 SPIFFE 的透明 mTLS 不需要应用代码修改,不需要 sidecar 注入。

第五,零信任不是一个开关,而是一个渐进过程。 从审计模式开始,收集流量基线,生成策略,测试验证,最后生产环境强制执行。Hubble 提供全程可观测性支持。

下一篇我们将系统对比 eBPF 与 netfilter 两大数据面技术,深入分析它们在性能、可维护性和功能覆盖度上的差异。


By .