通过 ngx_stream_ssl_preread_module 模块(1.11.5) 实现HTTPS和SSH共用一个端口

根据 $ssl_preread_protocol 变量 判断SSL协议版本,选择上游。使ssh连接到ssh端口,HTTPS连接到https端口

nginx版本必须大于1.15.2,因为 nginx 1.15.2 版本之后新增了一个属性$ssl_preread_protocol,可以让stream区分web ssl/tls和其他协议

流程

1.ssh端口

2.https端口

3.通过stream模块实现四层协议的转发,根据 $ssl_preread_protocol 判断进入ssh还是https端口

配置

1.nginx.conf

cat /etc/nginx/nginx.conf

stream {
    include /etc/nginx/conf.d/*.stream;
}

指定加载的stream配置文件

2./etc/nginx/conf.d/https.conf

upstream gateway-test {
        server 192.168.1.100:8080;
}

server {
        listen 85 ssl;
        server_name https.example.com;
        charset utf-8;

        ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_certificate /etc/nginx/ssl/example.com.pem;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;

        location /healthy {
                default_type text/html;
                return 200 'ok';
        }

        location / {
                proxy_redirect off;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://gateway-test;
        }
        access_log /var/log/nginx/gateway-test_access.log main;
        error_log /var/log/nginx/gateway-test_error.log;

}

3./etc/nginx/conf.d/ssh_https.stream

upstream ssh {
    server 172.16.230.128:22;
}

upstream https {
    server 172.16.230.128:85;
}

# 通过map,根据$ssl_preread_protocol 值设置 upstream 变量的值
# 1.请求SSL协议版本是 TLSv1.2 ,upstream 变量设置为 https
# 2.请求SSL协议版本是其它版本,比如TLSv1.1、TLSv1.3,upstream变量值为 tls.example.com:443
# 3.请求不包含 SSL协议版本,upstream设置为ssh
map $ssl_preread_protocol $upstream {
    "" ssh;
    "TLSv1.2" https;
    default tls.example.com:443;
}

server {
    listen 443;
    ssl_preread on;        # 允许在预读取阶段从 ClientHello 消息中提取信息。
    proxy_pass $upstream;  # 根据上面通过map设置的upstream变量,选择转发的上游地址
}

结果

1.通过ssh访问IP+端口,转发到ssh端口

2.通过TLSv1.2版本请求https端口,转发到https上游地址

3.通过其它TLS版本访问,转发到默认的 tls.example.com:443;

测试

添加hosts解析域名

/etc/hosts

172.16.230.128    https.example.com

ssh

swift@l-k8s-node-002:/etc/nginx/conf.d$ ssh swift@https.example.com -p 443
swift@https.example.com's password: 
Last login: Thu Dec  9 19:13:03 2021 from 172.16.230.128
swift@l-k8s-node-002:~$ 

https

可以通过curl指定不同的tls协议版本进行测试

$ curl --help|grep tlsv
     --proxy-tlsv1   Use TLSv1 for HTTPS proxy
 -1, --tlsv1         Use TLSv1.0 or greater
     --tlsv1.0       Use TLSv1.0
     --tlsv1.1       Use TLSv1.1
     --tlsv1.2       Use TLSv1.2
     --tlsv1.3       Use TLSv1.3
swift@l-k8s-node-002:~$ curl --tlsv1.2 https://https.example.com -k
{"data":"","message":"非法请求","success":false,"code":100}

swift@l-k8s-node-002:/etc/nginx/conf.d$ curl --tlsv1.2 https://https.example.com/healthy -k
ok

default

$ curl --tlsv1.1 https://https.example.com -k
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to https.example.com
$ curl --tlsv1.3 https://https.example.com -k
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to https.example.com

通过查看日志,确认转发到了default指定的地址,但是该地址无法访问,所以报错

注意:

如果要查看stream转发日志,可以在nginx.conf中的stream中添加日志配置

比如:

stream {
    log_format basic '$remote_addr [$time_local] '
                     '$protocol $status $bytes_sent $bytes_received '
                     '$session_time';

    access_log  /var/log/nginx/access.log basic;
    error_log  /var/log/nginx/error.log debug;
    
    include /etc/nginx/conf.d/*.stream;
}

ngx_stream_ssl_preread 模块

ngx_stream_ssl_preread_module模块(1.11.5)允许从ClientHello消息中提取信息,而无需终止 SSL/TLS,例如,通过SNI请求的服务器名称或在ALPN中通告的协议。默认情况下未构建此模块,应使用--with-stream_ssl_preread_module配置参数启用它。

Example Configuration

根据服务器名称选择上游:

map $ssl_preread_server_name $name {
    backend.example.com      backend;
    default                  backend2;
}

upstream backend {
    server 192.168.0.1:12345;
    server 192.168.0.2:12345;
}

upstream backend2 {
    server 192.168.0.3:12345;
    server 192.168.0.4:12345;
}

server {
    listen      12346;
    proxy_pass  $name;
    ssl_preread on;
}

根据协议选择上游:

map $ssl_preread_alpn_protocols $proxy {
    ~\bh2\b           127.0.0.1:8001;
    ~\bhttp/1.1\b     127.0.0.1:8002;
    ~\bxmpp-client\b  127.0.0.1:8003;
}

server {
    listen      9000;
    proxy_pass  $proxy;
    ssl_preread on;
}

根据 SSL 协议版本选择上游:

map $ssl_preread_protocol $upstream {
    ""        ssh.example.com:22;
    "TLSv1.2" new.example.com:443;
    default   tls.example.com:443;
}

# ssh and https on the same port
server {
    listen      192.168.0.1:443;
    proxy_pass  $upstream;
    ssl_preread on;
}

Directives

Syntax:        ssl_preread on | off;
Default:    ssl_preread off;
Context:    stream, server

preread阶段启用从 ClientHello 消息中提取信息。

Embedded Variables

$ssl_preread_protocol

  • 客户端支持的最高 SSL 协议版本(1.15.2)

$ssl_preread_server_name

  • 通过 SNI 请求的服务器名称

$ssl_preread_alpn_protocols

  • 客户端通过 ALPN(1.13.10)发布的协议列表。这些值用逗号分隔。

标签: Nginx, 端口复用

添加新评论