服务网格 E02:Cilium 的 eBPF、七层代理与认证
允许 TCP 为什么还可能拒绝报价
orders → inventory-v1 的 GET /quote?sku=paper 在四层被放行,不表示同一连接上的每个 HTTP 路径都获准。把 eBPF、相互认证和 mTLS 写成一个开关,会在排障时误把 L7 拒绝认作内核断网。按 Cilium 1.18 文档选择能力边界(2026-10-03 核验,网页实际标识 1.18.14);相互认证页面明确标为 Beta。这是候选文档基线,当前没有部署 Cilium,功能开关与内核/发行版兼容须在安装时再次核对;源码固定 v1.18.0 tag,不将文档补丁版本和源码标签混作同一构建。
图中的数据方向是 orders → 内核策略/转发 →(需要 L7 时)Envoy → inventory。返回值在反方向返回;源 identity、策略命中和代理是否真的收到请求各自需要证据。无 L7 规则时不能假定这个 HTTP 请求一定经过 Envoy。inventory 不调用 external-stub,它的只读报价也不产生合成 POST /reserve 账本条目。
四件事在四个位置
eBPF 程序在内核相关钩子处理连接、转发和基于身份的 L3/L4 策略;遇到需要解析 HTTP 的规则时,流量交给 Cilium 管理的 Envoy L7 代理检查路径、方法等属性。Envoy 执行七层过滤不意味着所有字节都由 eBPF 解析,也不意味着 Cilium 的所有策略都需要每 Pod 一个 Envoy sidecar。相互认证验证对端身份,和 WireGuard/IPsec 的节点间数据加密解决不同问题;一条链路可以只启用其中部分能力,不能从一次 200 推断认证、机密性或完整请求路径。
例子:允许 orders 的身份访问 inventory TCP 端口,但只允许 GET /quote。同源 GET /quote?sku=paper 预期到达 v1;同源 GET /admin 若配置为拒绝则应在 L7 层停止,库存应用没有对应处理记录。另起错误身份却请求 /quote,若 L4 身份策略拒绝,Envoy 未必看到它。核验须用同一个请求 ID 串起发送端、Cilium monitor/policy verdict、Envoy 访问日志、后端响应;单独一份 policy YAML 只是声明。源 identity 变动、L7 不支持的协议、直连 host-network 或转发路径例外都可能改变证据链。
以下是实验输入集合,非当前集群捕获的流量:
1 | |
| 能力 | 决策位置 | 哪一项证据不能代替它 |
|---|---|---|
| L3/L4 身份策略 | 内核数据路径 | /quote 返回 200 |
| HTTP 方法/路径策略 | 经 L7 代理时的 Envoy | CNI 已安装 |
| 对端认证、传输加密 | 各自启用的身份/加密链路 | eBPF 程序已加载 |
复跑前要回答的版本问题
在隔离集群冻结 Cilium、内核、CNI、Envoy 镜像 digest 与启用的 feature flags,查 1.18 Envoy 集成 和 相互认证文档 对应版本的支持状态;然后分别执行仅 L4、添加 L7、启用认证、启用透明加密的用例。记录密钥只允许公开元信息;没有覆盖的功能明确不宣称支持。当前无 kubectl 或 Cilium 数据面,上述对照 NOT_RUN。先修:02 Service 地址、19 身份、21 授权 与 20 的 mTLS。
同一份策略逐步增加约束
材料在 examples/service-mesh/electives/E02/,命名空间固定 sm-e02。workloads.yaml 部署 orders、unknown 和 inventory;应用对 /quote 与 /admin 都返回 200,并打印请求 ID。因此只有 L7 阶段才应出现代理产生的 403。这个后端特意不实现业务权限,避免应用自身 403 混入网络策略实验。
仅 L4 的完整 l4.yaml 如下:
1 | |
它只允许选中来源到库存 TCP 8080。第二阶段用同名资源替换为 l7.yaml,完整差异是添加 HTTP 方法和路径:
1 | |
两个文件必须替换同一个资源。额外保留一个宽松的允许策略,会因为允许规则组合而改变结论。本实验用不带查询字符串的 /quote,让路径匹配边界保持清楚。扩展到查询参数、URL 编码或路径重写时,必须分别测试代理实际匹配到的路径,不从这个样例推断全部情况。
认证阶段的 auth.yaml 在同一 ingress 项中增加:
1 | |
仓库保存了包含全部 L4/L7 字段的完整 auth.yaml,可以直接 apply。它要求已启用 Cilium/SPIRE 集成;auth-values.yaml 提供对应 Helm values。已有 Cilium 测试集群可以在保留原安装 values 的基础上追加配置;不要覆盖 kubeProxyReplacement、路由模式等与宿主有关的设置。
1 | |
上述 upgrade 只面向已由 Helm 安装的独立实验集群。官方 v1.18.0 相互认证教程把认证要求放在 ingress/egress 块,并要求检查 agent 的认证日志。认证握手与普通业务连接分开,认证缓存存在时,不应预期每个 HTTP 请求都有一次新握手。
运行三个阶段
1 | |
脚本每次 apply 同名策略并等待工作负载,保存命令、Pod imageID、策略和日志。L4 的判据是 orders 的两个路径都为 200;L7/auth 的判据统一为 /quote=200、/admin=403。unknown 发出的请求应在连接层失败;若获得任何 HTTP 响应,脚本判失败。脚本对 L7 拒绝使用收敛之后的新 ID,避免先前探测流量已经进入应用而干扰“被拒 ID 不应出现”的断言。
通过这三组功能判据还不能宣布认证或加密成立。应将 inventory Pod 节点、Cilium endpoint identity、源 identity 与 agent 认证日志对齐:
1 | |
需要认证细节时在隔离环境按官方教程临时启用 debug,并记录修改前后设置。目标证据包含同一 identity 对的认证要求、证书验证和认证成功记录;只有 HTTP 200 或 SPIRE Pod Ready 不够。测试跨节点路径时,应显式把源和目标安排到不同节点,节点相同的结果不能替代跨节点验证。
[PATTERN] 一次只增加一个执行层的约束。L4→L7 改变请求内容判决,L7→auth 改变来源可信前提。按阶段保存证据,才能找到行为第一次改变的位置。
失败与恢复不共用一个结论
L4 阶段若 /admin 已拒绝,先查其他命中策略或应用响应;这个阶段尚未加入 HTTP 规则。L7 阶段 /quote 也超时时,先查 Envoy 就绪与代理重定向,不把 TCP 失败解释成路径规则拒绝。auth 阶段才失败则检查 SPIRE 健康、身份条目和 agent 日志;现存 auth cache 可能让部分身份继续访问,不能以短时停 SPIRE 后仍成功断言认证未执行。
恢复基线可以重新 apply l7.yaml,撤去本例的认证要求,再复跑 quote/admin。要恢复 L4 对照则 apply l4.yaml;这会有意取消路径限制,所以只适合专用实验命名空间。实验完成删除 sm-e02,集群级 SPIRE 或加密配置恢复原安装 values。
传输加密需要另一轮跨节点实验:固定相同来源和目标,启用 WireGuard 或 IPsec,记录加密状态及节点间抓包。应用容器内能看到 HTTP 明文并不能否定节点间加密;节点抓包未找到字符串也不能独自证明加密已启用,因为抓错接口同样没有数据。本轮没有这类抓包,透明加密标为 NOT_RUN。
| 模式 | 观测 | 不能代替的证据 |
|---|---|---|
| 阶段替换同名策略 | L4 两路径允许,L7 仅 quote 允许 | 多份宽松策略并存 |
| 内容授权和身份认证分开 | 403 与认证身份日志分别保存 | 用 HTTP 200 证明认证 |
| 定位加密所在链路 | 跨节点接口与加密状态 | 应用容器内是否可见明文 |
练习与参考解答
- 画出
/admin被拒的最短可证路径。参考解答:发送端 → 内核四层允许 → L7 Envoy 按方法/路径拒绝;请求 ID 不应出现在库存处理日志。若监控只有 L4 拒绝,则不能说 L7 策略生效。 - 删除 L7 规则、保留四层允许和加密,能继续证明
/admin被拒吗?参考解答:不能。传输加密不是 HTTP 授权;应观察代理是否仍在路径中、重新发起同一来源请求,并检查是否有其他授权规则。
资料与系列导航
Cilium 1.18 Envoy 代理、相互认证;源码入口固定为 v1.18.0 proxy、auth,无本地源码实验。研究记录 writing-plans/service-mesh/research/E02.md。
系列导航:E01 Linkerd → E02 Cilium(本文) → E03 Proxyless gRPC。
