Nginx 实现同端口复用HTTPS/TLS业务
前置条件: nginx-1.15.2 以上
1.15.2 版本新增了 $ssl_preread_protocol 变量,通过该变量可以使用 stream 反向代理时预先判断连接是否为SSL/TLS协议或者为非SSL/TLS协议,从而实现同一个端口来转发不同的业务,
stream_ssl_preread
模块检查初始ClientHello
在SSL或TLS连接消息,并且提取其可用于管理连接几个值。$ssl_preread_protocol
版本1.15.2中添加的变量从消息client_version
字段中捕获最新的SSL / TLS版本号ClientHello
。如果消息中supported_versions
存在扩展名ClientHello
,则变量设置为TLSv1.2/TLSv1.3
。
实例:在一台反向代理服务器上运行Nginx,并监听443端口,后端有两组服务,一个为HTTPS(开启TLS1.2/1.3)网站服务,另一个为SSH 服务,我们要实现这两组服务运行在同一个端口上(配置的443端口)--入口请求由Nginx自动区分。
为简便,我这时直接使用 docker环境
- nginx 版本
# docker exec -it nginx nginx -V
nginx version: nginx/1.15.10
built by gcc 8.2.0 (Alpine 8.2.0)
built with OpenSSL 1.1.1b 26 Feb 2019
...<省略若干行>...
- 目录文件
# tree ./nginx-with-L4-reuse/
./nginx-with-L4-reuse/
├── config
│ └── nginx
│ ├── conf.d
│ │ └── default.conf
│ ├── fastcgi.conf
│ ├── fastcgi_params
│ ├── mime.types
│ └── nginx.conf
└── docker-compose.yaml
3 directories, 6 files
- docker-compose.yaml
# docker-compose.yaml
version: "2.4"
services:
nginx:
container_name: nginx
image: nginx:alpine
network_mode: host
volumes:
- ./config/nginx:/etc/nginx/:ro
ports:
- "443:443"
restart: always
- nginx.conf
user nginx;
worker_processes 2;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
log_format stream '{"@access_time":"$time_iso8601",'
'"clientip":"$remote_addr",'
'"pid":$pid,'
'"pro":"$protocol",'
'"ssl_pro": "$ssl_preread_protocol"',
'"pro":"$protocol",'
'"stus":$status,'
'"sent":$bytes_sent,'
'"recv":$bytes_received,'
'"sess_time":$session_time,'
'"up_addr":"$upstream_addr",'
'"up_sent":$upstream_bytes_sent,'
'"up_recv":$upstream_bytes_received,'
'"up_conn_time":$upstream_connect_time,'
'"up_resp_time":"$upstream_first_byte_time",'
'"up_sess_time":$upstream_session_time}';
upstream ssh {
server 192.168.50.212:22;
}
upstream web {
server 192.168.50.215:443;
}
map $ssl_preread_protocol $upstream {
default ssh;
"TLSv1.2" web;
"TLSv1.3" web;
}
# SSH and SSL on the same port
server {
listen 443;
proxy_pass $upstream;
ssl_preread on;
access_log /var/log/nginx/stream_443.log stream;
}
}
- 启用nginx 服务
docker-compose up -d
-
客户端分别发起HTTPS与SSH请求
-
TLS
# curl -I -H "Host: xx.com" https://127.0.0.1/ -k
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 03 Apr 2019 14:23:11 GMT
Content-Type: text/html
Content-Length: 561
Last-Modified: Tue, 19 Mar 2019 02:38:29 GMT
Connection: keep-alive
ETag: "5c905625-231"
Strict-Transport-Security: max-age=15768000
Accept-Ranges: bytes
* SSH
```
# ssh 127.0.0.1 -p 443
Last login: Wed Apr 3 21:54:52 2019 from 192.168.50.212
```
分别对应的请求日志
- HTTPS
{
"@access_time": "2019-04-03T13:54:45+00:00",
"clientip": "127.0.0.1",
"pid": 7,
"pro": "TCP",
"ssl_pro": "TLSv1.2",
"stus": 200,
"sent": 5384,
"recv": 266,
"sess_time": 0.122,
"up_addr": "192.168.50.215:443",
"up_sent": 423,
"up_recv": 5384,
"up_conn_time": 0.001,
"up_resp_time": "0.001",
"up_sess_time": 0.012
}
- SSH
{
"@access_time": "2019-04-03T13:54:53+00:00",
"clientip": "127.0.0.1",
"pid": 7,
"pro": "TCP",
"ssl_pro": "-",
"stus": 200,
"sent": 3115,
"recv": 3888,
"sess_time": 1.288,
"up_addr": "192.168.50.212:22",
"up_sent": 3911,
"up_recv": 3115,
"up_conn_time": 0,
"up_resp_time": "0.008",
"up_sess_time": 1.287
}
$ssl_preread_protocol 实现IP层实现了不同业务配置,在某种需求上很有意义--虽然存在功能限制。然而Tengine-2.3.0 已经实现的IP层基于域名转发,或许这一特性会引入到Nginx。