尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

gRPC Security Handshakers 源码深度解析:TLS/ALTS 握手框架与安全端点实现

gRPC Security Handshakers 源码深度解析:TLS/ALTS 握手框架与安全端点实现 gRPC Security Handshakers 源码深度解析TLS/ALTS 握手框架与安全端点实现【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc导读本篇文章聚焦 gRPC 核心仓库中负责安全握手的实现模块——src/core/handshaker/security/。该目录承载了 gRPC 安全基础设施中至关重要的一环通过 TLS、ALTS 等传输安全机制TSI完成客户端与服务端之间的身份认证与通信加密。阅读本文后你将理解SecurityHandshaker如何驱动 TSI 握手状态机、SecureEndpoint/PipelinedSecureEndpoint如何对grpc_endpoint进行加解密包装、握手完成后如何校验对端身份并移交 auth context以及握手管道pipelining启发式选择器的调优参数。一、模块定位安全握手在 gRPC 连接建立中的角色1.1 为什么需要 handshaker 框架在 gRPC 中一条连接从底层 TCP 就绪到真正承载 RPC 流量之间需要先执行若干握手步骤例如 HTTP CONNECT 代理协商、TLS 握手等。gRPC 为此设计了可插拔的 handshaker 框架src/core/handshaker/handshaker.h每个Handshaker代表一次握手操作HandshakeManager按照优先级依次调用它们。核心抽象定义如下见 handshaker.hHandshakerArgs握手过程中的输入/输出参数包含endpoint可被握手器替换为包装后的端点、ChannelArgs、未被消费的read_buffer、exit_early标志、EventEngine 指针、截止时间deadline以及 channelz 追踪节点等Handshaker抽象基类暴露DoHandshake()、Shutdown()和name()三个纯虚接口HandshakeManager按加入顺序串行执行握手器并通过on_handshake_done回调把最终结果交还调用方。HandshakerFactory通过HandshakerPriority枚举handshaker_factory.h声明自己的执行优先级安全握手器注册在kSecurityHandshakers这一档位于 TCP 连接建立、HTTP CONNECT 之后负责连接建立之后的传输安全握手。1.2 security/ 目录在整个框架中的位置src/core/handshaker/下的兄弟模块包括http_connect/HTTP CONNECT 代理握手、tcp_connect/TCP 基础握手、endpoint_info/端信息数据结构以及proxy_mapper相关注册表见 src/core/handshaker/AGENTS.md。security/是其中专门承担TLS / ALTS 等安全握手的目录其职责正如目录内 AGENTS.md 所述负责在客户端与服务端之间建立安全连接使用 TLS 或 ALTS 等传输安全机制认证服务端并加密双方全部通信。二、目录结构与核心文件职责src/core/handshaker/security/下共有 7 个文件对应关系如下依据 AGENTS.md 的 Files 小节文件职责security_handshaker.h / security_handshaker.cc定义SecurityHandshaker类是所有安全握手器的基类实际为对 TSItsi_handshaker的封装驱动并提供客户端/服务端两个HandshakerFactorysecure_endpoint.h / secure_endpoint.cc定义SecureEndpoint对底层grpc_endpoint的包装提供加解密后的安全通道pipelined_secure_endpoint.cc支持读取管道化read pipelining的SecureEndpoint实现pipelining_heuristic_selector.h选择是否启用读管道化的启发式策略含四种实现三、SecurityHandshakerTSI 握手状态机的驱动者3.1 创建入口与工厂注册SecurityHandshakerCreate()是安全握手器的统一创建入口security_handshaker.cc其逻辑为若传入的absl::StatusOrtsi_handshaker*携带错误状态则返回一个FailHandshaker它会在DoHandshake()时直接以失败状态回调name()为security_fail若 TSI 握手器指针为空同样返回FailHandshaker错误码为UnknownError否则构造真正的SecurityHandshaker。工厂注册发生在SecurityRegisterHandshakerFactories()security_handshaker.cc分别向HANDSHAKER_CLIENT与HANDSHAKER_SERVER注册ClientSecurityHandshakerFactory与ServerSecurityHandshakerFactory。该函数由核心插件注册表在构建CoreConfiguration时调用见 src/core/plugin_registry/grpc_plugin_registry.cc。两个工厂的实现非常精简security_handshaker.cc从ChannelArgs中取出对应的安全连接器对象客户端为grpc_channel_security_connector服务端为grpc_server_security_connector然后委托其add_handshakers()方法把具体的 TSI 握手器加入HandshakeManager。它们声明的优先级均为HandshakerPriority::kSecurityHandshakers。3.2 安全连接器如何产生 TSI 握手器以 SSL/TLS 为例SslChannelSecurityConnector::add_handshakers()src/core/credentials/transport/ssl/ssl_security_connector.cc展示了完整链路调用tsi_ssl_client_handshaker_factory_create_handshaker()创建 TSI 层握手器传入覆盖目标名overridden target name、可选 channel args如GRPC_ARG_TRANSPORT_PROTOCOLS以及用于遥测的CollectionScope、backend service、locality 等信息将得到的tsi_handshaker*连同this连接器和args一起交给SecurityHandshakerCreate()最终通过handshake_mgr-Add(...)挂到握手管理器上。同理ALTS 安全连接器src/core/credentials/transport/alts/alts_security_connector.cc、TLS 连接器src/core/credentials/transport/tls/tls_security_connector.cc、local 与 insecure 连接器都遵循同一模式。这正是 AGENTS.md 中SecurityHandshaker类被设计为可扩展的可以轻松添加新的安全机制的具体体现——新增一种安全机制只需提供对应的 TSI 握手器工厂与grpc_security_connector子类。3.3 握手状态机读-写-再读的循环驱动SecurityHandshaker::DoHandshake()security_handshaker.cc是握手入口其核心是一个围绕 TSItsi_handshaker_next()的状态循环DoHandshake └─ MoveReadBufferIntoHandshakeBuffer() // 把已读数据拷入握手缓冲 └─ DoHandshakerNextLocked() // 调用 tsi_handshaker_next() ├─ TSI_INCOMPLETE_DATA → grpc_endpoint_read() 等待更多数据 ├─ bytes_to_send 0 → grpc_endpoint_write() 向对端发送握手消息 │ └─ 写完后若无 handshaker_result → 继续读对端数据 └─ 拿到 handshaker_result → CheckPeerLocked() 校验对端身份关键实现细节均可在 security_handshaker.cc 中验证初始缓冲GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE为 256 字节MoveReadBufferIntoHandshakeBuffer()第 L151-L165 行在数据超出时会通过gpr_realloc动态扩容异步支持tsi_handshaker_next()可能返回TSI_ASYNC此时回调OnHandshakeNextDoneGrpcWrapper会在 TSI 线程被调用代码通过self.release()把引用释放给异步路径第 L419-L440 行防死锁设计读写回调通过OnHandshakeDataReceivedFromPeerFnScheduler/OnHandshakeDataSentToPeerFnScheduler投递到 EventEngine 上异步执行避免在持有互斥锁mu_时内联回调造成死锁源码注释明确指出这是 EventEngine 迁移前的临时方案并发保护整个握手状态机运行在Mutex mu_保护之下所有*Locked方法都要求持锁调用ABSL_EXCLUSIVE_LOCKS_REQUIRED。3.4 对端校验与握手收尾当 TSI 报告握手完成并给出tsi_handshaker_result后CheckPeerLocked()第 L314-L339 行做三件事通过tsi_handshaker_result_extract_peer()提取对端身份信息tsi_peer调用连接器的check_peer()异步校验对端TLS 场景下校验证书链与主机名见 ssl_security_connector.cc并把校验结果通过on_peer_checked_闭包回调校验通过后从 auth context 中查询GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME属性若不存在或为TSI_SECURITY_NONE则通过global_stats().IncrementInsecureConnectionsCreated()统计一次非安全连接。校验完成后进入OnPeerCheckedFn()第 L216-L312 行的收尾阶段从tsi_handshaker_result取回未使用字节tsi_handshaker_result_get_unused_bytes——这些字节属于握手消息尾部夹带的早期应用数据必须原样交还查询 frame protector 类型TSI_FRAME_PROTECTOR_ZERO_COPY/TSI_FRAME_PROTECTOR_NORMAL/TSI_FRAME_PROTECTOR_NONE据此创建零拷贝或普通 protector若存在 protector则用grpc_secure_endpoint_create()把原 endpoint 包装为安全端点未使用字节作为 leftover slices 传入否则把未使用字节直接追加回read_buffer把 auth context 写入ChannelArgs并在有 protector 时写入由 auth context 生成的 channelz 安全信息MakeChannelzSecurityFromAuthContext目前以 TLS 类型记录远端证书最后调用Finish(absl::OkStatus())通知握手成功。Shutdown()第 L519-L527 行则负责中途取消置位is_shutdown_、调用connector_-cancel_check_peer()取消对端校验、tsi_handshaker_shutdown()关闭 TSI 握手器并释放 endpoint。四、SecureEndpoint加解密通道的端点包装4.1 创建接口与 channel argSecureEndpoint本质上是把tsi_frame_protector/tsi_zero_copy_grpc_protector与底层grpc_endpoint组合成一个对外透明的安全端点。创建函数在 secure_endpoint.hgrpc_secure_endpoint_create()包装 iomgr 风格的grpc_endpoint可接收握手遗留的 leftover slicesgrpc_pipelined_secure_endpoint_create()EventEngine 风格、支持管道化的变体。该头文件还定义了三个可调 channel arg整数类型Channel Arg含义grpc.secure_endpoint.decryption_offload_threshold单次读取达到该大小时将解密卸载到 EventEngine 线程执行grpc.secure_endpoint.encryption_offload_threshold单次写入达到该大小时将加密卸载到 EventEngine 线程执行grpc.secure_endpoint.encryption_offload_max_buffered_writes加密卸载路径上允许缓冲的最大写请求数这些参数允许在高吞吐场景下把 CPU 密集的加解密操作移出 I/O 线程以换取更低的尾延迟与更高的吞吐阈值以下则内联执行以降低调度开销。4.2 数据面protector 与零拷贝路径从 pipelined_secure_endpoint.cc 的FrameProtector实现可以看到数据面的设计FrameProtector同时持有普通与零拷贝两种 protector二者互斥is_zero_copy_protector_标记当前使用哪种读/写各分配一个STAGING_BUFFER_SIZE8192 字节的暂存 slice普通 protector 模式下读写数据经暂存缓冲中转内存全部来自ResourceQuota的MemoryOwner通过memory_owner_.MakeSlice(...)分配并记录self_reservation_保证自身存活期间的内存配额读与写各自持有独立的互斥锁read_mu_/write_mu_允许并发读写方向上的加解密构造时若传入 leftover slices会复制进内部SliceBuffer保证握手阶段遗留字节在安全端点内被正确消费通过GRPC_TRACE_LOG(secure_endpoint, INFO)与GRPC_TRACE_FLAG_ENABLED(secure_endpoint)提供细粒度的调试追踪。4.3 管道化读取与启发式选择器PipelinedSecureEndpoint的管道化指在读取路径上提前发起下一次读让解密与网络读取并行从而摊薄大消息场景下的往返延迟。但持续管道化在小消息如普通 RPC 头部场景下会浪费资源因此引入 pipelining_heuristic_selector.h 动态决策启发式行为关键阈值AlwaysOffHeuristic永远关闭管道化—AlwaysOnHeuristic永远开启管道化—ConsecutiveSmallReadsHeuristic连续出现小读取时关闭管道化出现大读取时重新开启小读取阈值950 * 1024字节连续 30 次小读取即关闭MovingAverageHeuristic默认对读取大小做指数移动平均新值权重 0.05低于阈值关闭、高于阈值开启关闭阈值64 * 1024字节开启阈值950 * 1024字节PipeliningHeuristicSelector默认采用kMovingAverage可通过SetHeuristicType()在四种策略间切换。从阈值设计可以推断gRPC 认为单次读取超过约 950 KiB 属于大读取值得保持管道化而 64 KiB 以下的持续小读取则更适合内联解密、关闭管道化以节省开销。五、源码佐证测试用例与调用链印证端到端验证test/core/handshake/secure_endpoint_test.cc 与 test/core/handshake/secure_endpoint_read_coalescing_test.cc 直接测试grpc_secure_endpoint_create的读写加解密与读取合并行为test/core/event_engine/posix/posix_endpoint_test.cc 则覆盖了管道化安全端点在 EventEngine 路径上的行为。注册链路SecurityRegisterHandshakerFactories仅在 src/core/plugin_registry/grpc_plugin_registry.cc 声明并在第 L151 行调用印证了安全握手器作为核心插件随 gRPC Core 一同注册的事实。连接器协作除 SSL 外TLStls_security_connector.cc、ALTSalts_security_connector.cc、locallocal_security_connector.cc、insecureinsecure_security_connector.cc等连接器均调用SecurityHandshakerCreate注入握手器验证了安全连接器决定机制、SecurityHandshaker 统一驱动的分层设计。六、总结src/core/handshaker/security/是 gRPC 传输安全的核心实现层SecurityHandshaker以统一的状态机驱动 TLS/ALTS 等 TSI 握手SecureEndpoint与PipelinedSecureEndpoint负责握手完成后的数据加解密与读取管道化PipeliningHeuristicSelector则提供了可调优的性能开关。理解这一模块即可掌握 gRPC 从TCP 就绪到安全通道就绪的完整链路以及grpc.secure_endpoint.*系列 channel arg 和读取管道化阈值的调优入口。【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表