平台在 Gateway 上写了一条「全局限流 +
JWT」基线,应用团队又在 HTTPRoute 上贴
mergeType: StrategicMerge 的覆盖策略,升级到
Envoy Gateway v1.9.0 之后对象被 admission
拒绝;另一条 Lua EnvoyExtensionPolicy apply
成功,流量却没有任何脚本副作用。这两种「写了不生效」不是同一层失败:前者是
Policy Attachment 的父资源不合法,后者是
扩展 API
默认关闭。再往下,还有一类更隐蔽:策略已被翻译,但
EnvoyPatchPolicy 仍按旧 xDS 资源名打补丁。
本文只钉三件事:五类 Envoy Gateway 策略各自进入哪一层
IR/xDS;mergeType 在 v1.9 的合法目标;Lua
关闭与 Patch
键失效如何表现为「策略写了不生效」。不写每个字段的百科。BackendTLSPolicy
是 Gateway API 核心策略,落点在 第 7
篇,本文不展开。
本文是「Envoy Gateway / Gateway API」系列第 9 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 8 篇 · L4 路由 TCPRoute/UDPRoute 升 v1;未装 v1.6 CRDs 则静默跳过第 9 篇 · Policy attachment 五类策略的编译层、 mergeType仅 xRoute、Lua 默认关第 10 篇 · EnvoyProxy 与基础设施 数据面生命周期、GatewayNamespaceMode、remote infra 上一篇:L4 路由 · 下一篇:EnvoyProxy 与基础设施
版本锚定:Envoy Gateway v1.9.0(源码 tag v1.9.0,发布 2026-08-14)。机制对齐
gateway.envoyproxy.io/v1.9/Concepts / Gateway API Extensions / API 类型,以及 v1.9.0 Release Notes。Policy Attachment 模式钉 Gateway API GEP-713。无真实集群则不粘贴伪造kubectlstatus /egctl/config_dump。实验台账:未跑。
一、SecurityPolicy
SecurityPolicy 按 GEP-713 的 Policy
Attachment 挂到 Gateway API 对象上,不改 HTTPRoute
spec。v1.9 Concepts 把它归为 Security 类,目标是 Gateway 与
Route;任务文档补充:同命名空间内可挂
Gateway、ListenerSet、HTTPRoute、GRPCRoute、TCPRoute。挂
ListenerSet 只覆盖该 ListenerSet
里的监听器,不影响父 Gateway
自有监听器;sectionName
可再收窄到单个监听器或具名 route rule。
TCPRoute 上的能力是收窄的:官方只保证基于客户端 IP 的授权。JWT、API Key、Basic Auth、OIDC 不适用于 TCPRoute 目标——对象能挂上,不等于整份安全功能表都编译。
编译落点在 XdsIR 的 HTTP
安全过滤器,再经 xDS Translator 变成 HCM 链上的
jwt_authn / oauth2 /
ext_authz / cors /
csrf / rbac /
api_key_auth /
basic_auth。这是请求路径上的安全轴,不是
InfraIR、也不是 cluster 熔断。未设置 mergeType
时,官方规则是 最具体者获胜(route rule
> route > ListenerSet listener > ListenerSet >
Gateway listener > Gateway,且 ListenerSet 路径与 Gateway
自有 listener 是兄弟作用域,不互相覆盖)。同级多条策略按
creationTimestamp 再按 namespaced name
排序。
v1.9.0 在这条轴上钉了两处新能力,都不要当成「默认全开」:
spec.csrf:对 POST/PUT/DELETE/PATCH 校验Origin是否匹配目的地或additionalOrigins;shadowFraction未设则默认 0%(全部强制),设成 100% 才是只记csrf.request_valid/csrf.request_invalid、一律放行。- 授权规则可写 CEL。表达式必须求值为 true 才匹配;请求属性(path、host、method、headers 等)在授权时可用,响应属性在请求完成前不可用于放行决策。
JWT 在 v1.9 还有一处 xDS
名破坏面:生成的 listener 里,JWT provider /
requirement 名称改为
内容派生、可跨路由去重的稳定名。以前按路由派生的
jwt_authn.providers 或
requirementMap 键,会让
EnvoyPatchPolicy 与 extension server
对不上。
策略是否「附上了」要看 Policy 自己的
status,以及目标对象上是否出现 GEP-713 建议的
PolicyAffected 类条件。GEP 把 Discoverability
写成:应用开发者最好只看自己的 HTTPRoute
就能知道生效策略从哪来;实现做不到时,排障跳数就会超过一次
kubectl get。Envoy Gateway 用
status.ancestors
回报附着结果,并给被盖住的父策略打
Overridden=True。v1.9 还把每个策略的
status.ancestors 在添加过程中就截到 CRD
上限,避免「很多策略打同一目标」时翻译延迟按平方涨——这是性能修复,也说明
fan-out status 仍是 713 点名的那个问题。
mergeType
的限制单独放第六节。这里先记住:基线可以挂
Gateway,合并声明只能写在 xRoute
上。把「Gateway 上的 SecurityPolicy
Accepted」读成「所有子路由都强制
JWT」是错的:未合并时最具体的路由策略会整份替换基线。
二、BackendTrafficPolicy
BackendTrafficPolicy 管的是 Envoy
到后端
的连接与弹性:熔断、重试、超时、健康检查、负载均衡、限流。Concepts
把它标为 Traffic Handling,目标 Gateway 与 Route,并写明
Most specific configuration wins。v1.9
文档列出的目标包括
Gateway、ListenerSet、HTTPRoute、GRPCRoute、TCPRoute、UDPRoute、TLSRoute。同样必须与策略同命名空间。
编译落点跨 RDS 路由动作与 CDS
cluster:限流、重试、超时更靠近
route;熔断、主动健康检查、LB 更靠近 cluster。不要把它和
ClientTrafficPolicy 搞反——后者改的是 listener
上的下游连接。
v1.9.0 把 仅 shared 的全局限流规则
从生成 xDS 的 route.rateLimits 挪到
typedPerFilterConfig。对
EnvoyPatchPolicy / extension server
这是破坏性变更:继续按 route.rateLimits 匹配
shared-only 规则,升级后会静默打空。这不是「限流 CR 没
Accepted」,而是 补丁打在上一版布局上。
mergeType 合法目标比 SecurityPolicy
更宽:HTTPRoute、GRPCRoute、TCPRoute、UDPRoute、TLSRoute。挂
Gateway / Gateway listener / ListenerSet / ListenerSet
listener 会被拒。官方合并示例是平台在 Gateway 写 shared
全局滥用上限、应用在 HTTPRoute 上
StrategicMerge 一条更紧的非 shared
规则——合并后两条都生效。JSONMerge 走 RFC
7396,数组整段替换;StrategicMerge
对复杂结构做战略合并。未设 mergeType
时不合并,只留最具体那条。
三、ClientTrafficPolicy
ClientTrafficPolicy 管 下游客户端到
Envoy listener 的行为:TLS 终止与
mTLS、连接超时、客户端 IP 探测、路径规范化、HTTP/1/2/3
旋钮。v1.9 合法目标只有 Gateway 与
ListenerSet(均可带
sectionName)。不能挂
HTTPRoute。 把下游超时写在 Route 上的
ClientTrafficPolicy 不会进入翻译输入集。
编译落点在 XdsIR 的 listener /
下游连接选项,对应 LDS。优先级是:带
sectionName 的 listener 级 > ListenerSet
整份 > Gateway 整份。被更具体策略盖住的祖先会报
Overridden=True。挂 ListenerSet 时,策略 status
的 ancestorRef 指向 ListenerSet 本身,而不是父
Gateway。
v1.9.0 收紧了
spec.clientIPDetection:必须且只能设
xForwardedFor、customHeader、directSourceIP
之一。以前能过的空对象 clientIPDetection: {}
现在被 CEL 拒绝。directSourceIP 把下游 TCP
源地址当作客户端 IP,用来在 NLB / 外部流量策略为 Local 这类
L4 透明拓扑里解锁 SecurityPolicy 的
clientIPGeoLocations。这是 listener
身份轴,不是后端限流轴。
本策略 没有
mergeType。下游连接参数不走「子路由合并父
Gateway」那条继承模型;要分层就靠 section 特异性,不要套用
SecurityPolicy 的合并直觉。
四、EnvoyExtensionPolicy(Lua 默认关)
EnvoyExtensionPolicy 把
Wasm、ext_proc、Lua、dynamic module 挂进 HTTP
过滤器链。Concepts 目标为 Gateway、Route、Backend;v1.9 起
ListenerSet 也可作 targetRef。Lua
任务文档写明:同时挂 Gateway 与 HTTPRoute
时,路由侧优先。v1.9 API 也为该策略增加了
mergeType,且 godoc 写明 目前只能设在
xRoute 目标上。
Lua 在 v1.9.0 默认关闭。要开,必须在
EnvoyGateway 配置里设
extensionApis.enableLua: true。disableLua
已废弃,将在后续版本删除;两个字段互斥。官方 Lua
任务把默认关闭写成安全默认:脚本在 Envoy
进程内执行,没有强沙箱。没开 enableLua
时,一条看起来 Accepted 的 Lua 策略
不会把过滤器编进 listener。这是扩展 API
门,不是 IR 翻译 bug。
v1.9.0 还改了 Lua 的 xDS
布局:源码从每条路由的 LuaPerRoute
覆盖,挪到 listener 级 Lua
filter,避免内存随路由数线性涨。过滤器名字与配置布局都变了。继续匹配旧的
envoy.filters.http.lua/ 键,Patch
会打空。作为补偿,v1.9 增加
filterContext:同一份共享脚本可通过
request_handle:filterContext()
按路由取键值,而不必再靠 per-route 源码覆盖。
Wasm / ext_proc / dynamic module 仍走同一 CRD,但
不被 enableLua
门拴住。不要把「EEP 能挂」理解成「Lua 已启用」。
五、EnvoyPatchPolicy
EnvoyPatchPolicy 用 RFC 6902 JSON Patch 改
已经生成的 xDS,不是再走一遍 Gateway API
Translator。默认关闭,需
extensionApis.enableEnvoyPatchPolicy: true。官方任务把它标为不稳定:Envoy
API 字段会删,Envoy Gateway 也可能改资源
name。合法目标默认是同命名空间的
Gateway;mergeGateways
打开时改为挂 GatewayClass。多条 Patch 按
priority 升序应用(int32.min
最高)。
这是五类策略里唯一主要停在 xDS Translator 之后 的。前面四类进入 XdsIR 再变成 LDS/RDS/CDS/EDS/SDS;Patch 假定你已经知道那些资源的 type URL 与名字。v1.9.0 让这个假设更脆,至少这些键会失效:
| 旧匹配面 | v1.9.0 之后 |
|---|---|
| 路由派生的 JWT provider / requirement 名 | 改为内容派生的稳定名 |
shared-only 全局限流的
route.rateLimits |
改为 typedPerFilterConfig |
每路由 LuaPerRoute / 旧 Lua filter 名 |
listener 级 Lua filter |
DNS cluster 顶层 dns_refresh_rate /
respect_dns_ttl /
dns_lookup_family |
envoy.cluster.dns(DnsCluster)typed
config |
每资源 WellKnownCACertificates: System 的
SDS secret 名 |
共享 system_ca_certificates(见第 7
篇) |
| Unix socket SDS cluster 的路径派生名 | 带 hash 后缀以防碰撞 |
官方从 v1.5 起引入 xDS Name Scheme V2(listener 从
ns/gw/listener 迁到 tcp-80
这类名字),v1.9 仍由 XDSNameSchemeV2 runtime
flag 控制,默认关,计划 v1.10 默认开。Patch
若按旧 listener 名编写,即使策略
Programmed=True,也可能补丁打在不存在的资源上。Accepted
与「补丁命中了你以为的那棵树」不是同一句话。
官方安全警告把 Patch 标成高 CIA 风险:可注入任意配置,并提到凭据泄露面(CVE-2026-22771)。本文不复述利用步骤,只把结论钉住:Patch 是逃逸舱,不是日常 Policy Attachment。
六、v1.9 mergeType 仅 xRoute
未设 mergeType
时,五类策略里带层级的那些都走
最具体者获胜,不合并。mergeType
是显式退出这条默认规则:子策略与「路由附着层级上最近的父策略」做
StrategicMerge 或
JSONMerge。父策略仍可挂 Gateway /
ListenerSet——被拒绝的是 在父资源上声明
mergeType 字段。
v1.9.0 admission 把这件事写成破坏性变更:
| 策略 | mergeType 允许的目标 |
被拒的目标 |
|---|---|---|
| SecurityPolicy | HTTPRoute、GRPCRoute、TCPRoute | Gateway、Gateway listener、ListenerSet、ListenerSet listener |
| BackendTrafficPolicy | HTTPRoute、GRPCRoute、TCPRoute、UDPRoute、TLSRoute | 同上 |
| EnvoyExtensionPolicy | API:仅 xRoute | 按 godoc,不能设在父资源上 |
| ClientTrafficPolicy | 无此字段 | — |
| EnvoyPatchPolicy | 无此字段(用 priority) |
— |
已有对象若在父资源上带了 mergeType,Release
Notes 要求:CRD
升级后、更新这些对象之前,先删掉该字段。CEL 卡的是
create/update,不是「控制器运行时忽略」。表象是 kubectl
被拒,不是流量层 500。
合并时,路由策略找最近父策略:直连 Gateway 则先 listener 级再 Gateway 级;经 ListenerSet 则先 ListenerSet listener、再 ListenerSet、再父 Gateway。不会与兄弟作用域的 Gateway listener 策略合并。同一功能两边都配(例如两边都写 CORS)时,子策略该功能胜出。Secret / backend 引用按 当初声明该字段的策略的命名空间 解析,不改挂到路由之后就跑到路由命名空间去找。
flowchart TD
cr["xPolicy CR"] --> attach{"targetRef kind"}
attach -->|"Gateway / ListenerSet"| ir["XdsIR most-specific-wins"]
attach -->|"xRoute without mergeType"| ir
attach -->|"xRoute with mergeType"| merge["Merge with closest parent"]
merge --> ir
attach -->|"mergeType on parent"| reject["Admission reject"]
ir --> xds["xDS Translator LDS RDS CDS"]
xds --> patch{"EnvoyPatchPolicy"}
patch -->|"JSON Patch"| envoy["Envoy dataplane"]
patch -->|"miss old keys"| miss["Patch no-op"]
图:策略从 targetRef 进入 XdsIR 或被
admission 拒绝;Patch 发生在 xDS
生成之后。节点为英文;正文解释合法组合。
下表是 targetRef 合法组合(口径:v1.9
Concepts / 各策略任务文档 / Release
Notes;「—」表示该文档未把该 kind
列为目标,不表示控制器会接受)。mergeType
列只描述「字段是否允许出现」,不表示父资源不能作为合并的
源。
| 策略 | Gateway | Gateway listener | ListenerSet | HTTPRoute | GRPCRoute | TCPRoute | UDPRoute | TLSRoute | GatewayClass | Backend | mergeType |
|---|---|---|---|---|---|---|---|---|---|---|---|
| SecurityPolicy | Y | Y | Y | Y | Y | Y* | — | — | — | — | 仅 HTTP/GRPC/TCP Route |
| BackendTrafficPolicy | Y | Y | Y | Y | Y | Y | Y | Y | — | — | 仅五种 xRoute |
| ClientTrafficPolicy | Y | Y | Y | — | — | — | — | — | — | — | 无 |
| EnvoyExtensionPolicy | Y | 文档未单列 | Y | Y | 含于 Route | — | — | — | — | Y | API:仅 xRoute |
| EnvoyPatchPolicy | Y | — | — | — | — | — | — | — | 仅 mergeGateways | — | 无 |
* TCPRoute 上 SecurityPolicy 仅 IP 授权。所有策略默认与目标同命名空间。
落到本系列五轴,「策略写了不生效」至少要先点名层,再改 YAML:
| 表象 | 先怀疑的轴 | v1.9 可核对锚点 |
|---|---|---|
| kubectl 创建/更新被拒 | 附着 / admission | mergeType 挂在 Gateway / ListenerSet;空的
clientIPDetection: {};不合规的
apiKeyAuth.extractFrom |
| 对象存在,Lua 无副作用 | 扩展 API 门 | 未设 extensionApis.enableLua |
| 对象 Accepted,流量仍无 JWT/限流 | IR:最具体者获胜把父策略盖掉;或 TCPRoute 挂了不适用的 JWT | 父策略 Overridden=True;TCPRoute 仅 IP
授权 |
| 策略与 Patch 都 Programmed,行为仍是旧的 | xDS 布局变了 | JWT 稳定名、typedPerFilterConfig、listener
级 Lua、V2 资源名未启用 |
| 安全过滤器在、上游仍 503 | Envoy 数据面 / 后端 | 不在本篇;见 envoy 排障与第 13 篇 |
第 4
篇 把 Policy 折叠进同一次
Translate:Security / Backend / Client / EEP 进
XdsIR,再交给 xDS Translator;它们
不 进 InfraIR,不会让 Infra Manager
多建一个 Deployment。EnvoyPatchPolicy 发生在
XdsIR→xDS 之后,所以 IR dump 对不上 Patch
后的树——这是排障时必须换轴的原因。
七、谱系、争论与开放问题
Ingress 注解(无类型、不可移植)
→ Gateway API 核心资源保持可移植(角色分离)
→ GEP-713 Policy Attachment(元资源;Memorandum)
→ Direct / Inherited 曾拆成 GEP-2648 / GEP-2649,现已并回 713
→ Envoy Gateway 实现:最具体者获胜 + 可选 mergeType
→ v1.9:mergeType 只允许写在 xRoute 上
GEP-713 把 Policy 定义成「不改目标 spec、用独立 CR 增强行为」的元资源,并点名两个尚未被 API 消灭的工程问题:Discoverability(开发者看自己的 HTTPRoute 无法一眼看出生效策略从哪来)和 Fanout status(一条父策略要回写许多子孙 status)。GEP 自身的状态是 Memorandum:它是模式,不是一个新的核心 kind。Direct / Inherited 的独立 GEP 已标 obsolete,请以 713 正文为准,不要再把 2648/2649 当现行规范。
合并语义上,Envoy Gateway
暴露两条已标准化的补丁代数:JSONMerge 是 RFC
7396(数组替换);StrategicMerge 是 Kubernetes
战略合并。这不是 GEP-713 规定的唯一合并函数——713
只要求实现声明 \(f(\mathrm{spec},\mathrm{spec})\to\mathrm{spec}\)。v1.9
把「谁可以声明 merge」从父资源上拿掉,等于用 admission
收窄继承声明的位置,避免平台对象自己说「请把我和下级合并」造成的双向歧义。
争论:最具体者获胜(可预测、排障跳数少)vs
继承合并(平台基线 + 租户叠加)。713
把发现性问题写成开放工程;Envoy Gateway v1.9
选择「默认不合并、合并声明只能写在子路由」。反例是:平台无法再在
Gateway 策略上放 mergeType
表达「强制向下合并」——基线必须作为 被合并的父对象
存在,合并意图只能由路由策略发出。多租户里这更接近「应用选择加入」,而不是「平台强制覆盖」。
713 还把「一次 kubectl
跳数」写成可检验的发现性指标:从开发者拥有的 Route
出发,要几次才能知道哪条策略生效、内容是什么。Inherited
策略把有效目标(policy
scope)扩到整棵子树,跳数天然变长;Direct
策略跳数短,但平台基线要复制到每个 Route。Envoy Gateway
的默认(最具体者获胜、可选子侧
merge)是在这两极之间切一刀。v1.9 禁止在父资源上写
mergeType,等于拒绝「父对象声明继承代数」这条路,只保留「子对象请求与最近父合并」。
开放问题:在 mergeType
不能出现在 Gateway / ListenerSet
之后,平台级强制策略(「所有路由必须叠加上这条
JWT」)如何既可发现、又可机械证明没有路由用最具体者获胜把基线盖掉?713
的 Discoverability 段落仍适用;本系列第 13
篇只把「策略写了不生效」收束到 admission / Lua 门 /
Patch 旧键,不承诺 status 足以当 SLO。另一问:Patch
作为逃逸舱与类型安全 Policy 长期并存时,xDS
名稳定性是否应成为发行保证,而不是每条 Release Notes
里的破坏清单?
八、小结
- 五类策略不是同一层:Client 进 LDS;Security / EEP 进 HCM 过滤器;Backend 跨 RDS 与 CDS;Patch 打在生成后的 xDS 上。
- 未设
mergeType则最具体者获胜;v1.9 起mergeType只能出现在 xRoute 目标,挂父资源会被拒。 - Lua EEP 默认关,需
enableLua;disableLua已废弃。 - JWT 稳定名、shared 限流
typedPerFilterConfig、listener 级 Lua,都会让旧 Patch 键失效。
下一篇离开策略编译,问数据面 Pod 的生命周期归谁。
九、参考资料
规范 / 官方文档(A)
- Envoy Gateway v1.9.0 Release Notes(Lua
默认关、
mergeType仅 xRoute、JWT 稳定名、shared 限流布局、Lua 迁 listener、csrf/ CEL)。 - Envoy Gateway v1.9 Concepts 资源表;SecurityPolicy / BackendTrafficPolicy / ClientTrafficPolicy 概念页。
- Envoy Gateway v1.9
API:
MergeType、enableLua、CSRF/ CEL 授权、EnvoyPatchPolicySpec。 - Envoy Gateway v1.9 任务:Lua
Extensions(
enableLua);Envoy Patch Policy(JSON Patch、Name Scheme V2)。 - Gateway API GEP-713 Metaresources and Policy Attachment(Memorandum;Discoverability / Fanout;GEP-2648/2649 已并回)。
- RFC 6902 JSON Patch;RFC 7396 JSON Merge Patch。
- Envoy Gateway v1.9 System Design / Translator:Policy 折叠进 XdsIR 的路径见第 4 篇。
源码(A)
envoyproxy/gatewaytag v1.9.0:远程基础设施契约不在本篇;策略字段以发布的 CRD/API 类型为准。开写时未在本仓库 checkout 全量 tag 做 CEL 行号摘录。
核心论文 / 模式文
- Gateway API GEP-713(模式定义,非 peer-reviewed 论文)。
- 对照:Ingress 注解扩展失败被 Gateway API Extensions 概念页当作类型安全 CRD 的动机,而不是独立论文结果。
实验 / 工具
- 本篇无集群命令输出;
mergeType挂 Gateway 被拒、Lua 未enableLua的表象均 未跑,只写官方语义。
站内对照
- 系列目录
- 第 8 篇 · L4 路由
- 第 10 篇 · EnvoyProxy 与基础设施
- 第 4 篇 · Translator 与 IR
- 第 7 篇 · TLS /
SDS(
system_ca_certificates使旧 Patch 名失效) - Envoy 数据面 xDS 资源树
← 上一篇:L4 路由 · 系列目录 · 下一篇:EnvoyProxy 与基础设施
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】角色与附着:GatewayClass / Gateway / Route 各保证什么
钉清 Gateway API v1.6.1 角色分离在 Envoy Gateway v1.9.0 里的失败语义:parentRefs 与跨 namespace、ReferenceGrant 握手、Accepted 相对 Programmed / ResolvedRefs,以及 YAML 已 apply 仍无流量时该先查哪条条件。
【Envoy Gateway】Gateway API Translator 与 IR:对象如何变成 XdsIR / InfraIR
钉清 Envoy Gateway v1.9.0 为何用中间表示解耦 Gateway API 与 Envoy 资源树:XdsIR 与 InfraIR 的分工、Translate 的顺序与依赖,以及 status 为何必须在同一次翻译里计算。
【Envoy Gateway】HTTPRoute / GRPCRoute:匹配进入 RDS,阴影要靠 RouteRulesOverlap
钉 HTTPRoute/GRPCRoute 的附着、hostname、matches/filters/backendRefs 如何进入 RDS;对照 Gateway API v1.6.1 匹配优先级,以及 v1.9 RouteRulesOverlap 对同 listener 相同匹配的警告。