已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
【背景】 维护一套 Java 8 + Netty 4.1.x 自研 mTLS 网关,3 副本跑在自建 K8s 1.26,无 Istio/sidecar。上游客户端混合 Java 8(OkHttp 3.x)和 Go 1.21,存在大量长连接(gRPC 双向流 + WebSocket)。安全基线要求客户端证书 90 天强制轮换,CA 自签、每客户端一证书(CN=clientId)。
【现象】
运维脚本目前做法:CA 签发新 p12 → mv new.p12 old.p12 原地替换 → SIGUSR1 通知网关 reload。问题:已建立的长连接在 reload 后仍然握旧证书,原因是 Netty 的 SslContext 在 ChannelPipeline 安装 SslHandler 时就绑定了旧 SslContext,而 SslHandler 是 stateful 的,replace 会丢 pending IO。新握手虽然能读到新 truststore,但客户端侧若已按基线把旧 CA 从 truststore 摘掉,就会出现 SSLHandshakeException: bad certificate,直接 502。
【已尝试的排查与结果】
SslContextBuilder.forClient().trustManager(new File(new.p12)) 重建 SslContext —— 新连接生效,老连接无效(如上);pipeline.replace("ssl", new SslHandler(newCtx)) —— 单元测试里 pending 的 gRPC 流会丢,线上不敢用;channel.close() 让客户端重连 —— 思路可行但没人在生产验证过,担心 gRPC 长连接重连风暴。【期望答案形态】