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

【Kubernetes 网络深度系列】Cilium 身份识别与零信任网络

文章导航

分类入口
kubernetesnetworking
标签入口
#Cilium#Identity#zero-trust#mTLS#SPIFFE#L7-policy#eBPF#ClusterMesh

目录

上一篇我们拆解了 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 解耦。


系列导航 - 上一篇:Network Policy - 返回目录:Kubernetes 网络深度系列 - 下一篇:eBPF vs Netfilter 安全对比

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .