在 Docker 中运行 Cloudflare WARP
Cloudflare WARP 是 Cloudflare 提供的免费 VPN 服务,由于多数服务商都将其出口 IP 视作信誉良好的家宽 IP,许多人将其用于 IP 地址较脏的服务器,以便访问风控较严格的网站。然而当我们将它在自己的服务器上使用时,会遇到以下的问题:
- 官方 WARP 客户端在默认模式
1.1.1.1 with WARP
下会阻断所有入站连接,这意味着服务器上的网站和服务都无法被访问 - 官方 WARP 客户端在
Local Proxy
模式下尽管没有阻断入站连接的问题,其提供的 HTTPS/SOCKS5 代理并不能传输 UDP 数据包 - 为了防止滥用,Cloudflare 在部分地区阻止了第三方客户端 (wgcf 等) 访问 WARP 服务,暂不清楚它是否会扩大该措施的范围
本文将在 Docker 中运行官方 WARP 客户端,以解决上述问题。
简短的使用说明
启动容器
要在 Docker 中运行 WARP 客户端,只需将以下内容写入 docker-compose.yml
,然后运行 docker-compose up -d
即可。
1 |
|
试一试它是否工作:
1 |
|
如果输出中包含 warp=on
或者 warp=plus
,容器已经正常工作。如果输出中包含 warp=off
,则说明容器未能连接到 WARP 服务。
配置
你可以通过以下环境变量来配置容器:
WARP_SLEEP
:等待 WARP 守护进程启动的时间,单位为秒,默认为 2 秒。如果时间过短,可能会导致 WARP 守护进程未启动就开始使用代理,从而导致代理无法正常工作。如果时间过长,可能会导致容器启动时间过长。如果你的服务器性能较差,可以适当增加该值。WARP_LICENSE_KEY
:WARP 客户端的许可证密钥,可选。如果你订阅了 WARP+ 服务,可以将密钥填入该环境变量中。如果你没有订阅 WARP+ 服务,可以忽略该环境变量。
数据持久化:使用主机卷 ./data
持久化 WARP 客户端的数据。你可以更改该目录的位置,或者使用其他类型的卷。如果修改了 WARP_LICENSE_KEY
,请删除 ./data
目录以便客户端重新进行注册。
更改代理类型
容器使用 GOST 来提供代理,环境变量 GOST_ARGS
是传递给 GOST 的参数。默认为 -L :1080
,即在容器内以 HTTP 和 SOCKS5 协议同时监听 1080 端口。如果你希望获得 UDP 支持或者使用其他协议提供的高级功能,可以修改该参数。具体参考 GOST 文档。
如果你修改了端口号,你可能需要同时修改 docker-compose.yml
中的端口映射。
容器的健康检查
容器的健康检查会检查容器内的 WARP 客户端是否正常工作。如果检查失败,容器将会自动重启。具体地,在启动 30 秒后,每隔 15 秒检查一次,如果连续 3 次检查失败,容器将会被标记为不健康并触发自动重启。
1 |
|
如果你不希望容器自动重启,可以删除 docker-compose.yml
中的 restart: always
。你也可以通过 docker-compose.yml
修改健康检查的参数。
工作原理
该镜像包括两个组件,Cloudflare WARP 和 GOST。WARP 客户端用于连接 WARP 服务,GOST 用于提供代理。其灵感最初来自于 hostloc 的一篇帖子。这篇帖子是对于在容器中运行 WARP 的(我能找到的)最早尝试,提供了很多有用的信息,但是有以下缺陷:
- 仍然使用了第三方客户端 WGCF,可能会被 Cloudflare 封禁
- 需要进行交互式操作,不便于部署
以其为基础,我进行了一系列的修改,代码托管在 GitHub
WARP 官方客户端
下载和安装 WARP 官方客户端的过程在 WARP 文档中已经有很好的叙述了,这里不再赘述。关键在于如何将其在容器环境下运行起来。这些工作包括三部分:
- 为其创建 TUN 设备。容器中默认不存在 TUN 设备。
- 启动 WARP 守护进程。正常安装时,WARP 会将守护进程的相关配置写入
systemd
或者其类似物中,以在开机时自动启动。在容器中我们需要手动启动它。 - 给予足够的权限。
前两个问题在 entrypoint.sh
中解决:
1 |
|
需要注意的是
- TUN 设备的创建不能放在 Dockerfile 中,因为
/dev
被挂载为tmpfs
,并不在文件系统/
中。 - 我们需要等待守护进程启动后才能使用
warp-cli
命令。目前采用的是等待提前配置好的一段时间,如果各位有更好的方法,欢迎提出。
权限问题在 docker-compose.yml
中解决:
1 |
|
这赋予了容器足够但又不过多的权限。
夹带一点私货,我对于要求 --privileged=true
的容器持有强烈的抵触情绪,因为这意味着容器获得了宿主机的完整 root 权限,这是非常危险的。即使我们选择相信开发者,容器内的服务也有可能存在安全漏洞,一旦攻击者入侵了容器就能直接获得宿主机的全部控制权。通常情况下,容器并不需要如此高的权限,这样的要求往往是开发者懒得确认所需权限的偷懒行为,却严重危害了用户的安全。
GOST
GOST 是一个非常强大的代理软件,在本容器中我使用了它来提供代理服务,并且通过 GOST_ARGS
环境变量来实现了对于代理类型的配置。
健康检查
Docker 官方并不推荐在一个容器中运行多个进程,原因之一是子进程的崩溃可能无法被检测到。然而我通过一个巧妙的方式解决了这个问题。容器中运行了 WARP 守护进程和 GOST 代理,如果 GOST 代理崩溃,它将导致主进程(entrypoint.sh
)退出,从而触发自动重启;如果 WARP 守护进程崩溃,它将导致健康检查失败,从而触发自动重启。这样就实现了对于子进程崩溃的检测。
References
- Hostloc 论坛 - 灵感来源
- StackOverFlow - How to create tun interface inside Docker container image?
- Cloudflare WARP docs
- GOST 文档
- Docker docs - Run multiple services in a container
讨论
本文章未启用评论功能,请在 GitHub 存储库中提交问题。
在 Docker 中运行 Cloudflare WARP