一、问题探讨
服务器上部署了Trojan,它就会默认自动监听443端口号,并且识别流量特征,如果是Trojan协议流量,就会走科学上网逻辑;否则转发到本机80端口号,走Web服务器逻辑,前端就会显示网页内容。这是Trojan科学上网的基本原理,可以看到Trojan比较特殊,它的工作方式决定了其必须直接对接流量入口,Nginx 都只能挂在它的后面,这导致Nginx如果想要使用443端口号,就会和Trojan冲突。
二、SNI解决方案
SNI 的全称是 Server Name Indication(TLS 服务器名称指示)。SNI 是一种扩展的 TLS 协议,在建立加密连接时,客户端可以在发送加密请求时传输要访问的域名(服务器名称),这使得服务器能够根据接收到的域名选择合适的证书来建立加密连接。
在使用 SNI 之前,服务器在建立加密连接时只能根据 IP 地址来确定要提供的证书,这导致一些限制,例如一台服务器只能使用一个 SSL 证书。但是随着云服务和虚拟主机的普及,一台服务器可能托管多个域名,每个域名需要使用不同的 SSL 证书。SNI 解决了这个问题,允许一台服务器根据接收到的域名选择正确的证书来建立加密连接。
Nginx配置多个https域名就是基于SNI
server {
listen 443 ssl;
server_name www.chengxiaobai.com;
ssl_certificate www.chengxiaobai.com.crt;
}
server {
listen 443 ssl;
server_name nothing.chengxiaobai.com;
ssl_certificate nothing.chengxiaobai.com.crt;
}
如果没有Trojan服务,直接这么配置就可以实现多https域名监听443端口号,但是Trojan监听了443端口号,导致Nginx已经不能监听443端口号,况且Nginx还挂在Trojan后面。
1、Nginx的两种转发方式
Nginx支持OSI网络模型的七层应用层转发以及基于SNI的4层转发。
七层应用层转发:
nginx的配置文件中的常见配置
http {
......
server{
listen 80;
server_name xxx.kdyzm.cn;
location /{
proxy_pass http://127.0.0.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
}
这种proxy_pass反向代理就是应用层转发,从外层的“http”就能看出来,因为http是一种应用层协议。
基于SNI四层转发:
它在Nginx中的配置并不是在http配置模块中,因为它的作用层不一样,它作用于TCP/UDP协议层,直接转发TCP/UDP数据流。其配置形式如下
# 流量转发核心配置
stream {
# 这里就是 SNI 识别,将域名映射成一个配置名
map $ssl_preread_server_name $backend_name {
a.kdyzm.cn a;
b.kdyzm.cn b;
# 域名都不匹配情况下的默认值
}
# trojan,配置转发详情
upstream a {
server 127.0.0.1:4431;
}
# vmess,配置转发详情
upstream b {
server 127.0.0.1:4432;
}
# 监听 443 并开启 ssl_preread
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}
就使用这种方式解决上面说的问题:大体的思路就是trojan修改监听的端口号从443改成4431,其它的可以改成4432等之类的,然后让nginx监听443端口号,根据stream配置中的路由规则转发到trojan或者其他服务。
2、解决方案
第一步:修改trojan服务监听的端口号
trojan服务的配置文件在/usr/local/etc/trojan/config.json
,将local_port改成4431端口号
然后重启trojan服务:service trojan restart
第二步:修改nginx配置,新增stream指令路由
# 流量转发核心配置
stream {
# 这里就是 SNI 识别,将域名映射成一个配置名
map $ssl_preread_server_name $backend_name {
waline.kdyzm.cn waline;
blog.kdyzm.cn trojan;
# 域名都不匹配情况下的默认值
}
# trojan,配置转发详情
upstream trojan {
server 127.0.0.1:4431;
}
# vmess,配置转发详情
upstream waline {
server 127.0.0.1:4432;
}
# 监听 443 并开启 ssl_preread
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}
waline.kdyzm.cn是我新增的https域名,所以需要新增下http模块中的配置
server{
listen 80;
server_name waline.kdyzm.cn;
rewrite ^(.*)$ https://$host$1 permanent;
}
server{
listen 4432;
server_name waline.kdyzm.cn;
ssl on;
ssl_certificate /etc/nginx/ssl/waline/cert.pem;
ssl_certificate_key /etc/nginx/ssl/waline/key.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";
location /{
proxy_pass http://127.0.0.1:8360;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
# cache
add_header Cache-Control no-cache;
expires 12h;
}
}
之后重启nginx:service nginx restart
,如果重启失败,提示stream指令不合法啥的错误,注意手动引入包:
load_module /usr/lib64/nginx/modules/ngx_stream_module.so;
关于如何申请https证书,可以看下acme项目:https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E
和我之前的博客:https://blog.kdyzm.cn/post/58
其它客户端什么的都不用变,END.
参考文档:https://www.chengxiaobai.com/trouble-maker/trojan-shared-443-port-scheme
注意:本文归作者所有,未经作者允许,不得转载