news 2026/9/13 7:10:13

Envoy GCP 认证过滤器(gcp_authn)实战:从 GCE 元数据服务器获取服务间认证令牌

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Envoy GCP 认证过滤器(gcp_authn)实战:从 GCE 元数据服务器获取服务间认证令牌

Envoy GCP 认证过滤器(gcp_authn)实战:从 GCE 元数据服务器获取服务间认证令牌

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

导读

在微服务架构中,服务之间需要相互通信,当目标服务是私有服务并要求调用方携带访问凭据时,服务间(service-to-service)认证就变得必不可少。Envoy 的envoy.filters.http.gcp_authnHTTP 过滤器解决了这一问题:它从 Google Compute Engine(GCE)元数据服务器自动获取认证令牌(identity token 或 access token),并将令牌注入到转发请求的头部中,从而让调用方无需在应用代码里手工处理令牌获取逻辑。读完本文,你将掌握该过滤器的配置字段、完整的 YAML 配置示例、令牌获取与缓存机制,以及源码层面的工作原理。

过滤器概述与适用场景

gcp_authn过滤器用于从 GCE 元数据服务器 获取认证令牌。其典型应用场景是:多个服务组成的架构中,服务彼此需要通信,且目标服务是私有的、要求凭据才能访问,此时需要服务到服务的认证(authenticating service-to-service)。

该过滤器的工作方式是旁路式的:它对请求头进行处理(decodeHeaders 阶段),如果成功获取到令牌,就把令牌写入请求头再继续转发;如果未能从认证服务器获取令牌,则请求仍然会被发送到目标服务,由目标服务在需要认证令牌时拒绝该请求。这一点从 gcp_authn_filter.cc 的实现可以确认:当路由不存在、无法获取 audience 时,过滤器只是记录统计并Continue,不会拦截请求。

过滤器配置说明

该过滤器通过类型 URLtype.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig进行配置。完整的字段定义位于 gcp_authn.proto,过滤器注册名envoy.filters.http.gcp_authn定义在 gcp_authn_filter.h。

GcpAuthnFilterConfig主要包含以下核心字段:

字段类型必填说明
http_uriconfig.core.v3.HttpUri否(已废弃)从 GCE 元数据服务器获取令牌的 HTTP URI。URL 格式为http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]。该字段已标记废弃,原因是它与 Google 认证库提供的 API 表面不一致;控制面不应试图覆盖元数据服务器 URI,集群与超时应改用独立的clustertimeout字段配置。为保持向后兼容,若未设置新的cluster/timeout字段,仍会使用该字段中配置的 cluster 与 timeout
retry_policyconfig.core.v3.RetryPolicy获取令牌失败时的重试策略,并非所有数据平面都支持
cache_configTokenCacheConfig令牌缓存配置,用于避免对同一请求重复查询 GCE 元数据服务器
token_headerTokenHeader指定将令牌写入哪个请求头。默认情况下(不设置该字段),令牌写入Authorization头,格式为Authorization: Bearer <token>。若设置了token_metadata_key,该字段不生效
token_metadata_keystring可选的动态元数据(dynamic metadata)键,用于保存令牌。元数据以过滤器配置名作为命名空间,优先级高于token_header
clusterstring发送流量到 GCE 元数据服务器所用的集群。并非所有数据平面都支持,数据平面也可以有自己的元数据服务器访问机制
timeoutgoogle.protobuf.Duration从 GCE 元数据服务器获取令牌的超时时间。校验规则要求0 <= timeout < 4294967296s
audienceAudience定义认证服务。该字段覆盖集群typed_filter_metadata中的定义

其中audience(接收方服务)信息默认通过目标集群的metadata字段提供(typed_filter_metadata,命名空间为envoy.filters.http.gcp_authn),也可以在过滤器配置内联定义,内联定义优先。在源码 gcp_authn_filter.cc 的retrieveAudience()中,逻辑是:先检查过滤器配置中是否包含audience,有则直接使用;否则从路由目标集群的typed_filter_metadata中查找并解包。

令牌缓存配置(TokenCacheConfig)

TokenCacheConfig用于避免对认证服务器(此处即 GCE 元数据服务器)进行冗余的重复查询。其唯一字段为:

字段类型默认值说明
cache_sizegoogle.protobuf.UInt64Value0缓存条目数上限。最大为INT64_MAX(受底层缓存实现约束)。默认值 0(即 proto3 默认值)表示默认禁用缓存;设置为其他值则启用缓存。校验规则<= 9223372036854775807

缓存的具体实现见 token_cache.cc:

  • 缓存键生成:对Audience消息做 hash,若存在客户端证书指纹(用于 bound token),再叠加指纹的 xxHash64,见generateCacheKey()
  • 防冲突校验:命中缓存后,会通过MessageDifferencer::EqualsAudience做深比较,并比对指纹,避免 hash 碰撞误命中。
  • 过期校验:缓存条目带有expires_at过期时间,查询时会叠加JwtVerify::kClockSkewInSecond时钟偏移;若已过期,则移除该条目并返回未命中。
  • LRU 淘汰:底层使用 LRU 缓存结构,超过cache_size时淘汰最久未使用的条目。

Audience 配置详解

Audience消息(gcp_authn.proto)是接收方服务的 URL,即调用方服务正在调用的目标服务。它通过集群的metadata字段提供给过滤器。除了基本的url外,Audience还支持多种令牌获取模式,且具有严格的优先级顺序:

字段说明优先级
urlaudience URL,用于获取unbound(未绑定)JWT最低
access_token若设置,过滤器获取unbound Access Token而非 JWT高于url
bound_jwt.url若设置,过滤器获取bound JWT(绑定客户端证书的 JWT),URL 必填(min_len: 1高于access_tokenurl
bound_access_token若设置,过滤器获取bound Access Token高于bound_jwtaccess_tokenurl
iam_access_token若设置,为指定服务账号获取 access token,要求 audience 内联定义在过滤器配置中最高

iam_access_token子消息包含:

  • account:服务账号邮箱或唯一 ID 的模板字符串,支持动态元数据,例如my-sa-%DYNAMIC_METADATA(my_filter:tenant_project)%@my-project.iam.gserviceaccount.com
  • authorization:授权请求头值的模板字符串,例如Bearer %DYNAMIC_METADATA(gcp_authn:token)%
  • scopes:可选的自定义 OAuth 作用域列表,默认作用域为https://www.googleapis.com/auth/cloud-platform,默认令牌生命周期为 1 小时(源码 gcp_authn_client_impl.cc 中请求体默认写死lifetime: "3600s",若未指定 scopes 则使用上述默认 scope)。

模板字符串在过滤器创建时通过Formatter::FormatterImpl解析(见 gcp_authn_filter.cc),请求到来时基于请求头与 streamInfo 解析出实际的 account 与 authorization。

完整配置示例

官方示例配置文件位于 gcp-authn-filter-configuration.yaml,下面分别给出资源(集群)层配置HTTP 过滤器链配置

资源(Resource)配置:目标集群与元数据服务器集群

static_resources: clusters: - name: cluster_0 # 指向模拟目标服务的集群,其 typed metadata 中包含 audience 信息。 load_assignment: cluster_name: cluster_0 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 0.0.0.0 port_value: 8000 typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} metadata: typed_filter_metadata: envoy.filters.http.gcp_authn: "@type": type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.Audience url: http://test.com # 用于访问 GCE 元数据服务器的集群 - name: gcp_authn type: STRICT_DNS connect_timeout: 5s dns_lookup_family: V4_ONLY load_assignment: cluster_name: "gcp_authn" endpoints: - lb_endpoints: - endpoint: address: socket_address: address: "metadata.google.internal" port_value: 80

关键点:

  • 目标集群(cluster_0):通过metadata.typed_filter_metadata.envoy.filters.http.gcp_authn提供Audience,其中url: http://test.com表示目标服务地址。过滤器在请求路由到此集群时,会从该元数据中读取 audience(详见上文retrieveAudience())。
  • 元数据服务器集群(gcp_authn):采用STRICT_DNS类型解析metadata.google.internal,端口 80,并设置 5s 连接超时与V4_ONLYDNS 查询族。该集群名与过滤器配置中http_uri.cluster(或新的cluster字段)对应。

HTTP 过滤器链配置

static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 8000 filter_chains: - filters: - name: "http" typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: HTTP2 stat_prefix: "config_test" route_config: name: "route_config_0" virtual_hosts: - name: "integration" domains: ["*"] routes: - match: prefix: "/" route: cluster: "cluster_0" http_filters: - name: "envoy.filters.http.gcp_authn" typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig http_uri: uri: "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]" cluster: "gcp_authn" timeout: 10s - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

关键点:

  • 过滤器链中envoy.filters.http.gcp_authn必须位于envoy.filters.http.router之前,以保证在路由转发前完成令牌注入。
  • http_uri.uri使用 GCE 元数据服务器的 identity 端点模板,[AUDIENCE]是占位符,实际 audience 来自集群 metadata(或过滤器内联audience),客户端在发请求时会将其填入audience查询参数(见 gcp_authn_client_impl.cc)。
  • http_uri.cluster: gcp_authn指向上一节定义的元数据服务器集群;timeout: 10s表示获取令牌的超时。生产环境建议改用等价的clustertimeout顶层字段(http_uri已废弃)。

令牌获取流程与源码原理

从 gcp_authn_filter.cc 与 gcp_authn_client_impl.cc 可以梳理出完整的处理链路:

  1. 请求头阶段decodeHeaders):获取路由,若路由不存在则直接Continue;随后根据目标集群解析 audience;对iam_access_token模式,会解析模板得到 account 与 authorization;对 bound 令牌模式,会通过集群传输层套接字匹配器解析客户端证书指纹(getClientCertFingerprint,见 gcp_authn_filter.cc)。
  2. 查缓存:若配置了令牌缓存,先用 audience(及指纹)查缓存;命中且未过期则直接注入令牌并Continue
  3. 异步取令牌:未命中缓存时,根据 audience 的令牌类型调用客户端相应方法:
    • fetchUnboundJwt:GET.../default/identity?audience=<url>,返回 JWT;
    • fetchUnboundAccessToken:GET.../default/token,返回 JSON(access_token+expires_in);
    • fetchBoundJwt:GET.../default/identity?audience=<url>&bindCertificateFingerprint=<指纹>,其中指纹需要双重 URL 编码(GCP 元数据服务器强制要求,源码注释N.B.: double-URL-encoding is REQUIRED);
    • fetchBoundAccessToken:GET.../default/token?bindCertificateFingerprint=<指纹>
    • fetchIamAccessToken:向iamcredentials.googleapis.com发送 POST 请求(/v1/projects/-/serviceAccounts/<account>:generateAccessToken),请求体为 JSON:{"scope": [...], "lifetime": "3600s"},携带模板解析出的Authorization头。 所有元数据服务器请求都会带上Metadata-Flavor: Google头,并且不发送 X-Forwarded-For(GCP 元数据服务器会拒绝带该头的请求,见 gcp_authn_client_impl.cc)。
  4. 响应解析:JWT 直接按字符串解析并读取exp作为过期时间;Access Token 从 JSON 中解析access_tokenexpires_in计算过期时刻;IAM Access Token 从 JSON 解析accessTokenexpireTime
  5. 注入请求头addTokenToRequest,见 gcp_authn_filter.cc):
    • 若配置了token_metadata_key:将令牌写入 streamInfo 动态元数据(命名空间为过滤器配置名),不进请求头;
    • 否则若配置了token_header:写入<name>: <value_prefix><token>
    • 否则默认写入Authorization: Bearer <token>
  6. 写缓存并继续解码onComplete):令牌获取成功后插入缓存(若启用),随后调用continueDecoding()继续过滤器链;失败则记录日志(令牌不会被注入)。
  7. 请求取消onDestroy):若请求在等待令牌时被销毁,会取消正在进行的异步请求。

此外,若设置了retry_policy,客户端会将其转换为路由级重试策略,默认重试条件为5xx,gateway-error,connect-failure,reset,并开启bufferBodyForRetry(见 gcp_authn_client_impl.cc)。

统计指标

过滤器会暴露以下计数器统计(定义于 gcp_authn_filter.h):

  • retrieve_audience_failed:从集群元数据解析 audience 失败次数;
  • empty_audience:audience 已配置但未指定任何令牌类型(url/access_token 等均为空)的次数;
  • client_cert_fingerprint_calculated:成功计算客户端证书指纹的次数。

可在 Envoy 管理接口或 metrics 端点观测这些统计,用于排查 audience 配置错误与 bound 令牌指纹问题。

测试验证

仓库为 gcp_authn 过滤器提供了完整的测试覆盖,位于 test/extensions/filters/http/gcp_authn 目录:

  • gcp_authn_filter_test.cc:过滤器单元测试(audience 解析、令牌注入、缓存命中/未命中、各令牌类型分支、统计计数等);
  • gcp_authn_client_impl_test.cc:客户端实现测试(URL 构造、双重编码、响应解析、重试、错误处理);
  • gcp_authn_filter_integration_test.cc:集成测试,验证从过滤器配置到元数据服务器 mock 再到目标集群的端到端链路;
  • token_cache_test.cc:令牌缓存查找、过期、指纹组合键等行为测试;
  • filter_config_test.cc 与 crypto_utils_test.cc:配置构建与证书指纹计算测试。

使用注意事项

  • 目标服务若不要求认证令牌,未获取到令牌时请求仍会被正常转发(由目标服务自行决定是否拒绝),因此该过滤器是"尽力注入"而非"强制拦截"。
  • 生产配置应使用cluster/timeout顶层字段替代已废弃的http_uri内嵌配置;http_uri仅保留用于向后兼容。
  • 若需绑定客户端证书的 bound 令牌,必须确保目标集群配置了 TLS 客户端证书,否则指纹计算失败会返回 500 本地响应(bound_token_fingerprint_unavailable)。
  • iam_access_token模式要求 audience 内联定义在过滤器配置中(不能只依赖集群 metadata),否则返回 500(iam_token_config_error)。
  • 在高并发场景建议配置cache_config.cache_size > 0以复用令牌、降低元数据服务器压力。
  • 令牌默认写入Authorization: Bearer <token>;如需自定义请求头或写入动态元数据,使用token_headertoken_metadata_key

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 7:05:57

千问 LeetCode 77. 组合 Python3实现

LeetCode 77. 组合&#xff08;Combinations&#xff09;是一道非常经典的回溯算法模板题。题目要求返回范围 [1, n] 中所有可能的 k 个数的组合。 针对这道题&#xff0c;这里为你提供两种主流的 Python3 实现方案&#xff1a; 方法一&#xff1a;回溯法 剪枝优化&#xff08…

作者头像 李华
网站建设 2026/9/13 7:05:39

智能井盖工业级设计:MCU选型与传感器防护实战

1. 智能井盖的工业级挑战&#xff1a;从实验室到暴雨现场 实验室里的智能井盖原型和实际投入使用的产品之间&#xff0c;隔着一条名为"工业级可靠性"的鸿沟。我曾参与过某省会城市智慧井盖项目的全周期开发&#xff0c;第一批投放的300个井盖在首场暴雨中就损失了近1…

作者头像 李华
网站建设 2026/9/13 7:04:35

汽车保险盒与继电器工作原理及故障排查指南

1. 汽车保险盒深度解析&#xff1a;从入门到精通作为一名在汽车电子领域摸爬滚打多年的"老司机"&#xff0c;我见过太多因为保险盒问题导致的车辆故障。记得去年冬天&#xff0c;有位朋友的车窗突然无法升降&#xff0c;在修理厂花了800元更换升降电机&#xff0c;结…

作者头像 李华
网站建设 2026/9/13 7:02:46

RK3588+双LQ50实现27B大模型端侧部署

1. 项目概述&#xff1a;这不是“跑个模型”&#xff0c;而是一次端侧AI硬件架构的重新定义 把27B参数量的大语言模型塞进M.2插槽——光看标题&#xff0c;很多人第一反应是“这不可能”。毕竟Qwen3.8-27B在标准服务器上动辄需要4A100 80GB才能流畅推理&#xff0c;显存占用超1…

作者头像 李华
网站建设 2026/9/13 7:02:35

职业发展多维视角:技术人如何突破成长瓶颈

1. 职业发展的多维视角&#xff1a;为什么只关注工作本身远远不够刚入行那会儿&#xff0c;我像大多数新人一样&#xff0c;把全部精力都放在提升专业技能上。每天研究最新的技术文档&#xff0c;反复练习业务代码&#xff0c;周末也在参加各种技术培训。直到有次晋升答辩&…

作者头像 李华