深入解析 gRPC EndpointInfo 手握手器:连接端点信息的采集与授权消费链路
【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc
导读
在 gRPC 的握手(Handshake)阶段,连接的对端与本地地址信息是安全授权(如 RBAC)、审计与排障的重要输入。本文以 gRPC C++ 核心库中 endpoint_info 目录为切入点,剖析EndpointInfo数据结构的定位、EndpointInfoHandshaker的实现细节,以及端点地址如何通过ChannelArgs一路传递到授权评估模块(evaluate_args.cc)。读完本文,你将掌握 gRPC 握手框架的扩展方式、端点信息采集的完整调用链,以及如何基于源码与测试验证这一机制。
EndpointInfo:连接端点的信息载体
根据 endpoint_info/AGENTS.md 的说明,本目录承载的是EndpointInfo类——一个包含连接本地端点(local endpoint)与远端端点(remote/peer endpoint)信息的数据结构。它的核心价值在于:为握手器(handshaker)提供一种便捷的方式来访问连接双方的地址信息。
从源码结构看,该目录的实际实现由两个文件构成:
- endpoint_info_handshaker.h:声明端点信息相关的两个 ChannelArgs 键,以及注册函数
RegisterEndpointInfoHandshaker; - endpoint_info_handshaker.cc:实现
EndpointInfoHandshaker与EndpointInfoHandshakerFactory,完成地址采集与写入。
需要说明的是,目录内并未单独存放名为endpoint_info.h的类定义文件,EndpointInfo的实际实现被内联在EndpointInfoHandshaker及其工厂类中,这一点在阅读源码时需要留意。
握手框架:EndpointInfo 所处的执行环境
要理解 EndpointInfo 手握手器,先要了解它所依附的 gRPC 握手框架。在 src/core/handshaker/AGENTS.md 中,握手框架被描述为“在客户端发送首个请求之前,对连接执行初始握手的可插拔机制”,典型用途包括 HTTP CONNECT(客户端侧)与各类安全初始化(TLS、ALTS 等)。
框架的核心抽象在 handshaker.h 中定义:
Handshaker(handshaker.h):单个握手操作的抽象基类,通过DoHandshake执行具体握手,通过name()标识自身;HandshakeManager(handshaker.h):按加入顺序串行调用一组 handshaker,前一个完成后将HandshakerArgs传递给下一个;HandshakerArgs(handshaker.h):在 handshaker 之间流转的输入/输出参数结构,包含endpoint(grpc_endpoint指针)、args(ChannelArgs)、read_buffer、exit_early、deadline等成员。
关键在于HandshakerArgs.args是一个贯穿整个握手链的可变ChannelArgs——每个 handshaker 都可以向其中写入或改写键值对。EndpointInfo 手握手器正是利用这一点,把端点地址以 ChannelArgs 的形式注入到后续流程中。
EndpointInfoHandshaker 实现解析
地址写入的核心逻辑
EndpointInfoHandshaker::DoHandshake 是整个机制的入口,其实现非常简洁:
void DoHandshake( HandshakerArgs* args, absl::AnyInvocable<void(absl::Status)> on_handshake_done) override { args->args = args->args .Set(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS, grpc_endpoint_get_local_address(args->endpoint.get())) .Set(GRPC_ARG_ENDPOINT_PEER_ADDRESS, grpc_endpoint_get_peer(args->endpoint.get())); InvokeOnHandshakeDone(args, std::move(on_handshake_done), absl::OkStatus()); }它做了两件事:
- 从当前连接的
grpc_endpoint对象中提取地址:本地地址通过grpc_endpoint_get_local_address获取,远端(对端)地址通过grpc_endpoint_get_peer获取; - 将结果写入
args->args(ChannelArgs)的两个键中,这两个键在 endpoint_info_handshaker.h 中定义:
// Set by the handshaker to indicate the local address of the endpoint. #define GRPC_ARG_ENDPOINT_LOCAL_ADDRESS "grpc.internal.endpoint_local_address" // Set by the handshaker to indicate the peer address of the endpoint. #define GRPC_ARG_ENDPOINT_PEER_ADDRESS "grpc.internal.endpoint_peer_address"随后立即以absl::OkStatus()完成握手(InvokeOnHandshakeDone),即该手握手器不消费也不改写任何字节流,只是纯信息采集,不会阻塞或延迟后续握手。
优先级与执行时机
EndpointInfoHandshaker由工厂类EndpointInfoHandshakerFactory创建,其Priority()返回 HandshakerPriority::kSecurityHandshakers,源码注释明确要求“必须在 kTCPConnectHandshakers 之后执行”。
参考 handshaker_factory.h 中定义的优先级枚举,握手链的执行顺序为:
| 优先级 | 含义 | 适用侧 |
|---|---|---|
kPreTCPConnectHandshakers | TCP 连接建立之前 | 主要客户端 |
kTCPConnectHandshakers | 实际建立 TCP 连接 | 主要客户端 |
kHTTPConnectHandshakers | 实际建立 HTTP CONNECT | 主要客户端 |
kReadAheadSecurityHandshakers | 连接建立后、安全握手前 | 主要服务端 |
kSecurityHandshakers | 连接建立后的安全握手 | 客户端与服务端 |
kTemporaryHackDoNotUseEndpointWrappingHandshakers | 临时端点包装(勿用) | — |
对比 tcp_connect_handshaker.cc 中返回的kTCPConnectHandshakers优先级,可以确认:EndpointInfo 手握手器在TCP 连接已经建立之后、安全握手(TLS/ALTS)同优先级阶段采集地址,因此拿到的grpc_endpoint已经处于可查询地址的就绪状态。
客户端与服务端双侧注册
RegisterEndpointInfoHandshaker 同时将工厂注册到客户端与服务端两种握手类型:
void RegisterEndpointInfoHandshaker(CoreConfiguration::Builder* builder) { builder->handshaker_registry()->RegisterHandshakerFactory( HANDSHAKER_CLIENT, std::make_unique<EndpointInfoHandshakerFactory>()); builder->handshaker_registry()->RegisterHandshakerFactory( HANDSHAKER_SERVER, std::make_unique<EndpointInfoHandshakerFactory>()); }这意味着无论客户端发起连接还是服务端接受连接,其握手链中都会包含这一个手握手器。该注册函数的实际调用点位于插件注册中心 grpc_plugin_registry.cc,属于 gRPC 核心配置(CoreConfiguration)初始化的一部分,随库启动自动生效,无需用户额外配置。
数据流向:从 grpc_endpoint 到授权决策
采集到的两个地址以字符串形式写入 ChannelArgs 后,真正的消费方是 gRPC 的安全授权模块。以 evaluate_args.cc 为例:
EvaluateArgs::PerChannelArgs::PerChannelArgs(grpc_auth_context* auth_context, const ChannelArgs& args) { // ... 从 auth_context 提取 transport_security_type、spiffe_id、uri_sans 等 local_address = ParseEndpointUri( args.GetString(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS).value_or("")); peer_address = ParseEndpointUri( args.GetString(GRPC_ARG_ENDPOINT_PEER_ADDRESS).value_or("")); }授权评估器在构造评估参数时,会从 ChannelArgs 中按GRPC_ARG_ENDPOINT_LOCAL_ADDRESS与GRPC_ARG_ENDPOINT_PEER_ADDRESS两个键读取地址字符串(缺失时回退为空串),再通过ParseEndpointUri解析为结构化的地址对象。
ParseEndpointUri(evaluate_args.cc)的解析逻辑说明了地址字符串的格式约定:
- 先按 URI 语法解析,取出 path 部分;
- 调用
SplitHostPort切分 host 与 port; - 用
SimpleAtoi将端口转为整型; - 用
StringToSockaddr尝试将地址解析为 IPv4/IPv6 的 socket 地址结构。
由此可推断grpc_endpoint_get_peer/grpc_endpoint_get_local_address返回的地址字符串形如ipv4:1.2.3.4:123或ipv6:[2001:0db8:...]:456,这与测试代码中的输入格式完全吻合。解析后的本地/远端地址会被用于 RBAC(基于角色的访问控制)授权匹配,即 gRPC 的授权策略可以依据“连接来自哪个对端地址、落在哪个本地端口”等维度做出放行或拒绝的决策。
测试验证:如何确认端点信息链路正确
仓库中的测试用例为上述行为提供了直接验证:
- evaluate_args_test_util.h 提供了两个测试辅助方法,将端点地址写入 ChannelArgs:
void SetLocalEndpoint(absl::string_view local_uri) { args_ = args_.Set(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS, local_uri); } void SetPeerEndpoint(absl::string_view peer_uri) { args_ = args_.Set(GRPC_ARG_ENDPOINT_PEER_ADDRESS, peer_uri); }- evaluate_args_test.cc 展示了典型用法,例如
util_.SetLocalEndpoint("ipv6:[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:456")与util_.SetPeerEndpoint("ipv4:255.255.255.255:123"); - authorization_matchers_test.cc 中大量使用
SetLocalEndpoint/SetPeerEndpoint来构造授权匹配场景,覆盖 IPv4、IPv6 与非法地址等边界情况(例如ipv6:[1:2::3::]:456这类格式错误的输入),用于验证授权匹配器的解析与容错行为。
此外,构建系统层面,test/core/test_util/BUILD 中引用了//src/core:endpoint_info_handshaker目标,说明测试工具链与 EndpointInfo 手握手器之间的依赖关系是显式声明的。
小结:EndpointInfo 在 gRPC 安全体系中的位置
回顾整条链路,EndpointInfo 的设计体现了 gRPC 握手框架“可插拔、职责单一”的架构思想:
- 采集:
EndpointInfoHandshaker在 TCP 连接建立后、安全握手阶段,从grpc_endpoint提取本地与对端地址; - 传递:地址以
ChannelArgs键值对(grpc.internal.endpoint_local_address/grpc.internal.endpoint_peer_address)随握手链流转; - 消费:授权评估模块从 ChannelArgs 中读取地址并解析为结构化数据,支撑 RBAC 授权决策。
对于希望在 gRPC 中获取连接端点信息的开发者而言,这一机制提供了明确的参考范式:只需在握手链中挂载一个与EndpointInfoHandshaker同构的采集器,即可让连接元数据随握手过程自然沉淀到ChannelArgs,供后续任意环节消费。相关实现细节可进一步参阅 endpoint_info_handshaker.cc、handshaker.h 与 evaluate_args.cc。
【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考