2020年7月

文件原地址 自建梯子教程 --Trojan版本
本文仅为自己做笔记备份,仅有对我有用的部分,参考详细教程,请点击链接查看原作者博客

Trojan工作原理浅析
Trojan是一个比较新的翻墙软件,在设计时采用了更适应国情的思路。在穿透GFW时,人们认为强加密和随机混淆可能会欺骗GFW的过滤机制。然而,Trojan实现了这个思路的反面:它模仿了互联网上最常见的HTTPS协议,以诱骗GFW认为它就是HTTPS,从而不被识别。
Snipaste_2020-07-16_19-32-51.png
如图所示,Trojan工作在443端口,并且处理来自外界的HTTPS请求,如果是合法的Trojan请求,那么为该请求提供服务,否则将该流量转交给web服务器Nginx,由Nginx为其提供服务。基于这个工作过程可以知道,Trojan的一切表现均与Nginx一致,不会引入额外特征,从而达到无法识别的效果。当然,为了防止恶意探测,我们需要将80端口的流量全部重定向到443端口,并且服务器只暴露80和443端口,这样可以使得服务器与常见的Web服务器表现一致。
创建用户账户(可选)

为了系统安全,一般不建议直接使用root用户对系统做设置,而vultr默认只有root用户,故可以自己新建一个非root但是有sudo权限的用户继续后面的操作,代码如下所示,注意密码强度不能太低。第一条命令创建用户,第二条命令设置密码,第三条命令将该用户加入sudo组,第四条命令切换到该用户。

sudo useradd -m -s /bin/bash trojanuser
sudo passwd trojanuser
sudo usermod -G sudo trojanuser
su -l trojanuser
创建服务账户
很多看教程的小伙伴问“为什么非要弄这么多用户,直接root不可以吗?”,这里简单回答一下。既然Trojan是占用443端口,使用https协议,所以必然是要安装证书的。本教程使用acme.sh为Trojan生成证书,并配置了acme.sh的自动更新,包括代码和证书的更新。一方面,既然有配置自动更新就有可能出各种问题,毕竟你对更新之后的代码和证书是否可用一无所知。另一方面,acme.sh和Trojan均为开源软件,不一定值得信赖。基于此,为了降低acme.sh和Trojan对系统的影响和其相互影响,故需要单独为acme.sh和Trojan建立没有sudo权限的用户。如果执意使用具有sudo权限的用户,或者头铁直接使用root,出了问题是要自己承担责任的。

这里我们创建两个用户,分别为trojan和acme。其中用户trojan只需要运行trojan服务,无需登录,也无需家目录,故设置为系统用户即可。这里将用户acme也设置为系统用户,但是区别在于acme需要配置acme.sh,故需要家目录。注意到,我并未给用户acme设置密码,所以该用户也不能登录,只能通过其他已经登录的用户切换过去,这样尽可能的保证了系统的安全与任务的独立。因为trojan和acme都需要读写证书文件,所以将acme和trojan添加到同一个用户组certusers,待申请到证书后将证书所有权交给用户组certusers并允许组内用户访问即可。

sudo groupadd certusers
sudo useradd -r -M -G certusers trojan
sudo useradd -r -m -G certusers acme
安装依赖
由于Debian系列系统和CentOS系列系统使用不同的包管理软件,所以安装软件的命令不一样,下面两个小节自己对照自己系统选择命令。

Ubuntu or Debian
更新源

sudo apt update
sudo apt upgrade -y
安装acme.sh需要的依赖。

sudo apt install -y socat cron curl
启动crontab

sudo systemctl start cron
sudo systemctl enable cron
安装Trojan需要的依赖。

sudo apt install -y libcap2-bin xz-utils nano
安装Nginx。

sudo apt install -y nginx
CentOS
安装acme.sh需要的依赖。

sudo yum install -y socat cronie curl
启动crontab

sudo systemctl start crond
sudo systemctl enable crond
安装Trojan需要的依赖。

sudo yum install -y xz nano
安装Nginx。

sudo yum install -y nginx
配置Nginx
若你已经有配置Nginx虚拟主机,配置Nginx之前请,可以自己使用cp命令备份一下原来的虚拟主机,等介绍完基本配置再讲如何与现有服务集成。

关闭默认虚拟主机

由于Nginx配置在Debian系列系统和CentOS系列系统组织方式不同,所以配置文件位置和使用方式有细微区别,为了统一,我将CentOS系列系统的组织结构做细微调整。

在Debian系列系统中,Nginx的虚拟主机配置文件在/etc/nginx/sites-available/文件夹中,如果要开启某一个虚拟主机,则建立一个软连接到/etc/nginx/sites-enabled/文件夹并重启Nginx即可。默认虚拟主机在/etc/nginx/sites-enabled/文件夹,需要关闭掉,否则会冲突。

在CentOS系列系统中,Nginx的虚拟主机配置文件在/etc/nginx/conf.d/文件夹中以.conf后缀保存,写入之后就可以使用。默认虚拟主机集成在Nginx配置文件/etc/nginx/nginx.conf中,需要打开将其中的server块删除,否则会冲突。Debian系列系统中的/etc/nginx/sites-enabled/和/etc/nginx/sites-available/文件夹结构在CentOS系列系统中是没有的,不过这个策略很不错,可以很方便的开启和关闭虚拟主机,我这里手动调整一下。

CentOS
按上述分析,我们使用下面两条命令在/etc/nginx/中添加两个文件夹。

sudo mkdir /etc/nginx/sites-available
sudo mkdir /etc/nginx/sites-enabled
执行如下命令使用nano打开Nginx配置文件,删除其中server块,并添加对/etc/nginx/sites-enabled/文件夹的索引。

sudo nano /etc/nginx/nginx.conf
配置文件修改结果如下图所示。
etc_nginx_nginx_conf.jpg
CentOS反向代理需要配置SELinux允许httpd模块可以联网,否则服务器会返回502错误。

sudo setsebool -P httpd_can_network_connect true
Ubuntu or Debian
使用如下命令关闭Nginx默认虚拟主机。

sudo rm /etc/nginx/sites-enabled/default
写入虚拟主机到Nginx配置文件
1.执行如下命令,使用nano添加虚拟主机。(注意域名<tdom.ml>改为你自己的域名,这是虚拟主机的文件名,只是用来自己识别的。)

sudo nano /etc/nginx/sites-available/<tdom.ml>
基于综述部分讲解的Trojan工作原理,现给定Nginx虚拟主机如下所示。这些虚拟主机可以直接拷贝到上面虚拟主机配置文件中再修改为你自己的,其中要修改的地方包括:

第4行的server_name的值<tdom.ml>改为你自己的域名;
第7行的proxy_pass随便指向一个没有敏感信息的网站都可以,这就是你要反向代理的网站,这里我是用了RFC文档的地址;
第15行的server_name的值<10.10.10.10>改为你自己的IP;
第17行<tdom.ml>改为自己的域名,注意别填错了。
server {

listen 127.0.0.1:80 default_server;

server_name <tdom.ml>;

location / {
    proxy_pass https://www.ietf.org;
}

}

server {

listen 127.0.0.1:80;

server_name <10.10.10.10>;

return 301 https://<tdom.ml>$request_uri;

}

server {

listen 0.0.0.0:80;
listen [::]:80;

server_name _;

location / {
    return 301 https://$host$request_uri;
}

location /.well-known/acme-challenge {
   root /var/www/acme-challenge;
}

}
解释一下这些虚拟主机的一些细节:第一个server接收来自Trojan的流量,与上面Trojan配置文件对应;第二个server也是接收来自Trojan的流量,但是这个流量尝试使用IP而不是域名访问服务器,所以将其认为是异常流量,并重定向到域名;第三个server接收除127.0.0.1:80外的所有80端口的流量并重定向到443端口,这样便开启了全站https,可有效的防止恶意探测,其中.well-known是为申请证书所准备的,下文用到的时候自然明白。注意到,第一个和第二个server对应综述部分原理图中的蓝色数据流,第三个server对应综述部分原理图中的红色数据流,综述部分原理图中的绿色数据流不会流到Nginx。

2.使能配置文件注意域名<tdom.ml>改为你自己的域名

sudo ln -s /etc/nginx/sites-available/<tdom.ml> /etc/nginx/sites-enabled/
启动Nginx
Nginx启动命令和Trojan一样,就不过多解释了。

sudo systemctl restart nginx
sudo systemctl status nginx
配置证书
当从Let’s Encrypt获得证书时,Let’s Encrypt会验证证书中域名的控制权。一般采用HTTP-01或DNS-01方式来验证,详情参考官方文档验证方式。本文使用HTTP-01方式验证,若需要使用DNS-01方式验证,参考acme.sh官方文档How to use DNS API。

创建证书文件夹
第一条命令新建一个文件夹/etc/letsencrypt/live用于存放证书。第二条命令将证书文件夹所有者改为acme,使得用户acme有权限写入证书。

sudo mkdir -p /etc/letsencrypt/live
sudo chown -R acme:acme /etc/letsencrypt/live
创建webroot并修改所有者
本文使用acme.sh的http方式申请证书,http方式需要在网站根目录下放置一个文件来验证域名所有权,故需要acme.sh和nginx均对webroot目录有权限,故将运行Nginx的worker进程加入certusers组,下文再将webroot目录附加给certusers组即可。

在不同的Linux发新版本中,nginx可能使用不同的用户运行worker process,可能为www-data,nginx,nobody中的一个,故需要自己运行下述命令查找nginx: worker process所属用户:

ps -eo user,command|grep nginx
上述命令输出第二行第一列即为nginx: worker process所属用户,然后根据实际情况,运行下面三个命令之一:

sudo usermod -G certusers www-data
sudo usermod -G certusers nginx
sudo usermod -G certusers nobody
运行下面两条命令,第一条命令新建一个文件夹/var/www/acme-challenge用于给acme.sh存放域名验证文件。第二条命令将证书文件夹所有者改为acme,使得用户acme有权限写入文件,同时当验证的时候Nginx可以读取该文件。

sudo mkdir -p /var/www/acme-challenge
sudo chown -R acme:certusers /var/www/acme-challenge
安装acme.sh自动管理CA证书脚本
分别执行如下命令,注意看是否报错。第一条命令切换到用户acme。第二条命令安装acme.sh。第三条命令退出当前用户。第四条命令再次切换到用户acme。注意到这里两次切换用户的操作不能省略,因为安装完acme.sh之后要重新登录当前用户,否则无法识别出acme.sh命令。

sudo su -l -s /bin/bash acme
curl https://get.acme.sh | sh
exit
sudo su -l -s /bin/bash acme
申请证书
执行如下命令(注意域名<tdom.ml>改为你自己的域名),等待一会儿。

acme.sh --issue -d <tdom.ml> -w /var/www/acme-challenge
看到下图的提示表示证书申请成功。
证书申请成功提示.jpg
申请失败怎么办?证书申请失败的可能性一般有:1. 文件夹权限问题,请仔细检查每一步是否都正确;2. 证书申请次数超限,此时切忌反复尝试。证书每一个周申请次数是有限制的(20次),如果超限了就需要等一个周或者更换域名了(这个限制是争对每一个子域单独做的限制,所以万一超限了还可以用子域名继续部署)。解决方案是:在上述命令后加--staging参数继续测试。测试通过之后,删除上图所示四个证书文件以及该域名对应文件夹并取消--staging参数再执行一次。--staging参数申请的证书只作为测试用,客户端是无法认证通过的(提示SSL handshake failed: tlsv1 alert unknown ca),所以使用--staging参数申请到了证书之后要去掉--staging参数重新申请一次。

安装证书
执行如下命令(注意域名<tdom.ml>改为你自己的域名),第一条命令使用acme.sh将证书安装到certfiles目录,这样acme.sh更新证书的时候会自动将新的证书安装到这里。第二条命令是配置acme.sh自动更新和自动更新证书,这样配置完Trojan之后一般不用管服务器。

acme.sh --install-cert -d <tdom.ml> --key-file /etc/letsencrypt/live/private.key --fullchain-file /etc/letsencrypt/live/certificate.crt
acme.sh --upgrade --auto-upgrade
修改权限
最后还要允许组内用户访问证书。可通过如下命令实现。第一条命令将证书文件夹所在用户组改为certusers。第二条命令是赋予证书文件夹组内用户读取权限。运行这两条命令之后用户trojan就有权限读取证书了。第三条命令退出用户acme,因为证书已经安装完成。

chown -R acme:certusers /etc/letsencrypt/live
chmod -R 750 /etc/letsencrypt/live
exit
配置Trojan
安装Trojan
分别执行如下四个命令,注意看是否报错。第一个命令是安装Trojan,安装完成一般会提示版本号注意看是否是最新版本。第二个命令是将Trojan配置文件的所有者修改为用户trojan,由于使用sudo安装的Trojan,该配置文件默认是属于root用户的,而我们需要使用用户trojan运行Trojan,不修改所有者会导致启动Trojan遇到权限问题。第三个命令备份Trojan配置文件,以防万一。第四个命令是使用nano修改配置文件。

sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/trojan-gfw/trojan-quickstart/master/trojan-quickstart.sh)"
sudo chown -R trojan:trojan /usr/local/etc/trojan
sudo cp /usr/local/etc/trojan/config.json /usr/local/etc/trojan/config.json.bak
sudo nano /usr/local/etc/trojan/config.json
第四个命令执行完之后屏幕会显示Trojan的配置文件,定位到password、cert和key并修改。密码按自己喜好,cert和key分别改为/etc/letsencrypt/live/certificate.crt和/etc/letsencrypt/live/private.key。编辑完成配置文件之后按屏幕下方快捷键提示(^O和^X即:Ctrl+O和Ctrl+X)保存并退出nano。修改之后的config文件如图所示。另外,如果有IPv6地址,将local_addr的0.0.0.0改为::才可以使用。
修改config.jpg
启动Trojan
修改Trojan启动用户
执行如下命令,打开trojan.service文件,并将用户修改为trojan。

sudo nano /etc/systemd/system/trojan.service
添加用户效果如图所示,注意等号旁边没有空格。
changeUser.jpg
然后重新加载配置文件。

sudo systemctl daemon-reload
赋予Trojan监听443端口能力
执行如下命令,赋予Trojan监听1024以下端口的能力,使得Trojan可以监听到443端口。这是由于我们使用非root用户启动Trojan,但是Linux默认不允许非root用户启动的进程监听1024以下的端口,除非为每一个二进制文件显式声明。

sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/trojan
使用systemd启动Trojan
Trojan启动、查看状态命令分别如下,第一条是启动Trojan,第二条是查看Trojan运行状态。启动之后再查看一下状态,Trojan显示active (running)即表示正常启动了。如果出现fatal: config.json(n): invalid code sequence错误,那么是你的配置文件第n行有错误,请检查。如果启动失败,还可以用journalctl -f -u trojan查看systemd的日志。

sudo systemctl restart trojan
sudo systemctl status trojan
更新证书
当acme.sh重新安装证书之后,需要通知Trojan重新加载证书。最简单的方案是每三个月登录服务器重启Trojan,但是不够完美,毕竟重启的时候会导致服务中断。其实Trojan有实现reload certificate and private key功能,只需要在证书更新后给Trojan发送SIGUSR1消息即可。Trojan收到SIGUSR1消息后便会自动加载新的证书和密钥文件,这样就不用重启Trojan了。手动给Trojan发送SIGUSR1消息的命令是sudo -u trojan killall -s SIGUSR1 trojan,但是这样也不够完美,也得每三个月登录服务器运行一次该命令。其实我们可以给用户trojan添加定时任务,使其每个月运行一次该命令即可。实现如下。

首先,编辑用户trojan的crontab文件

sudo -u trojan crontab -e
在文件末尾添加一行如下,该行表示每个月1号的时候运行命令killall -s SIGUSR1 trojan,由于是使用用户trojan运行的,故不需要在前面加sudo -u trojan。

0 0 1 killall -s SIGUSR1 trojan
最后查看crontab是否生效。

sudo -u trojan crontab -l
更新Trojan
如果Trojan版本有更新(可以去这里查看是否有更新),那么使用本教程搭建的服务器端更新Trojan版本只需要三条命令即可,不过要注意的是,第一条命令会提示是否覆盖配置文件,如果没有必要请回答n,否则配置文件将会被覆盖(如果不小心覆盖了就得自己重新编辑了)。第二条命令重新赋予Trojan监听443端口的能力。第三条命令重启Trojan。

sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/trojan-gfw/trojan-quickstart/master/trojan-quickstart.sh)"
sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/trojan
sudo systemctl restart trojan
配置Trojan和Nginx开机自启
虽然开机自启一般用不着,除非vultr机房停电,但是反正也没什么代价,弄一下吧。

sudo systemctl enable trojan
sudo systemctl enable nginx
检查服务器是否配置成功
到这里服务器就配置完成了。此时你可以在浏览器里面访问你的网站看是否能够访问,如果你的网站可以访问了,那么就一切正常啦。

另外,基于以上考虑到的可能的恶意探测,可以验证一下以下情况是否正常。

浏览器中使用ip访问:重定向到https://tdom.ml;
浏览器中使用https://ip访问:重定向到https://tdom.ml(跳转的时候浏览器可能提示不安全是正常的);
浏览器中使用tdom.ml访问:重定向到https://tdom.ml