首页
/ Istio Ambient Mesh 中 PeerAuthentication 的实现:从 ztunnel 转换机制到 Waypoint 信任边界

Istio Ambient Mesh 中 PeerAuthentication 的实现:从 ztunnel 转换机制到 Waypoint 信任边界

2026-09-05 09:37:21作者:凤尚柏Louis

本文解析 Istio Ambient Mesh 中 PeerAuthentication 资源的工作原理:与 sidecar 模式不同,Ambient 工作负载的 mTLS 策略并不直接下发给 ztunnel,而是由 istiod 按“网格级 → 命名空间级 → 工作负载级”的优先级规则计算出生效策略,再转换为 ztunnel 自定义的 Authorization xDS 资源进行 L4 层强制执行。读完本文,你将掌握 PeerAuthentication 在 ztunnel 与 Waypoint 代理两条链路上的完整转换逻辑、DENY 策略的组/规则/匹配语义,以及可直接复现验证的测试用例入口。

背景:Ambient 下 PeerAuthentication 语义为何不同

PeerAuthentication 资源定义了流量到达某个网格工作负载时是否必须通过 Istio mTLS 加密传输。Sidecar 模式下其语义相对明确,但 Ambient 工作负载的架构差异——节点级 L4 代理 ztunnel 取代了容器内 sidecar——决定了策略的执行方式必须相应调整:

  1. ztunnel 的目标是成为一个最小化 L4 代理,其 xDS 配置被刻意精简;
  2. 策略不能在 ztunnel 内部做复杂的继承计算,因此计算工作前移到控制面(istiod);
  3. 结果是 ztunnel 收到的永远是一份“已经算好”的、按工作负载粒度裁剪的最终策略。

PeerAuthentication 与 ztunnel

ztunnel 只消费两种 xDS 资源

ztunnel 仅支持两种(自定义)xDS 资源:

因此 ztunnel 不会直接收到 PeerAuthentication。当 istiod 检测到一条指向 Ambient 捕获工作负载的 PeerAuthentication 时,它会按 mesh-wide → namespace → workload 的优先级规则计算该工作负载的生效策略,并把该策略发送(以 Authorization 形式)给 ztunnel。

以文档给出的经典例子:这条 PeerAuthentication 要求选中 app: a 的工作负载整体 STRICT,仅 9090 端口例外为 PERMISSIVE:

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: strict-and-permissive-mtls
spec:
  selector:
    matchLabels:
      app: a
  mtls:
    mode: STRICT
  portLevelMtls:
    9090:
      mode: PERMISSIVE

它会被转换成如下的 Authorization

action: DENY
groups:
- rules:
  - matches:
    - notPrincipals:
      - presence: {}
- rules:
  - matches:
    - notDestinationPorts:
      - 9090
name: converted_peer_authentication_strict-and-permissive-mtls
scope: WORKLOAD_SELECTOR

上述策略的语义是:在 ztunnel 处拒绝未认证流量,除非其目的地端口是 9090

结合源码理解转换:convertPeerAuthentication

上面这个转换并非“理论描述”,仓库中有一个对应的完整黄金测试文件:输入 peer-authn-strict-and-permissive-port-mtls-in.yaml 恰好就是上文那条 STRICT + 9090 PERMISSIVE 的策略,而期望输出 peer-authn-strict-and-permissive-port-mtls.yaml 与文档给出的 Authorization 逐字段一致,这直接印证了转换行为的确定性。

转换的核心实现位于 authorization.goconvertPeerAuthentication 函数,从源码结构看,其关键设计如下:

  1. 只有“工作负载级 + 带端口级 mTLS”的策略才会产出转换结果。函数开头就检查:如果策略位于根命名空间、没有 selector、或者没有任何 portLevelMtls,直接返回 nil(这类策略等价于纯 STRICT 或纯 PERMISSIVE,不需要逐端口表达):

    // violates case #1, #2, or #3
    if cfg.Namespace == rootNamespace || pa.Selector == nil || len(pa.PortLevelMtls) == 0 {
        return nil
    }
    
  2. STRICT 顶层模式 → “无 principal 即拒绝”规则mode == STRICT 时,转换结果首先加入一条基于 notPrincipals: [presence] 的匹配——即:连接的 TLS 证书中若不存在 principal(未认证),命中 DENY。

  3. 非 STRICT 的端口 → 端口豁免规则。对于 portLevelMtls 中 PERMISSIVE/DISABLE 的端口,在顶层为 STRICT 的前提下,追加 notDestinationPorts: [port] 规则——连接目的地不是该端口时拒绝,从而把 9090 这类端口豁免出强制 mTLS。多个 STRICT 端口则各自生成独立的 Group(Group 之间是 OR 关系),实现“这些端口同样要求认证”。

  4. 全 STRICT 策略提前短路。如果顶层是 STRICT 且所有端口级也都是 STRICT,函数返回 nil:这等价于一条纯 STRICT 策略,由 xDS 服务器直接推送静态 STRICT 策略(常量 istio_converted_static_strict,见 authorization.go#L37-L39),无需逐端口展开。

  5. 命名规范:转换后的策略命名为 converted_peer_authentication_<原策略名>(通过 model.GetAmbientPolicyConfigName 生成),scope 固定为 WORKLOAD_SELECTORaction 固定为 DENY

Authorization 的匹配语义

转换产物的字段语义由 authorization.proto 定义,理解它对读懂转换结果至关重要:

层级 逻辑关系 说明
groups 之间 OR 至少一个 group 命中,策略动作生效
group.rules 之间 AND 组内所有规则必须同时命中
rule.matches 之间 AND 规则内所有条件必须同时成立
同一 Match 中多字段 AND 多类型字段同时设置时须全部满足
同字段多值 OR 如多个 principal 匹配任一即可

Match 支持 namespaces / principals / source_ips / destination_ips / destination_ports 及其 not_ 反义形式,字符串匹配支持精确、前缀、后缀与 presence(存在性)四种类型。scope 分为 GLOBALNAMESPACEWORKLOAD_SELECTOR 三级;PeerAuthentication 转换结果固定落在 WORKLOAD_SELECTOR,因为它的 selector 绑定了具体工作负载标签。

优先级解析与静态 STRICT 策略

“mesh → namespace → workload”的继承计算集中在 authorization.goconvertedSelectorPeerAuthentications 函数中,从源码结构看有几个值得注意的规则:

  • 同级冲突时最旧者获胜getOldestPeerAuthnCreationTimestamp 比较,同一层级存在多条无 selector 策略时,创建时间最早的生效(并有 Switch selected mesh policy to ... 的 Debug 日志)。
  • 工作负载策略只在非根命名空间生效:位于根(系统)命名空间且带 selector 的 PeerAuthentication 被忽略。
  • 生效策略是 STRICT 时引用静态策略effectivePeerAuthenticationKeys 会把 istio-system/istio_converted_static_strict 加入该工作负载的策略键集合;而一旦工作负载策略中出现了需要端口例外的混合模式(如 STRICT + 9090 PERMISSIVE),静态 STRICT 策略会被剔除,改发转换后的完整策略——避免“先整体拒绝、再端口豁免”的双重下发。
  • 合并(merge)逻辑:当根/命名空间策略为 STRICT、工作负载策略为非 STRICT 顶层但带有 STRICT 端口时,转换会把父级的 STRICT 语义合并进工作负载策略(shouldMergeStrict 分支),使下发的单条 Authorization 自包含全部约束。

用测试用例验证转换行为

文档指引读者直接阅读 TestRBACConvert 的测试用例(ambientindex_test.go#L1751)。该测试遍历 testdata 目录下所有 *-in.yaml 文件,调用 convertPeerAuthentication 后与同名黄金文件比对。目录中有 20 余组 PeerAuthentication 用例,覆盖了完整的模式组合矩阵,例如:

输入文件(输入策略) 覆盖场景
peer-authn-strict-in.yaml 纯 STRICT 工作负载策略(期望输出为空,走静态策略)
peer-authn-strict-and-permissive-port-mtls-in.yaml STRICT + 9090 PERMISSIVE 端口豁免(即文档主例)
peer-authn-strict-and-disable-port-mtls-in.yaml STRICT + DISABLE 端口,期望输出与 PERMISSIVE 端口相同
peer-authn-strict-and-strict-port-mtls-in.yaml 顶层与端口全 STRICT,等价短路
peer-authn-permissive-port-mtls-strict-in.yaml 顶层 PERMISSIVE 下的单个 STRICT 端口
peer-authn-permissive-root-strict-namespace-permissive-workload-in.yaml 根 STRICT、命名空间 PERMISSIVE、工作负载端口的多级合并

这些黄金文件可直接作为“策略 → L4 授权”映射的规范参考。

PeerAuthentication 与 Waypoint 代理

适用前提(原文档的明确标注):本节描述的 Waypoint 联动行为在文档写作时尚未完全实现,且依赖于 ztunnel hairpinning 的设计讨论。以下内容反映的是该机制的目标设计。

关键在于 ztunnel 转发时机:ztunnel 先在其 L4 层应用 TRANSPORT 类策略(即 Authorization),之后才把流量转发给 Waypoint 代理。由此产生两类行为:

  1. 目标工作负载的生效策略达到 STRICT 时:未认证流量会在到达 Waypoint 之前就被 ztunnel 拒绝——这正是上文 DENY 策略的落点;
  2. 生效策略为 PERMISSIVE(默认值)时:ztunnel 对 Waypoint 打开一条普通 TLS 的 HBONE 隧道(注意:这不是 mTLS),并在不呈递客户端证书的情况下转发流量。

这引出 Ambient 安全模型中最重要的一条约束:Waypoint 代理绝不能从入站连接中推导任何身份,即使该连接来自 ztunnel 的 hairpin 路径。换言之,所有走 TLS HBONE 隧道的流量都必须视为不可信;此后流量仍经由 ztunnel 返回(依然在同一条 TLS HBONE 隧道上)并被转发到目的工作负载。

PERMISSIVE 策略下未认证流量的路径

graph TD;
src[src pod]-->|plaintext port|ztunnel{"ztunnel (L4 policy applied here)"}
ztunnel{ztunnel}-->|TLS|wp{waypoint}
wp-->|mTLS|ztunnel
ztunnel-->|plaintext|dst[dst pod]

即:源 pod 以明文到 ztunnel 的本地端口;ztunnel 在此处应用 L4 策略;ztunnel 以 TLS(非 mTLS)到 waypoint;waypoint 返回;ztunnel 以明文送达到目的 pod。

认证请求到达被捕获目的端的路径

graph TD;
src[src pod]-->|15008|ztunnel{ztunnel}
ztunnel-->|HBONE|dwp{"destination waypoint (all policy applied here)"}
dwp{destination waypoint}-->|15008|dztunnel{destination ztunnel}
dztunnel-->|host network|dst[dst pod]

认证流量则经由 15008 端口的 HBONE mTLS 隧道直达目标 Waypoint,由 Waypoint 应用全部策略(包括 L7 策略),再经目的端 ztunnel 走宿主网络到达工作负载。

与 L4 AuthorizationPolicy 的边界

PeerAuthentication 转换只是 Authorization 资源的一个来源。同一个 convertAuthorizationPolicy 所在的 convertAuthorizationPolicy 函数还负责把 AuthorizationPolicy 的 L4 字段(IP、端口、principal、命名空间、ServiceAccount 及 when 条件中的 L4 属性,见 l4WhenAttributes)转换为 Authorization;若策略包含 HTTP 层属性,istiod 会剥离这些字段并写入“ztunnel 不支持 HTTP 属性,需部署 Waypoint 代理执行”的 Status 条件(httpRuleFmt 等模板)。这与本文主题一脉相承:Ambient 中 L4 强制在 ztunnel、L7 强制在 Waypoint,PeerAuthentication 作为纯传输层策略,天然完整落在 ztunnel 一侧。

小结

  • ztunnel 不消费 PeerAuthentication;istiod 计算生效策略后下发转换好的 AuthorizationDENY + WORKLOAD_SELECTOR),规则是 groups OR、rules AND、matches AND;
  • STRICT 顶层模式编译为“无 principal 即拒绝”,非 STRICT 端口编译为 notDestinationPorts 豁免,全 STRICT 策略短路为静态策略 istio_converted_static_strict
  • 同级策略冲突以 CreationTimestamp 最旧者获胜;多级继承的合并逻辑与 20 余组黄金文件测试一一对应,可通过 TestRBACConvert 复现验证;
  • 在 Waypoint 路径上,ztunnel 先执行 L4 策略再转发;PERMISSIVE 下走的是无客户端证书的普通 TLS HBONE 隧道,因此 Waypoint 必须将一切隧道流量视为不可信、绝不推导身份。
登录后查看全文
热门项目推荐
相关项目推荐