Trojan 原理简介

最简单来讲, Trojan 是通过 HTTPS 的方式进行穿墙操作的, 也就是所有的请求, 先通过 Trojan 客户端加工之后, 以正常 HTTPS 请求的方式发往墙外服务器, 然后在 Trojan 服务端, 对请求内容进行解析后, 将原请求转发给目标服务器,再将目标服务器返回的数据包装成正常的 HTTPS 响应包返回给 Trojan 客户端。

Trojan 数据流

Trojan 和 Nginx 的配合模式

由于整个穿墙操作借助的是 TLS, 所以 Trojan 需要监听 443 端口, 而 Nginx 则是在 Trojan 后面提供网页服务的。 也就是说其实 Trojan 会导致 Nginx 无法使用 HTTPS, Trojan 会独占 443 端口, 只为穿墙域名提供 HTTPS 服务, Nginx 只能使用 HTTP。 这就很蛋疼了, 为了穿墙导致其他域名全部无法使用 HTTPS, 如果这台服务器单纯就是用来扶墙的还好, 但是如果还有别的站点需要进行托管, 那就很不爽了。

我们可以通过一张图来看一下 Trojan 和 Nginx 的配合模式。

Trojan-Nginx

单看图,是不是觉得挺好啊,Trojan 这不是能识别 Trojan 请求和非 Trojan 请求么? 那其他需要托管的站点通过浏览器访问的时候,不都应该是正常的么? 想法是美好的,当然如果你不在乎是不是 HTTPS,现实也很美好。

问题是什么

Trojan 只能为一个域名配置 HTTPS,而它本身又独占了 443 端口, 就导致你的整台服务器上就只能有一个 HTTPS 域名服务, 多个 HTTPS 配置不了。

那么,如何在使用 Trojan 的时候, 部署多个 HTTPS 站点呢?

解决方案

Trojan 和 Nginx 的配合模式一节中我们知道, 所有请求先是经过 Trojan, 然后由 Trojan 进行甄别之后决定是否要交给 Nginx 进行处理。

我们把处理流程进行一下更改,其实就能是实现我们的需求了。 如图:

Nginx In Front

这样的话,我们就把 443 端口从 Trojan 中解放出来交还给了 Nginx。 所有请求先到达 Nginx, Nginx 中进行处理, 如果请求的是为 Trojan 配置的域名, 那么就将请求转发给 Trojan, 如果不是则由 Nginx 自行处理。

使用 Nginx ngx_stream_ssl_preread_module 这个模块就能满足我们的诉求。 看这儿

具体配置

/etc/nginx/nginx.conf

... stream{ map $ssl_preread_server_name $name { trojan.xxx.com trojan; anotherdomain.com nginx; } upstream trojan { server 127.0.0.1:8889; } upstream nginx { server 127.0.0.1:8888; } server { listen 443; listen [::]:443; proxy_pass $name; ssl_preread on; } } http { server { server { listen 8888 ssl; listen [::]:8888 ssl; server_name anotherdomain.com; ssl_certificate xxxxxx.crt; ssl_certificate_key xxxxxxx.key; sl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; ... } } ...

/usr/src/trojan/server.conf

{ "run_type": "server", "local_addr": "0.0.0.0", "local_port": 8889, "remote_addr": "127.0.0.1", "remote_port": 80, ... }