Nginx 实现HTTPS与SSH端口复用
通过 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.comssh
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.3swift@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
okdefault
$ 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)发布的协议列表。这些值用逗号分隔。