平时在动漫花园等 BT 站用磁力链接下资源时总是遇到因为所有 Tracker 服务器都连不上而需要等待很久才能开始下载的情况,就萌生了自己搭建 Tracker 服务器念头。

Tracker Server

首先准备 Linux Server 一台,本文中使用的系统为 Ubuntu 16.04。

网上开源的 Tracker Server 实现有不少,经过对比,我们采用 bittorrent-tracker 这一项目作为我们的服务端。因为其同时支持 IPv4 和 IPv6 和 http、udp、websocket 等多种上报方式,且部署简单。

首先安装 nodejs 和包管理器 npm:

1
2
3
$curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash -
$sudo apt-get install -y nodejs
$sudo apt-get install npm

安装 bittorrent-tracker:

1
$npm install -g bittorrent-tracker

若只想搭建一个简单的 tracker,安装完毕后运行$bittorrent-tracker即可开始使用,更多参数请自行探索。

TLS over nginx

个人比较常用 http 向 tracker 进行上报,但是现在已经到了 8102 年末了,http 这种明文协议不应该成为我们的选择。

所以我们使用 nginx 进行反向代理来反代 http 请求,同时使用 TLS 加密所有连接。

当然首先你需要有一个域名和对应的证书,推荐使用 Let’s encrypt 的免费证书。我使用它们家的证书 bot 来自动签发和管理 nginx 配置文件 TLS 部分内容。为了便于说明,下文中以TRACKER_DOMAIN替代我使用的域名。

首先安装 nginx,因为后续需要使用一些特性,我们安装完整版的 nginx:

1
$sudo apt install nginx-full

编辑 nginx 配置文件:

1
$sudo vim /etc/nginx/sites-enabled/tracker
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
server {
server_name TRACKER_DOMAIN;#监听域名
#同时监听ipv4和ipv6
listen 443 ssl;
listen [::]:443 ssl;
root /var/www/tracker;

location / {

proxy_pass http://127.0.0.1:8000/; #8000为bittorrent-tracker默认端口,请根据自己设置修改
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

#以下为 Let's encrypt certbot 自行配置完成,请根据证书情况自行修改
ssl_certificate /etc/letsencrypt/live/TRACKER_DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/TRACKER_DOMAIN/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}
# 配置http端口重定向至https
server {
if ($host = TRACKER_DOMAIN) {
return 301 https://$host$request_uri;
}

listen 80;
listen [::]:80;
server_name TRACKER_DOMAIN;
return 404;
}

配置完成后重启 nginx,并启动 bittorrent-tracker 即可:

1
2
$service nginx restart
$bittorrent-tracker -p 8000 --trust-proxy --http --http-hostname 127.0.0.1

注意因为 http 请求由 nginx 处理,所以服务端仅需要监听在127.0.0.1即可。--trust-proxy参数必须打开,用于保证服务端从X-Forwarded-For字段获取客户端真实IP。

CDN Support

有时候出于安全和全球访问稳定性考虑,需要将服务器真实 IP 通过 CDN 隐藏起来,通过 CDN 转发所有请求到服务端。对于一般用户来说, Cloudflare 的免费套餐是一个好选择。但是 CDN 在转发请求时会导致 nginx 将 CDN 服务器地址一并加入X-Forwarded-For字段转发给 Tracker 服务端,导致错误的记录。这需要使用 nginx 的 Realip 模块来将 CDN 的 IP 排除:

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
server {
server_name TRACKER_DOMAIN;#监听域名
#同时监听ipv4和ipv6
listen 443 ssl;
listen [::]:443 ssl;
root /var/www/tracker;

location / {
#设置来自CDN的可信IP段,以Cloudflare为例
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;

real_ip_header X-Forwarded-For; #从X-Forwarded-For检索IP地址。
real_ip_recursive on; #递归去除可信IP

proxy_pass http://127.0.0.1:8000/; #8000为bittorrent-tracker默认端口,请根据自己设置修改
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

#以下为 Let's encrypt certbot 自行配置完成,请根据证书情况自行修改
ssl_certificate /etc/letsencrypt/live/TRACKER_DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/TRACKER_DOMAIN/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}
# 配置http端口重定向至https
server {
if ($host = TRACKER_DOMAIN) {
return 301 https://$host$request_uri;
}

listen 80;
listen [::]:80;
server_name TRACKER_DOMAIN;
return 404;
}