上一篇我们拆解了 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=defaultIdentity 48372 代表的不是某一个 Pod,而是所有具有
app=frontend, env=prod 标签、运行在
default namespace 的 Pod。无论这些 Pod 的 IP
怎么变化,Identity 始终是 48372。
安全相关标签的筛选
不是所有 Pod 标签都参与 Identity 计算。Cilium 会自动排除
pod-template-hash、controller-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: defaultCRD 模式不需要额外的 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=frontend 对
app=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: TCPSPIFFE 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 中具体化为四点:默认拒绝(无明确允许则拒绝)、最小权限(只允许必需的资源访问)、身份感知(基于工作负载身份而非网络位置)、持续验证(每次请求都验证)。
第一步:默认拒绝策略
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: ALLOWEDcilium 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 disabledIdentity 规模管理
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 两大数据面技术,深入分析它们在性能、可维护性和功能覆盖度上的差异。