Run Cloudflare WARP in Docker
Cloudflare WARP is a free VPN service provided by Cloudflare. As most service providers consider its exit IP as a reputable residential broadband IP, many people use it for accessing websites that have strict risk control policies, especially when their server’s IP address is not clean. However, when we use it on our own servers, we may encounter the following issues:
- The official WARP client, in the default mode
1.1.1.1 with WARP
, blocks all inbound connections, which means that websites and services on servers cannot be accessed. - Although the official WARP client in
Local Proxy
mode does not have the problem of blocking inbound connections, the HTTPS/SOCKS5 proxy it provides cannot transmit UDP packets. - In order to prevent abuse, Cloudflare blocks third-party clients (such as wgcf) from accessing WARP services in some regions, and it is currently unknown whether this measure will be extended to other regions.
This article will run the official WARP client in Docker to solve the above problem. The project is published on GitHub.
TL;DR
Start the container
To run the WARP client in Docker, just write the following content to docker-compose.yml
and run docker-compose up -d
.
1 |
|
Try it out to see if it works:
1 |
|
If the output contains warp=on
or warp=plus
, the container is working properly. If the output contains warp=off
, it means that the container failed to connect to the WARP service.
Configuration
You can configure the container through the following environment variables:
WARP_SLEEP
: The time to wait for the WARP daemon to start, in seconds. The default is 2 seconds. If the time is too short, it may cause the WARP daemon to not start before using the proxy, resulting in the proxy not working properly. If the time is too long, it may cause the container to take too long to start. If your server has poor performance, you can increase this value appropriately.WARP_LICENSE_KEY
: The license key of the WARP client, which is optional. If you have subscribed to WARP+ service, you can fill in the key in this environment variable. If you have not subscribed to WARP+ service, you can ignore this environment variable.
Data persistence: Use the host volume ./data
to persist the data of the WARP client. You can change the location of this directory or use other types of volumes. If you modify the WARP_LICENSE_KEY
, please delete the ./data
directory so that the client can detect and register again.
Change proxy type
The container uses GOST to provide proxy, where the environment variable GOST_ARGS
is used to pass parameters to GOST. The default is -L :1080
, that is, to listen on port 1080 in the container at the same time through HTTP and SOCKS5 protocols. If you want to have UDP support or use advanced features provided by other protocols, you can modify this parameter. For more information, refer to GOST documentation.
If you modify the port number, you may also need to modify the port mapping in the docker-compose.yml
.
Health check
The health check of the container will verify if the WARP client inside the container is working properly. If the check fails, the container will automatically restart. Specifically, 15 seconds after starting, a check will be performed every 15 seconds. If the inspection fails for 3 consecutive times, the container will be marked as unhealthy and trigger an automatic restart.
1 |
|
If you don't want the container to restart automatically, you can remove restart: always
from the docker-compose.yml
. You can also modify the parameters of the health check through the docker-compose.yml
.
How it works?
This image includes two components, Cloudflare WARP and GOST. The WARP client is used to connect to the WARP service, and GOST is used to provide a proxy. Its inspiration initially came from a post on hostloc (in chinese). This post was the earliest attempt (that I could find) to run WARP in a container, and provided a lot of useful information, but had the following shortcomings:
- Still using the third-party client WGCF, which may result in being banned by Cloudflare.
- Requires interactive operations, which is inconvenient for deployment.
Based on this, I have made a series of modifications and uploaded the code to GitHub.
Official WARP client
The process of downloading and installing the official WARP client is well described in the WARP documentation. Here, we focus on how to run it in a containerized environment, which involves three parts:
- Creating a TUN device, which is not available by default in a container.
- Starting the WARP daemon. Normally, the WARP installation process writes the relevant configurations for the daemon into
systemd
or a similar tool, which allows it to start automatically at boot time. However, in a container environment, we need to start it manually. - Granting sufficient permissions.
The first two issues are addressed in entrypoint.sh
:
1 |
|
It should be noted that:
- The creation of TUN device cannot be placed in the Dockerfile because
/dev
is mounted astmpfs
, which is not in the file system/
. - We need to wait for the daemon to start before using the
warp-cli
command. Currently, we use a pre-configured wait time. If you have a better method, please feel free to suggest it.
The permission issue can be resolved in the docker-compose.yml
:
1 |
|
This gives containers enough, but not excessive, privileges.
I have a strong aversion to containers that require --privileged=true
, because that means the container gains complete root access to the host machine, which is extremely dangerous. Even if we trust the developers, there may still be security vulnerabilities in the services running inside the container, and once an attacker gains access to the container, they can directly obtain full control over the host machine. In most cases, containers do not need such high privileges, and this requirement is often a lazy behavior of developers who do not want to confirm the necessary permissions, but seriously endangers users' security.
GOST
GOST is a very powerful proxy software. In this container, I use it to provide proxy services and configure the proxy type through the GOST_ARGS
environment variable.
Health check
Docker officially does not recommend running multiple processes in one container, partly because the crash of a subprocess may not be detected. However, in this particular container, I avoided this issue. The container runs the WARP daemon process and GOST proxy. If the GOST proxy crashes, it will cause the main process (entrypoint.sh
) to exit, triggering an automatic restart. If the WARP daemon process crashes, it will cause the health check to fail, also triggering an automatic restart. This achieves detection of subprocess crashes.
Automatic Fix for Host Connectivity
In July 2024, GitHub user @ostrolucky reported that the host might be unable to connect to the container due to traffic being intercepted by Cloudflare WARP. Upon investigation, I found that when connecting through Cloudflare Zero Trust, organization's configured split tunnel may not include the subnet where the Docker network resides. This results in traffic being routed to WARP's network interface, preventing packets sent from the container from reaching the host. Specifically, Cloudflare WARP create a routing rule (you can find it by ip rule list
) to move all packets not matching a specific fwmask
to a route table it created, and intercepts all packets not belonging to the split tunnel and not handled by the WARP interface using nftables.
To automatically resolve this issue, I added a script in the container's healthcheck, automatically adding necessary rules to nftables and routing rule list to ensure that traffic from containers can reach the host properly. Users can enable this feature by setting the environment variable BETA_FIX_HOST_CONNECTIVITY=1
.
References
- Hostloc forum - inspiration (in chinese)
- StackOverFlow - How to create tun interface inside Docker container image?
- Cloudflare WARP docs
- GOST docs
- Docker docs - Run multiple services in a container
Discussion
This article does not have comments enabled. Please submit an issue in the Github repository.
Run Cloudflare WARP in Docker
https://blog.caomingjun.com/run-cloudflare-warp-in-docker/en/