
利用 Nginx 四层 SNI 预读透传 TLS 流量,使出网受阻节点借道同内网健康主机免证书透明出网。
我在 OCI 部署了 S1、S2 两台服务器。S1 访问公网正常,S2 请求部分 HTTPS 服务却超时或被拒绝。相同目标、相同请求,只因出口节点不同,结果便完全不同。
可能是网络原因或者目标服务器存在黑白名单,我尝试过更换 S2 的公网 IP,但成本较高,结果也不确定。若把服务迁回 S1,又会浪费 S2 的现有资源。在 S1 额外部署应用代理,则会增加维护复杂度。
嗨,这个网络问题,我不想再增加其他的网络服务应用,为何同一个云服务商的服务器出口命运截然不同,这个IP受到了歧视待遇?
S2 的出网故障并非目标服务宕机。推测更可能是 S2 的出口公网 IP 遭遇了目标侧拦截,或是特定路径上的网络路由受限。
在同属 Oracle Cloud Infrastructure(OCI)同一区域的内网环境下,排查现象呈现出鲜明反差。我在 S1 上请求外部目标 HTTPS 服务时,TCP 握手与 TLS 协商在几十毫秒内顺利完成。但在 S2 上发起完全相同的请求时,控制台却在等待数十秒后输出连接超时(Connection timed out)。
正在渲染 Mermaid 图表...
它就像你寄出一封信件,邮局链路完好,信件内容合法。收件单位可能配置了过滤规则,也可能是该片区投递路径存在异常。收件方只要在信封上看到 S2 的发件门牌号,信件就直接石沉大海;但如果借用信誉清白的 S1 门牌号寄出,通信便能立刻恢复。
已观察到以下现象:
云厂商(如 OCI)仅保障从云主机到公网网关的基础连通性,无权也无法干涉第三方站点的网络安全策略与公网路由选择。
遇到此类故障,支撑工程师常误以为“服务器访问外部受阻,找机房客服提工单就能解封”。事实上, 当 OCI 的 Security List 安全列表与 Route Table 路由表允许出站流量(0.0.0.0/0)时,可确认该配置未显式阻止对应的出站流量。
正在渲染 Mermaid 图表...
外部服务端的阻断或路由异常通常更加复杂,存在多种非对称因素:
向 OCI 提交工单,技术支持只能确认机房网关工作正常。机房运维无法要求外部站点修改策略,更无法左右外部自治系统(AS)的路由流向。继续在工单沟通上消耗时间,无法缩短业务中断窗口。
最理性的破局选择是在现存资源中寻找出路。既然 S1 与 S2 处于同一个虚拟云网络(VCN)内部,两台主机之间的私有内网通信完全畅通,且免收内网流量费。那么利用 S1 充当中继传输站,由 S1 的 IP 代替 S2 向外部发送请求,便能在不改变基础设施的前提下恢复网络连通。
S1 不必持有目标站点证书,也能把 S2 的 HTTPS 连接准确送到目标域名。关键是读取 TLS 握手中的服务器名称指示(Server Name Indication,SNI),再原样转发 TCP 字节流。
可以把 S1 想成只看信封地址的转运站。像在于它从 ClientHello 中读出域名,却不拆开加密内容;不像在于它仍建立了一条由 S1 发起的独立 TCP 连接,而非单纯转寄同一个数据包。
Nginx 的 ngx_stream_ssl_preread_module 提供这一能力。TLS ClientHello 中通常以明文携带 SNI 域名。Nginx 在 stream 四层代理中启用 ssl_preread on 后,便能用该域名决定上游地址。后续 TLS 握手和 HTTP 内容均直接透传,S1 没有证书私钥,也无法解密请求内容。
常被误以为这等同于 Nginx 的 HTTPS 反向代理。其实这是 TCP 透传:反向代理终止 TLS,需要证书;这里不终止 TLS,只读取握手前部的 SNI 字段。
发行版软件包是否包含该模块不能一概而论。许多主流 Linux 的 Nginx 预编译包已提供 stream 与 SSL preread 能力,但应在 S1 实机核验,而不是假定“默认存在”。
将以下片段放入 S1 的 Nginx 顶层 stream 配置中。<S1可用DNS解析器> 必须替换为 S1 能访问的 DNS 地址。
proxy_pass 使用 SNI 变量,因此 Nginx 需要 resolver 在运行时解析目标域名。没有它,变量形式的上游域名可能无法解析,连接会失败。配置变更后执行 nginx -t,再平滑重载服务。
正在渲染 Mermaid 图表...
代价很具体:S1 的同一 IP 上,TCP 443 端口将被 stream 监听独占。若 S1 原本通过该 IP 托管常规 HTTPS 网站,网站不能再直接监听该端口。可选边界是改用另一公网 IP,或把本地网站迁至其他端口;不能让 HTTP 模块与 stream 模块同时绑定同一 IP:443。
该方案也依赖客户端发送可见 SNI。没有 SNI 的旧客户端,以及启用加密 ClientHello(ECH)且 Nginx 无法获得域名的场景,无法据此动态选择目标。
S2 不需要安装代理客户端。只需让目标域名在 S2 本地解析为 S1 的私网地址,同时保持 URL 中的域名不变。
此后,S2 执行 https://target.example.com 时,连接实际进入 S1 的 443 端口,但 TLS 中的 SNI 和证书校验名称仍是 target.example.com。S1 读出该名称,向真实目标建立新的出站 TCP 连接,并双向转发字节流。
因此,目标服务器在 TCP 连接层看到的源 IP 是 S1 的公网出口 IP,而不是 S2 的公网 IP。S2 的地址不会出现在这条 TCP 会话中。边界是应用层若主动携带身份信息,例如客户端自行添加 X-Forwarded-For,那是另一条信息通道;本方案只替换网络连接的源地址。
在 S2 上验证时,仍使用原始域名,而不是 S1 的 IP:
应检查三件事:TLS 握手是否完成、证书的域名是否匹配目标域名、HTTP 状态码是否符合预期。还应限制只有 S2 能访问 S1 的 443 端口;否则任何能连到 S1 的主机都可能把它当作任意域名的 TCP 出口。
利用 Nginx 四层 SNI 预读透传 TLS 流量,使出网受阻节点借道同内网健康主机免证书透明出网。
【核心优势】
【边界与代价】
【适用场景】 节点因公网 IP 拦截、单向丢包或 BGP 路由故障导致外部通信超时,且同内网存在可正常出网的辅助节点。
【最佳避坑姿势】