NGINX SSL/TLS 预读模块解密 ngx_stream_ssl_preread_module 实战指南

一、模块概览

  • 模块名称 :ngx_stream_ssl_preread_module

  • 首次引入 :NGINX 1.11.5(2016-05-03)

  • 编译选项 :--with-stream_ssl_preread_module(需启用 --with-stream)

  • 核心功能 :在不解密SSL/TLS连接的前置读(preread)阶段,直接从 ClientHello 中提取:

    • SNI(Server Name Indication)
    • ALPN(Application-Layer Protocol Negotiation)
    • 最高支持协议版本

这一能力让你能在四层(TCP/UDP)入口,就根据客户端想访问的域名或协议,进行零拷贝 、无感知的路由分流。

二、核心指令与变量

指令

nginx 复制代码
ssl_preread on | off;
  • Context :stream、stream { server { ... } }
  • 默认 :off
  • 功能:开启后,NGINX 在收到 TCP 握手数据(ClientHello)后、正式解密前,提取 SNI、ALPN、协议版本等信息。

内置变量

变量名 含义 引入版本
$ssl_preread_server_name 从 ClientHello 的 SNI 扩展中抓取到的服务器域名(或空串) 1.11.5
$ssl_preread_alpn_protocols 客户端在 ALPN 扩展中声明的、以逗号分隔的协议列表(如 h2,http/1.1) 1.13.10
$ssl_preread_protocol 客户端支持的最高 TLS/SSL 版本,如 TLSv1.3;空串表示无 SSL 握手信息 1.15.2

三、配置示例

1. 基于 SNI 分流

nginx 复制代码
# 将域名映射到不同上游
map $ssl_preread_server_name $backend {
    backend.example.com      backend1;
    api.example.com          backend2;
    default                  backend_default;
}

upstream backend1 { server 10.0.0.1:443; }
upstream backend2 { server 10.0.0.2:443; }
upstream backend_default { server 10.0.0.3:443; }

stream {
    server {
        listen      443;
        ssl_preread on;

        proxy_pass  $backend;
    }
}
  • 流程 :客户端发起 TLS 握手 ➔ NGINX 读取 SNI ➔ 根据 $backend 动态路由 ➔ 再交由 proxy_pass 进行底层转发。

2. 基于 ALPN 协议分流

nginx 复制代码
map $ssl_preread_alpn_protocols $backend {
    ~\bh2\b           http2_upstream;
    ~\bhttp/1.1\b     http1_upstream;
    default           generic_upstream;
}

upstream http2_upstream  { server 10.0.1.1:8443; }
upstream http1_upstream  { server 10.0.1.2:8080; }
upstream generic_upstream { server 10.0.1.3:9000; }

stream {
    server {
        listen      443;
        ssl_preread on;

        proxy_pass  $backend;
    }
}
  • 示例场景 :同端口承载 gRPC(h2)、传统 HTTPS(http/1.1)与其他 TLS 应用。

3. 基于协议版本路由

nginx 复制代码
map $ssl_preread_protocol $backend {
    ""        ssh_upstream;              # 非 TLS 客户端,如 SSH 直接访问
    "TLSv1.2" tls12_upstream;
    default   tls_upstream;
}

upstream ssh_upstream  { server 10.0.2.1:22; }
upstream tls12_upstream { server 10.0.2.2:443; }
upstream tls_upstream   { server 10.0.2.3:443; }

stream {
    server {
        listen      443;
        ssl_preread on;

        proxy_pass  $backend;
    }
}
  • 应用示例:在同一端口同时支持 SSH 与 HTTPS,或根据客户端 TLS 版本降级兼容。

四、典型应用场景

  1. 二合一入口
    同端口同时做 SSH、HTTPS、gRPC、MQTT 等多协议采用零拷贝分流。
  2. 金丝雀/灰度路由
    根据客户端 SNI 域名,将小部分测试流量指向预发布环境。
  3. 智能协议网关
    前置读取 ALPN、协议版本后,路由至不同类型的业务集群。
  4. DDoS 与安全防护
    在 ClientHello 阶段就可基于 SNI 黑名单、弱协议版本(如 TLSv1.0)等条件做丢弃或限速,降低后端资源消耗。
  5. 运维监控统计
    利用 $ssl_preread_* 变量埋点 NGINX 日志,统计不同域名、协议类型、TLS 版本的连接分布。

五、性能与注意事项

  • 零解密开销:只读取前置数据,不做完整握手或证书校验,CPU 开销极低。

  • 必须先开启 ssl_preread on; :否则 $ssl_preread_* 变量为空,路由失效。

  • 变量映射效率高 :map 内部使用哈希查找,百万 QPS 场景也能稳定分流。

  • 与 ngx_stream_ssl_module 区别:

    • ssl_preread 仅读取 ClientHello,不终结 TLS;
    • ssl 指令(ngx_stream_ssl_module)则终结 TLS 握手并解密后续流量。
  • 安全性考量:预读阶段不能校验客户端证书或执行 OCSP,可配合后置 TLS 终结或接入防火墙。

  • SNI 空白 :部分老旧客户端不发送 SNI,会走 default 分支,需合理配置兜底上游。

六、小结

ngx_stream_ssl_preread_module 给 NGINX 四层代理带来了"先看再转"的魔法:在不解密的前提下,提取 SNI、ALPN 与版本信息,实现灵活、零拷贝的多协议分流与灰度路由。无论是合并 SSH/HTTPS、按域名做灰度,还是搭建智能协议网关,这个模块都能助你一臂之力。

动手即见效 :升级到 NGINX ≥1.11.5,启用 --with-stream_ssl_preread_module,快速在你的流量入口实现智 能路由吧!

相关推荐
通信大模型4 小时前
IEEE TCCN | 面向低空经济网络的Agentic AI驱动多无人机轨迹优化
网络·人工智能·无人机
wdfk_prog4 小时前
LWIP教程 09:从 `tcp_slowtmr()` 到 Fast Retransmit——TCP 超时重传与重复 ACK
服务器·网络·数据库·网络协议·tcp/ip
15Moonlight4 小时前
Linux网络(02):Socket 编程 UDP
linux·网络·udp
盛世宏博智慧档案5 小时前
一文读懂 PoE+Modbus TCP 温湿度传感器布线规范与故障排查
网络·网络协议·tcp/ip
liulilittle5 小时前
Linux 下 select 测试函数
linux·服务器·网络·数据库·c++·select·c
夜雪一千5 小时前
Python 生成模拟地址数据:Faker之外的备选库
网络·windows·python
小此方6 小时前
Linux网络(二十一):深入理解 TCP 异常处理:网线断开、Keepalive 保活机制与 Linux 内核传输层协议源码剖析
linux·网络·tcp/ip
北京盛世宏博6 小时前
工业环境监测选型:Modbus TCP以太网温湿度传感器部署、调试、避坑全指南
开发语言·网络·php
Raspberry_Pi_官方账号6 小时前
Raspberry Pi Desktop 全面升级,现已登陆 PC 和 Mac
服务器·网络·macos·树莓派·raspberry pi·raspberrypios