条件

域名A: aaa.example.com
域名B: bbb.example.com
域名trojan: trojan.example.com
域名A,域名B和域名trojan的DNS解析均为同一个IP地址。
aaa.example.combbb.example.com为不同的其他站点,trojan.example.com为trojan站点。

介绍

HTTPS 已经是互联网服务准入的基本门槛了,同时 443 端口作为 HTTPS 请求的默认端口,在虚拟主机服务的支持下,搭配的天衣无缝。

但是 Trojan 比较特殊,因为它的工作方式导致了其必须直接对接流量入口,否则其协议无法被服务端正常识别,同时为了增加服务的隐蔽性,一般会把它配置在 443 端口。但是 443 端口只有一个,虽然 Trojan 提供了「非标请求」的转发功能,但是毕竟是一个新生事物,所有流量都过它手,在稳定、性能、灵活等等方面都不够好,而且还不支持 TLS 转发。

Nginx安装

若未安装Nginx或没有包含对应模块,教程看这→点此查看Nginx安装教程

执行nginx -V查看是否包含--with-stream_ssl_preread_module模块

Trojan-go安装

若未安装Trojan-go,安装教程在这→点此查看Trojan-go安装教程

端口分配

Nginx服务占用端口为80,443,1443
Trojan服务占用端口为10443

端口80用于Nginx正常提供http服务
端口443用于Nginx的stream块基于SNI分流,如果是trojan,分流至10443的trojan服务。否则分流至自身的1443端口对外提供正常的https服务。
端口1443用于Nginx正常提供https服务,所有非trojan站点都可以在nginx中使用这个端口进行配置,无需每个站点一个端口,支持本地静态和动态proxy_pass。
端口10443用于Nginx转发至Trojan服务。

防火墙仅对外开放80443端口即可。

配置

首先得明确,Trojan 是无法通过 Nginx 在 7 层进行代理的,所以它设定必须在流量入口,Nginx 都只能挂在它的后面。Nginx 支持基于 SNI 的 4 层转发。简单说就是:识别 SNI 信息,然后直接转发 TCP/UDP 数据流。

Nginx的stream块在http块前。流量方向为nginx > stream [ > server ] > http [ > server ]
所以同时配置stream块和http块的listen 443,其中http块会失效。
经过摸索和配置,总结了一套自认为最优雅的nginx和trojan-go的配置方案。

Nginx stream块配置如下

/usr/locol/nginx/conf/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
stream {
map $ssl_preread_server_name $backend_name {
trojan.example.com trojan;
aaa.example.com web;
bbb.example.com web;
; 根据需要选择是否开启默认站点
#default web; # 默认web
}

upstream trojan {
server 127.0.0.1:10443;
}

upstream web {
server 127.0.0.1:1443;
}

server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}

Nginx http块配置如下

多站点域名A,域名B,域名trojan共存,注意默认的listen 80不能删除,因为trojan-go的remote_addrremote_port不支持https。

/usr/locol/nginx/conf/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
http {
server {
listen 1443 ssl;
http2 on;
server_name aaa.example.com;
client_max_body_size 1000M;

ssl_certificate aaa.example.com.crt;
ssl_certificate_key aaa.example.com.key;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

server {
listen 1443 ssl;
http2 on;
server_name bbb.example.com;
client_max_body_size 1000M;

ssl_certificate bbb.example.com.crt;
ssl_certificate_key bbb.example.com.key;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

server {
listen 1443 ssl;
http2 on;
server_name trojan.example.com;
client_max_body_size 1000M;

ssl_certificate trojan.example.com.crt;
ssl_certificate_key trojan.example.com.key;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

Trojan-go配置如下

server.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"run_type": "server",
"local_addr": "0.0.0.0",
"local_port": 10443,
"remote_addr": "127.0.0.1",
"remote_port": 80,
"log_level": 2,
"password": [
"密码"
],
"ssl": {
"cert": "trojan.example.com.crt",
"key": "trojan.example.com.key",
"sni": "trojan.example.com",
"fallback_addr": "127.0.0.1",
"fallback_port": 1443
},
"router": {
"enabled": true,
"block": [
"geoip:private"
],
"geoip": "/root/trojan-go/geoip.dat",
"geosite": "/root/trojan-go/geosite.dat"
}
}

总结

关于端口合理分配,以下仅仅是我的癖好
10000以上端口为各个自有服务或三方服务端口,用于nginx反向代理访问,禁止开放防火墙。类似mysql端口3306,则对应就是13306。redis默认6379则对应16379。
1443为443的下位替代,不是三方服务,保证http块中的server正常服务。
如有多域名多证书多站点配置的需要可以看我的这个文章。Nginx多域名多证书多站点配置最佳实践

感谢大佬的探索,参考链接:Trojan 共用 443 端口方案