适用范围:Nginx 的 TCP
stream分流,需要stream与stream_ssl_preread模块。示例需结合自己的服务监听与后端协议配置。
一、SSH、SFTP 与 HTTPS 共用 TCP 443
SFTP 是通过 SSH 传输文件的独立协议,不是给 FTP 加上 TLS 的 FTPS。SSH 和 TLS 的连接开头不同,Nginx 可以预读 TLS ClientHello,将 TLS 流量送给 HTTPS 后端,其余流量送给 SSH 后端。
下面的后端在同一台机器上,SSH 监听 127.0.0.1:22,HTTPS 监听 127.0.0.1:8443。先确认真实监听地址,避免把后端重新指向入口 443 形成循环。stream 与 http 是同级上下文,不能将此配置放进 http {} 的站点文件。
stream {
upstream ssh_backend { server 127.0.0.1:22; }
upstream https_backend { server 127.0.0.1:8443; }
map $ssl_preread_protocol $backend {
"" ssh_backend;
default https_backend;
}
server {
listen 443;
listen [::]:443;
ssl_preread on;
proxy_pass $backend;
proxy_connect_timeout 5s;
proxy_timeout 1h;
}
}
$ssl_preread_protocol 是预读得到的客户端支持的最高 TLS 版本,空值表示未识别出 TLS。它是变量,开关指令是 ssl_preread on;。这个兜底会把其他非 TLS 数据也送去 SSH,并非完成 SSH 身份验证;认证仍由 sshd 负责。Nginx 官方预读文档提供了同类分流方式。
先运行 nginx -t,通过后 reload,再分别验证正常域名 HTTPS、ssh -p 443 user@host 和 sftp -P 443 user@host。保留原管理连接及独立的 SSH 入口,确认新入口可用后再调整访问规则。没有 IPv6 的机器去掉 [::] 监听。
这种四层转发保留 TLS 加密,由 HTTPS 后端完成 TLS 握手与证书校验。后端通常看到 Nginx 的连接地址。若要传递客户端地址,参见PROXY protocol 的接收与信任边界;普通 sshd 不直接接受 PROXY protocol,不能对上述入口随意加 proxy_protocol on。
二、按 ALPN 提议分流
$ssl_preread_alpn_protocols 是客户端在 ClientHello 中提议的协议列表,值用逗号分隔。它不代表已经与服务端协商成功的最终协议。客户端可能同时提议 h2 和 http/1.1,以下 map 会优先匹配前面的规则。
stream {
map $ssl_preread_alpn_protocols $backend {
~\bh2\b 127.0.0.1:8001;
~\bhttp/1.1\b 127.0.0.1:8002;
~\bxmpp-client\b 127.0.0.1:8003;
default 127.0.0.1:8002;
}
server {
listen 9000;
ssl_preread on;
proxy_pass $backend;
}
}
这些端口的后端必须能接收原始 TLS 连接。若后端只接收明文 HTTP,单靠 ssl_preread 不会替它解密。HTTP/3 使用 QUIC/UDP;这段 TCP 配置不能凭 h3 的 ALPN 实现 HTTP/3 分流。
三、按 SNI 分流
stream {
map $ssl_preread_server_name $backend {
backend.example.com 127.0.0.1:8443;
default 127.0.0.1:9443;
}
server {
listen 443;
ssl_preread on;
proxy_pass $backend;
}
}
SNI 是客户端声明的目标名称。需要给每个后端配置与目标名称匹配的证书,并明确无 SNI 或未知名称的处理方式。它适合选择后端,不能充当访问认证。采用加密 ClientHello 等机制时,可见名称也可能不再是应用的真实目标名称。
以上三段是不同方案,不能同时复制成多个监听同一地址 443 的 stream server。若要合并,应先确定 TLS/非 TLS,再在 TLS 分支选择 SNI 或 ALPN,并逐条验证。
评论区