在 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: '3'

services:
warp:
image: caomingjun/warp
container_name: warp
restart: always
ports:
- '1080:1080'
environment:
- WARP_SLEEP=2
# - WARP_LICENSE_KEY= # optional
cap_add:
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv4.conf.all.src_valid_mark=1
volumes:
- ./data:/var/lib/cloudflare-warp

试一试它是否工作:

1
curl --socks5 127.0.0.1:1080 https://cloudflare.com/cdn-cgi/trace

如果输出中包含 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
2
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \
CMD curl -fsS "https://cloudflare.com/cdn-cgi/trace" | grep -qE "warp=(plus|on)" || exit 1

如果你不希望容器自动重启,可以删除 docker-compose.yml 中的 restart: always 。你也可以通过 docker-compose.yml 修改健康检查的参数。

工作原理

下面介绍了容器的工作原理,供希望改进本容器或者构建类似容器的开发人员参考。如果你只是想使用容器,只需要阅读上面的部分即可

该镜像包括两个组件,Cloudflare WARPGOST。WARP 客户端用于连接 WARP 服务,GOST 用于提供代理。其灵感最初来自于 hostloc 的一篇帖子。这篇帖子是对于在容器中运行 WARP 的(我能找到的)最早尝试,提供了很多有用的信息,但是有以下缺陷:

  • 仍然使用了第三方客户端 WGCF,可能会被 Cloudflare 封禁
  • 需要进行交互式操作,不便于部署

以其为基础,我进行了一系列的修改,代码托管在 GitHub

WARP 官方客户端

下载和安装 WARP 官方客户端的过程在 WARP 文档中已经有很好的叙述了,这里不再赘述。关键在于如何将其在容器环境下运行起来。这些工作包括三部分:

  • 为其创建 TUN 设备。容器中默认不存在 TUN 设备。
  • 启动 WARP 守护进程。正常安装时,WARP 会将守护进程的相关配置写入 systemd 或者其类似物中,以在开机时自动启动。在容器中我们需要手动启动它。
  • 给予足够的权限。

前两个问题在 entrypoint.sh 中解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# create a tun device
mkdir -p /dev/net
mknod /dev/net/tun c 10 200
chmod 600 /dev/net/tun

# start the daemon
warp-svc &

# sleep to wait for the daemon to start, default 2 seconds
sleep "$WARP_SLEEP"

# if /var/lib/cloudflare-warp/reg.json not exists, register the warp client
if [ ! -f /var/lib/cloudflare-warp/reg.json ]; then
warp-cli register && echo "Warp client registered!"
# if a license key is provided, register the license
if [ -n "$WARP_LICENSE_KEY" ]; then
echo "License key found, registering license..."
warp-cli set-license "$WARP_LICENSE_KEY" && echo "Warp license registered!"
fi
# connect to the warp server
warp-cli connect
else
echo "Warp client already registered, skip registration"
fi

需要注意的是

  • TUN 设备的创建不能放在 Dockerfile 中,因为 /dev 被挂载为 tmpfs,并不在文件系统 / 中。
  • 我们需要等待守护进程启动后才能使用 warp-cli 命令。目前采用的是等待提前配置好的一段时间,如果各位有更好的方法,欢迎提出。

权限问题在 docker-compose.yml 中解决:

1
2
3
4
5
cap_add:
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv4.conf.all.src_valid_mark=1

这赋予了容器足够但又不过多的权限。

夹带一点私货,我对于要求 --privileged=true 的容器持有强烈的抵触情绪,因为这意味着容器获得了宿主机的完整 root 权限,这是非常危险的。即使我们选择相信开发者,容器内的服务也有可能存在安全漏洞,一旦攻击者入侵了容器就能直接获得宿主机的全部控制权。通常情况下,容器并不需要如此高的权限,这样的要求往往是开发者懒得确认所需权限的偷懒行为,却严重危害了用户的安全。

GOST

GOST 是一个非常强大的代理软件,在本容器中我使用了它来提供代理服务,并且通过 GOST_ARGS 环境变量来实现了对于代理类型的配置。

健康检查

Docker 官方并不推荐在一个容器中运行多个进程,原因之一是子进程的崩溃可能无法被检测到。然而我通过一个巧妙的方式解决了这个问题。容器中运行了 WARP 守护进程和 GOST 代理,如果 GOST 代理崩溃,它将导致主进程(entrypoint.sh)退出,从而触发自动重启;如果 WARP 守护进程崩溃,它将导致健康检查失败,从而触发自动重启。这样就实现了对于子进程崩溃的检测。

References

  1. Hostloc 论坛 - 灵感来源
  2. StackOverFlow - How to create tun interface inside Docker container image?
  3. Cloudflare WARP docs
  4. GOST 文档
  5. Docker docs - Run multiple services in a container

讨论

本文章未启用评论功能,请在 GitHub 存储库中提交问题。

作者

Cao Mingjun

发布于

2023-07-17

更新于

2023-07-17

许可协议