当前一台个人VPS上部署多个服务已是常态,不同服务也会绑定不同的域名,因此想要提供HTTPS服务,就需要共用443端口。如果装有
Trojan
,在共用443端口的时候还要注意配置。此文针对trojan和Nginx共用443端口配置过程做简要记录,以备遗忘。
Trojan默认工作在服务器443端口,直接对接入口流量。如果要与Nginx共用443端口,需要设计为统一流量入口,根据域名服务进行二次转发。如下图所示:
flowchart LR;
A[Client] -->|HTTPS Request| B[Nginx] --> C{Dispatch}
C --> Trojan & V2Ray -->|Invalid Request| D[Default Page]
C --> Web
利用TLS握手阶段的SNI信息将流量在4层进行转发。Nginx支持基于SNI的4层转发,也就是识别SNI信息,然后直接转发TCP/UDP数据流。该功能由ngx_stream_ssl_preread_module
模块提供,Nginx默认不启用该模块,该模块使用stream
,不是http
。
➜ ~ nginx -V
nginx version: nginx/1.20.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
确认Nginx是支持的,下面直接进行配置。
user nginx;
pid /var/run/nginx.pid;
# 其他配置保持默认即可
# 流量转发核心配置
stream {
# 这里就是 SNI 识别,将域名映射成一个配置名
map $ssl_preread_server_name $backend_name {
aa.metaprogramming.space web;
trojan.metaprogramming.space proxy_trojan;;
# 域名都不匹配情况下的默认值
default web;
}
# web,配置转发详情
upstream web {
server 127.0.0.1:10240;
}
# trojan,配置转发详情
upstream trojan {
server 127.0.0.1:10241;
}
upstream proxy_trojan {
server 127.0.0.1:10249;
}
server {
listen 10249 proxy_protocol;
proxy_pass trojan; # 转发给真正的服务
}
# 监听 443 并开启 ssl_preread
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}
http {
server {
listen 10240 ssl proxy_protocol;
server_name xxx.metaprogramming.space;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_certificate /etc/nginx/cert/xxx.crt;
ssl_certificate_key /etc/nginx/cert/xxx.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
}
}
}
trojan 服务配置为监听 10241端口即可。
在后端服务为了拿请求的real client ip
使用Proxy Protocol
协议进行通信,Nginx支持该协议,只需要在转发端和接收端配置上代理协议即可。所以在上述的转发层增加了proxy_protocol
配置。由于Trojan不支持该协议,所以增加了一个中间层帮Trojan把协议去掉,从而保证其他服务能获取用户真实IP,又保证Trojan流量正常处理。
参考: