侧边栏壁纸
博主头像
いちごみるく

行动起来,活在当下

  • 累计撰写 70 篇文章
  • 累计创建 48 个标签
  • 累计收到 55 条评论

目 录CONTENT

文章目录

ssh(sftp)和https共用同一端口【Nginx根据TLS、Alpn、SNI分流】

适用范围: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,并逐条验证。

0

评论区