Cilium 零信任网络策略实战:L3/L4/L7 全栈访问控制
通过实际案例讲解如何用 CiliumNetworkPolicy 实现 Kubernetes 零信任网络,包括默认拒绝策略、基于标签的 L3/L4 控制、HTTP/gRPC 级别 L7 策略、DNS 策略和出口流量控制,覆盖微服务安全架构的核心场景。
零信任网络的核心原则是“默认不信任,按需显式授权”。在 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 的实时可观测性,安全团队可以先用“审计模式”(只观测不阻断)建立流量基线,再逐步收紧策略,避免一次性切换带来的业务中断风险。零信任网络不是一蹴而就的,从命名空间级默认拒绝开始,逐层添加最小权限策略,是最稳健的落地路径。
