前言

很多人都有看电影电视的经历抑或是习惯。囿于国内的言论环境,很多资源都无法正常引进,或者在引进的时候被删减。通过各类云盘流通的资源质量良莠不齐,而使用国外流媒体平台观看又会面对诸多限制和高昂成本的问题,对于我来说,直接食用国外通过 BitTorrent(BT)分享的生肉或者加上自行寻找配对的熟肉字幕更加具有实践性。然而,又一次苦于国内的网络连通性和网络封锁,国内下载 BT 的时候免不了有的资源没有速度、无法下载完全。这个时候,一个国外的网络环境不可或缺。

然而,绝大部分的国外网络资源,无论是私有专用网还是服务器,都会受到 DMCA 或者其他类型的版权投诉的影响,使 BT 下载不可持续。也有一部分的服务器位于独特的司法管辖内,能给予资源获取较高的自由度。这篇文章分享了我低成本和低门槛的海外 BT 下载和取回的解决方案。

读懂本文,你需要:

  1. 对 Linux 系统和其操作的基本了解
  2. 对 Nginx 的基本了解
  3. 对英文的掌握度
  4. 一个海外网络环境

本文方案的构成:

  1. 某主机商(见文末)位于比利时卢森堡的服务器,挂载缓存 HDD 硬盘(256GB @ 35MB/s),约 26¥/月
  2. qBitTorrent-Nox 及其 Web UI 作为 BT 下载器
  3. Nginx 作为反向代理前端、目录列举和用户鉴权的工具
  4. AList 作为目录列举和文件管理工具

本文的局限性:

  1. 没有涉及 PT(Private Tracker)的使用
  2. 使用一键服务器环境,牺牲了安全性,存在可能的隐患
  3. 下载、取回速度显著受限于服务器挂载硬盘的读写速度以及(在直连的情况下)服务器和大陆的网络连通性

本文约 4000 字。

上手

准备服务器环境

系统为 Ubuntu 20.04。开机 df -h 看看磁盘状态:

root@localhost:~# df -h
Filesystem Size Used Avail Use% Mounted on
udev 459M 0 459M 0% /dev
tmpfs 98M 628K 98M 1% /run
/dev/vda1 20G 974M 18G 6% /
tmpfs 489M 0 489M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 489M 0 489M 0% /sys/fs/cgroup
tmpfs 98M 0 98M 0% /run/user/0

首先 apt update && apt install -y python3-pip 更新软件列表,然后安装 pip3(Python 3)已经内置。

选择最优源

使用 pip3 install apt-select 安装 apt-select,这个工具可以为包管理工具选择最快的源,方便后面的其他安装。

因为服务器在比利时卢森堡,所以使用比利时的国家代码 BE:

apt-select -C BE -m up-to-date -c -t 10

apt-select 输出如下:

Getting list of mirrors...done.
Testing latency to mirror(s)
[4/4] 100%
Getting list of launchpad URLs...done.
Looking up 4 status(es)
[2/4] 50%
1. mirror.unix-solutions.be
Latency: 5.25 ms
Org: Unix-Solutions BVBA
Status: Up to date
Speed: 1 Gbps
2. archive.ubuntu.com
Latency: 12.87 ms
Org: Canonical Ltd.
Status: Up to date
Speed: 100 Mbps
Choose a mirror (1 - 2)
'q' to quit 1

使用数字键选择一个符合心意的源,使用如下命令替换生成好的 sources.list 并备份原来的:

sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup && \
sudo mv sources.list /etc/apt/

安装常用软件和 Web 环境

然后使用如下命令再次更新软件源并安装一些常用软件,最后更新所有软件包:

apt update
apt install build-essential wget curl vim coreutils git htop iotop nload screen dnsutils
apt upgrade

Oneinstack 和警告

参见 https://v2ex.com/t/979226
Oneinstack 于 2023 年 4-5 月、9 月下旬以后两度出现官网安装包包含恶意代码的问题,且网站已经出售,请不要使用官网的一键包
下面的安装包为 Dropbox 转存版本,在没有完全代码审计的情况下一定程度上降低了风险,请按步骤操作

使用 Oneinstack 一键包安装 LNMP 环境(Linux, Nginx, MySQL, PHP)。鉴于最近 LNMP.org 和 Oneinstack 出现含有恶意代码的问题,这里使用本人转存自 Dropbox 的一个据称没有污染的版本 CleanInStack.

首先编辑 /etc/hosts 屏蔽 Oneinstack 官方不可信域名:

0.0.0.0 mirrors.linuxeyes.com
0.0.0.0 mirrors.oneinstack.com

下载应该没问题的 Oneinstack 一键包并 md5 checksum:

wget https://github.com/hifocus/CleanInStack/releases/download/0.0.0/cleaninstack-full.tar.gz
md5sum cleaninstack-full.tar.gz

# md5 应为 b2947bac8cb66d54fcd90d30e406598f

tar -xzf cleaninstack-full.tar.gz
./oneinstack/install.sh

其实安装 Nginx 即可,喜欢的话可以把 PHP 和 mySQL 装全了。

更新防火墙规则

然后更新防火墙规则:允许 22(或者你选择的 ssh 口)、80、443 端口,禁止其他端口:

# 如果没安装
apt install ufw

ufw disable # 先禁用 ufw (Uncomplicated Firewall - iptables 的前端) 规则
ufw default deny # 默认阻拦所有规则

ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp

ufw enable # 应用更改和启用 ufw
ufw status verbose # 查看详细规则

禁止 IP 直接访问

到现在为止,如果我们直接访问服务器的 IP 会出现 Oneinstack 的界面,而访问 https://IP 在我们添加第一个 https 域名后也会显示该域名的证书。出于强迫症,让我们来禁止 80/443 端口对 IP 的直接访问。

首先自签一张 SSL 证书:

mkdir /usr/local/nginx/conf/ssl/
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/nginx/conf/ssl/nginx.key -out /usr/local/nginx/conf/ssl/nginx.crt
ls /usr/local/nginx/conf/ssl

注意此处 /usr/local/nginx/ 为你 Nginx 的安装路径。

然后编辑 /usr/local/nginx/conf/nginx.conf

; 在 80 端口的 server 块下添加 return 444;
server {
listen 80;
server_name _;
return 444;
}

; 新增一个监听 443 端口的 server 块:

server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /usr/local/nginx/conf/ssl/nginx.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/nginx.key;
return 444;
}

保存文件,运行 nginx -t 测试:

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

没问题的话就可以用 service nginx restart 重启 Nginx。现在通过 80/443 端口就无法直接访问服务器的 IP 了。

安装 BitTorrent 下载环境

使用 qBitTorrent-nox

qBitTorrent 和 qBitTorrent-nox 的区别在于后者带有 Web UI 而非原版的 GUI,在服务器环境上更方便操作

# 添加 ppa 源
add-apt-repository -y ppa:qbittorrent-team/qbittorrent-stable

# 更新软件源并安装 qBitTorrent-nox
apt update && apt install -y qbittorrent-nox

## 看看安装成功了没
qbittorrent-nox --version
# qBittorrent v4.3.9

添加守护进程

守护进程可以方便地管理软件的开关状态,也可以设置开机自启。

# 使用你喜欢的编辑器,这里使用 vim
vim /etc/systemd/system/qbittorrent-nox.service

# 文件里放以下内容
[Unit]
Description=qBittorrent client
After=network.target

[Service]
ExecStart=/usr/bin/qbittorrent-nox --webui-port=10086 # 你喜欢的端口
Restart=always

[Install]
WantedBy=multi-user.target

启用开机自启动:

sudo systemctl enable qbittorrent-nox
# Created symlink /etc/systemd/system/multi-user.target.wants/qbittorrent-nox.service → /etc/systemd/system/qbittorrent-nox.service.

现在你就可以通过:

  • service qbittorrent-nox start 启动 qBittorrent-nox
  • service qbittorrent-nox stop 停止 qBittorrent-nox
  • service qbittorrent-nox status 查看 qBittorrent-nox 的状态

qb-Nox 默认用户名:admin,默认密码:adminadmin

因为我们之前在防火墙禁止了相关端口,此时 qB 的 Web UI 是没办法通过 IP:port 访问的。

使用 Nginx 创建反代

我们用 Nginx 来创建一层反向代理方便我们日后使用。此处我们使用 Oneinstack 自带脚本来创建一个虚拟主机:

首先将任意域名/子域名指向服务器 IP.

cd ~

# 这里我把 acme.sh 的默认证书颁发机构改为了 Let's Encrypt,感觉 ZeroSSL 1. 不纯粹免费 2. 签发速度慢
./oneinstack/src/acme.sh-master/acme.sh --set-default-ca --server letsencrypt

# 创建 virtual host
./oneinstack/vhost.sh

按照提示选择选项,记住在开始选择 3 自动签发免费 SSL 证书,且确保域名 DNS 正确指向服务器。

# 成功后输出
Your domain: example.huangxin.dev
Virtualhost conf: /usr/local/nginx/conf/vhost/example.huangxin.dev.conf
Directory of: /data/wwwroot/example.huangxin.dev
Let's Encrypt SSL Certificate:/usr/local/nginx/conf/ssl/example.huangxin.dev.crt
SSL Private Key: /usr/local/nginx/conf/ssl/example.huangxin.dev.key

编辑 /usr/local/nginx/conf/vhost/example.huangxin.dev.conf:

# 删除以下内容

location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}

# 添加以下内容
location / {
proxy_pass http://0.0.0.0:10086/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

此时,访问 https://example.huangxin.dev 就能看到 qB 的 Web UI 了。

为 Web UI 加上密码

但是我们同样不想把我们的 qB 面板暴露在公网。在这里我们创建一层 Basic Auth 来给 qB 面板加上密码(或者用 qB 自带用户系统也行):

# 创建用户名(记得替换 {username})
sh -c "echo -n '{username}:' >> /usr/local/nginx/.htpasswd"

# 创建密码(输入两次密码确认)
sh -c "openssl passwd -apr1 >> /usr/local/nginx/.htpasswd"

在刚刚的 location / {} 块下添加:

auth_basic "Restricted"; # 验证提示
auth_basic_user_file /usr/local/nginx/.htpasswd;

照例 nginx -t 测试,service nginx restart 重启。现在访问 example.huangxin.dev 就需要输入用户名和密码了。

可以进入 qB 面板 - Tools(工具) - Options (选项) - Web UI,在 Authentication(验证) 一栏下把 Bypass authentication for clients on localhost (对本地主机上的客户端跳过身份验证) 勾选上,这样就无需输入两次密码。当然,也可以在上面的 Langauge 一栏下把语言改成中文。

然后我们在设置里再进入 BitTorrent 一栏,开启匿名模式以及在底部添加一些 tracker,可以从以下两个仓库里选:

挂载 Volume

接下来,我们需要把另外购买的 Volume 挂载到服务器上,参考这篇文章:https://www.idcoffer.com/archives/6453

# 查看磁盘列表
fdisk -l

# 选择要挂在的磁盘,这里是 /dev/sda
fdisk /dev/sda

# 分别选择
n # 创建新分区
p # 创建主分区
w # 保存更改

# 再次查看磁盘
fdisk -l

# 格式化成 EXT4 文件系统
mkfs.ext4 /dev/sda1

# 为分区创建一个文件夹
mkdir /disk2

# 挂载分区
mount /dev/sda1 /disk2

## 编辑 /etc/fstab 文件
vim /etc/fstab

# 加入下面一行 注意修改磁盘名称和挂载点
/dev/sda1 /disk2 ext4 defaults 0 0

# 重启服务器
reboot

# 看看是否成功自动挂载
df -h

现在,我们就可以进入 qB Web UI,把下载位置改成 /disk2 了。

启用目录列举

下载好了 BT,怎么拉回本地呢?有一个方法是直接通过 aria2c 的 Web RPC 推送到本地或者是路由器、NAS之类(见下一部分)。但是由于 aria2c 配置复杂和小白不友好,而且服务器在卢森堡,我觉得还是用 IDM 多线程下载比较好。

我们可以通过同一个域名,使用一个子目录,来实现目录列举。修改 /usr/local/nginx/conf/vhost/example.huangxin.dev.conf:

location /files {
# 依然开启 Basic Auth 验证
auth_basic "Restricted";
auth_basic_user_file /usr/local/nginx/.htpasswd;

# 将子目录的根目录指向下载文件夹
alias /disk2/downloads;

# 开启目录列举
autoindex on;
}

根据 ChatGPT 的针对大文件、相对慢的磁盘的推荐,我还在 location 块里加上了如下设置:

gzip off;

sendfile on;
output_buffers 1 1m;
tcp_nopush on;

open_file_cache max=100 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

这样,我们就可以通过 Nginx 自带的目录列举功能来直观地看到服务器上的文件了:

光看还不够,下载好的资源一个目录下十来个文件,如何全部获取他们的 URL 然后放到下载器里下载呢?虽然可以有很极客的解决办法,但是这里我们可以用比较原始的方法:

  1. 进入资源目录
  2. 右键查看源代码
  3. 复制所有 ` 标签
  4. Google 搜索 “href extraction” 工具,我随便找了个:https://en.toolpage.org/tool/href-extractor,提取所有相对链接,复制到 VS Code 内
  5. 回到资源目录,复制上方 URL,回到 VS Code,使用 Alt + Shift + 鼠标左键 选中所有 URL,然后 Ctrl + C 复制
  6. VS Code 内的就是所有文件的绝对 URL,放入 IDM(Task - Add Batch Download from Clipboard)即可开始下载

安装 AList

AList 是一个支持多个源的目录列举工具,包括本地、阿里云盘、Google Drive、各种对象存储等等

GitHub 页面:https://github.com/alist-org/alist

通过安装 AList,我们可以通过一个图形化的界面对服务器的文件目录直接进行操作(主要用来删除下载完的文件),免去了连接服务器和手打 rm -rf 命令的麻烦。另外,Alist 可以支持离线下载和使用 Aria2 RPC 推送下载。

AList 有一键脚本,但是只支持安装最新的版本。由于 v3.28.0 似乎魔改了后台,个人不太喜欢,这里手动安装 GitHub Release 里的 3.26.0 版本,参考 https://alist.nn.ci/zh/guide/install/manual.html

# 创建文件夹并进入
mkdir -p /data/apps/alist
cd /data/apps/alist

# 下载有 MUSL 静态链接的二进制文件并解压缩
wget https://github.com/alist-org/alist/releases/download/v3.26.0/alist-linux-musl-amd64.tar.gz
tar -xzf alist-linux-musl-amd64.tar.gz && rm alist-linux-musl-amd64.tar.gz

# 测试安装
./alist help

# 获取 admin 账户信息,并从输出里找到临时密码
./alist admin
# INFO[2023-10-06 21:19:47] Successfully created the admin user and the initial password is: xxxxxxxxxx

# 添加守护进程
vim /usr/lib/systemd/system/alist.service

# 输入以下内容
[Unit]
Description=alist
After=network.target

[Service]
Type=simple
WorkingDirectory=path_alist # 将 path_alist 改为 alist 的绝对路径,在这里是 /data/apps/alist
ExecStart=path_alist/alist server
Restart=on-failure

[Install]
WantedBy=multi-user.target

# 重载配置
systemctl daemon-reload

# 启用开机自启
systemctl enable alist

# 启用 alist
service alist start

# 查看状态,确保无报错
service alist status

子目录的反代设置

由于我们打算把 AList 放在子目录里,参考这篇文档,我们首先要进入 AList 同目录下的 /data 文件夹,修改 config.json

vim /data/apps/alist/data/config.json

# 将 "site_url" 改为 `/cloud` 或者你喜欢的子目录名
# "site_url": "/cloud",

在 Nginx 虚拟主机配置文件内新增:

location /cloud/ {
auth_basic off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://0.0.0.0:5244/cloud/; # 注意 proxy_pass 必须带子目录路径和最后的斜杠
}

nginx -t 测试,service nginx restart 重启;此时,就能通过 https://example.huangxin.dev/cloud 访问 AList 实例。

访问 AList 实例,使用上面的 admin 账号密码登陆,修改密码,进入存储 - 添加 - 本地存储,设置挂载路径为 / 或者 /downloads,设置根目录为 /disk2/downloads(或者你对应的路径),就可以通过 AList 访问下载好的文件。

也可以在右边的工具栏设置本地 aria2 RPC,这样就可以直接推送下载了。

使用中转服务器加速下载

由于国内国际网络的连通性不加,加上服务器本身并没有大陆优化,如果你碰巧有对大陆优化且带宽相对充裕的服务器,除了使用代理下载外,还可以用中转服务器上的 Nginx 进行又一层的反代。

  1. 在中转服务器上安装 Nginx,参考上面的教程
  2. 新建一个子域名,例如 proxy-download.huangxin.dev,并将其指向中转服务器 IP
  3. 在中转服务器上建立一个虚拟主机
  4. 在 Nginx 虚拟主机配置文件里应用如下配置:
        location / {
    # 可以另外加一层 Basic Auth
    auth_basic "Restricted Content";
    auth_basic_user_file /usr/local/nginx/.htpasswd;

    # 反代地址,记得尾端的斜杠
    proxy_pass https://example.huangxin.dev/files/;

    # Set the proxy headers so the original host and IP are known
    proxy_set_header Host example.huangxin.dev;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # Basic HTTP Authentication
    # 在请求头内包含被代理服务器的 Basic Auth 用户名和密码
    proxy_set_header Authorization "Basic dXNlcjpwYXNzd29yZA=="; ## user:password 的 base64 编码

    # Enable support for resumable downloads
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 4 32k;
    }
  5. 现在,请求 https://proxy-download.huangxin.dev 就会被反代到 https://example.huangxin.dev/files/,并且会自动带上 Basic Auth 的用户名和密码

成果速览

使用 BuyVM 1C1G 20GB @ 10Gbps 的服务器可以做到 BT 跑满硬盘速度下载(约 35MB/s),通过中转服务器取回能跑到本地软件限制的限制带宽上限(约 7800KB/s):