利用 Nginx 部署支持 SNI 转发的 Trojan 服务端以及踩坑记录

前言

之前使用 Caddy 部署的服务端,到现在出现了一个问题:不具备部署多个网络服务的能力。

我尝试过使用反向代理,但无论如何都不能成功连接。正当我一筹莫展之时,我发现了这篇文章。啪的一下很快啊,我马上蹦了起来开始操作。

部署 Trojan 的教程可以查看这篇文章

由于部署过程中有很多坑,为引导读者避坑,本文将以我的操作顺序以及问题的解决过程来记录,所以如果你只是想知道部署方法,请先通读文章。

安装 Nginx

一般情况下,只需执行以下命令:

bash
1
sudo apt install nginx

但是我比较“喜欢”绕(搜索关键词不对),于是我在官网上查到了完整的安装方法:

bash
1
2
3
4
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

确保输出为:

1
2
3
pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
uid nginx signing key <signing-key@nginx.com>

如果不是就删掉 /usr/share/keyrings/nginx-archive-keyring.gpg 再来。

下一步就是安装了:

主要版并不是不稳定,按照官网说法,只是不那么稳定而已。我选择的是主要版。

官网没写怎么换分支,我也不会。

bash
1
2
3
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
bash
1
2
sudo apt update
sudo apt install nginx

安装 Certbot

文档:https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal

安装 Snapd 的方法:

bash
1
sudo apt install snapd
bash
1
2
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

配置

安装之后,噩梦就开始了。

我开始到处学习 Nginx 的配置文件,最后我是这么做的:

首先删除一个默认的配置文件:/etc/nginx/conf.d/default.conf

然后编辑配置文件:

/etc/nginx/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
user  nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}

# 这部分是原文中复制过来的
stream {
# 这里就是 SNI 识别,将域名映射成一个配置名
map $ssl_preread_server_name $backend_name {
[Trojan 与伪装服务器共用的域名] trojan;
# 域名都不匹配情况下的默认值
default web;
}

# web,配置转发详情
upstream web {
server 127.0.0.1:80;
}

# trojan,配置转发详情
upstream trojan {
server 127.0.0.1:[Trojan 端口];
}

# 监听 443 并开启 ssl_preread
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;

keepalive_timeout 65;

include /etc/nginx/conf.d/*.conf;

server {
server_name [Trojan 与伪装服务器共用的域名];

location / {
root /usr/share/nginx/html/;
index index.html index.htm;
}

listen 80;
}
}

编辑 Trojan 的配置文件 /usr/local/etc/trojan/config.json ,把 local_port 修改一下,重启。

bash
1
sudo systemctl restart trojan

注册系统服务,启动 Nginx:

bash
1
2
3
sudo nginx -s stop
sudo systemctl enable nginx
sudo systemctl start nginx

申请证书:

bash
1
sudo certbot --nginx

Certbot 会从配置文件中发现你配置的域名并自动修改配置文件,配置 SSL 。

配置完成后,再次修改配置文件:

/etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
server_name [域名];

location / {
root /usr/share/nginx/html/;
index index.html index.htm;
}

listen 80 ssl;
ssl_certificate /etc/letsencrypt/live/[域名]/fullchain.pem; # 记下这两个路径
ssl_certificate_key /etc/letsencrypt/live/[域名]/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
bash
1
sudo nginx -s reload

编辑 Trojan 的配置文件 /usr/local/etc/trojan/config.json ,把证书路径修改一下,重启。

bash
1
sudo systemctl restart trojan

这时我们就可以用原来的客户端配置连接到代理了。

作为一个合格的懒狗自动化玩家,我们肯定是要测试一下自动更新 SSL 的可靠程度。(怕自己配置出错)

bash
1
sudo certbot renew --dry-run

但我们竟收到了 400 错误!

打开浏览器:

浏览器

按照我们配置 Caddy 的经验,这样配置应该没有错。

上网搜索,怎么配怎么不对:有说 ssl 后加 default 的,有说端口 443 加 ssl 而 80 不加的,可行的就第一种,但也失败了。(说是失败,不如说是不适合我们的需求)

到这里,我们就止步不前了,我也尝试过搜索 Nginx 取消这个功能的办法,无奈没有搜到。

于是我开始翻看我看过的各种文章,果然还是最开始给我灵感的文章又给了我灵感。文章中的转发设置中并没有转到 80 端口,而这个问题产生的原因是使用 HTTP 协议访问了启用了 SSL 的 80 端口,我们再新开一个端口,用于 SSL 连接不就行了吗?

开干:

/etc/nginx/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
77
78
79
80
user  nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}

stream {
map $ssl_preread_server_name $backend_name {
[Trojan 及伪装域名] trojan;
default web;
}

upstream web {
server 127.0.0.1:[Web SSL 端口];
}

upstream trojan {
server 127.0.0.1:[Trojan 端口];
}

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

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;

server {
server_name [Trojan 及伪装域名];

location / {
root /usr/share/nginx/html/;
index index.html index.htm;
}

listen [Web SSL 端口] ssl;
listen 80;
ssl_certificate /etc/letsencrypt/live/[*]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[*]/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
server_name [其他服务域名];

listen [Web SSL 端口] ssl;
listen 80;
ssl_certificate /etc/letsencrypt/live/[*]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[*]/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
}

重载 Nginx ,大功告成:

bash
1
sudo nginx -s reload

后记

折腾这个花了我一个下午的光阴,这次折腾给我一个启示:思维不能僵化。HTTP 端口不一定是 80 ,HTTPS 端口也不一定是 443 。折腾完后我再次蹦了起来:NB !并马上打开电脑记录。

本文内容可能与实际情况有些出入(文件内容、实现效果等),但最终结果部分应该不会错。

利用 Nginx 部署支持 SNI 转发的 Trojan 服务端以及踩坑记录

https://blog.xfqlittlefan.xyz/2022/02/02/Nginx-Trojan-Website/

作者

xfqwdsj

发布于

2022-02-02

更新于

2022-12-10

许可协议

评论