Install Cloudflare WARP on any Linux Distro, Thanks to Distrobox!
Archer Allstars
Posted on April 15, 2024
Update 05/19/24: Using a normal container instead of an
init
container.
Currently, Cloudflare WARP can be installed on Ubuntu, Debian, RHEL, and CentOS. See? Not many Linux distros are supported.
Cloudflare WARP is a very popular free VPN. It's fast since it operates on Cloudflareโs global network through WireGuard connection. There's no limit to bandwidth usage. But it can't spoof your location, and it will not work very well with torrenting, since it doesn't support port-forwarding.
However, we can set up WARP in a container, a Distrobox container to be specific, then proxy the host's connection to the container through SOCKS5 port. This method would benefit unsupported system like Arch and openSUSE, for example. Also, immutable OSes like Fedora Silverblue and openSUSE MicroOS would be able to enjoy WARP connection easily.
Without further ado, let's see how to set this up.
๐๏ธ Table of contents:
- Install Distrobox
- Create a Distrobox Container
- Install Cloudflare WARP client in the Container
- Enable WARP Service
- Register the Client
- Turn On Malware Filtering (optional)
- Enable Proxy Mode and Config Proxy Port
- Connect to WARP
- Export the Client (
warp-svc
) - Reroute Our Host's Connection
- Verify WARP Connection
- Automatically Connect to WARP on System Startup + Kill Switch
- Containers Maintenance
1. Install Distrobox
Distrobox is a container manager designed to integrate tightly with the host. Compared to Docker and Podman, it's a lot easier to use, as most complicated things are set up OOTB. Nevertheless, it still uses Podman or Docker (not recommended) behind the scene.
To install Distrobox on openSUSE Tumbleweed, for example:
sudo zypper install distrobox
On a very old point-release distro, current Ubuntu LTS for example, you might want to install the latest version of Distrobox using Homebrew:
brew install distrobox
.
On Tumbleweed, this will pull in Docker automatically.
However, I prefer Podman as Docker Desktop is not supported on my system (openSUSE). ๐ค But more importantly, Podman runs rootless by default, while Docker doesn't. In fact, Docker has rootless mode. However, it doesn't work as well, see Distrobox issue #223. To lump, if you want to use Distrobox as an apps' compatibility layer for your OS, Podman is the way to go. It works great in both rootless and rootful modes.
Therefore, on openSUSE Tumbleweed, for example, I install Podman with sudo zypper install podman
, then in ~/.config/distrobox/distrobox.conf
, I tell Distrobox to use Podman instead:
container_manager="podman"
If you're on Ubuntu, the only way to get the latest Podman is to build from source. Official Ubuntu support from upstream is not going to happen in the foreseeable future. There's a request for Podman as a Snap package on Snapcraft forum, though. Please consider voting in that thread, so Canonical might release a snapped Podman one day. ๐ฃ๏ธ
2. Create a Distrobox Container
First, we need to decide which OS image we'll use for a container. See a list of supported containers here. I recommend the official Ubuntu 22.04 image from Docker Hub, as Cloudflare WARP client doesn't support Ubuntu 24.04 yet. So, I create a container with:
distrobox create -i docker.io/library/ubuntu:22.04 -n cfw-dbx -H ~/distrobox/cfw-dbx --unshare-netns --volume /run/dbus/system_bus_socket:/run/dbus/system_bus_socket --additional-flags "-p 127.0.0.1:1080:1080"
-
distrobox create
is used to create a Distrobox container. See all options of this command here. -
-i
is used to specify the image we want to use for the container. -
-n
is used to specify the container name. In this case, I usecfw-dbx
. -
-H
is used to separate the container$HOME
from the host (I don't want the container's config files, which could be temporary, to be messed up with my system configs). In this case, I specify my host's~/distrobox/cfw-dbx
to be my container's$HOME
. All the container's configs will be in this folder. -
--unshare-netns
is used to separate the container internet from the host. So, the container's internet configurations won't conflict with the host. -
--volume /run/dbus/system_bus_socket:/run/dbus/system_bus_socket
is used to share DBus system daemon with the host. It's usually needed if the app inside the container complained aboutFailed to connect to the bus: Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory
. -
--additional-flags "-p 127.0.0.1:1080:1080"
is used to add Docker/Podman flags that are not available in Distrobox. In this case, it's necessary to map proxy's SOCKS5 port (1080) on the host to the same port inside the container (we will config WARP's proxy to use this port later).
After the creation process, enter the container by:
distrobox enter cfw-dbx
You can simply exit from the container by... using
exit
command inside the container ๐ However, the container will still be running in the background. To stop the container:distrobox stop <container name>
.
3. Install Cloudflare WARP client in the Container
You can refer to the official WARP client installation instruction here.
To complete the instruction without any issue, we need to install these packages:
sudo apt install curl lsb-release
4. Enable WARP Service
Since we do not use an init
container (a container with separated systemd
and user session), we need to enable WARP service manually. Otherwise, we won't be able to use warp-cli
to make any connection at all:
sudo warp-svc
Do not close this terminal window. You can let it run.
5. Register the Client
Open a new terminal window, then run:
distrobox enter cfw-dbx
Or we can simply click on the container's icon, which Distrobox automatically created for us when we created the container:
Run this command in the container to register the client:
warp-cli registration new
6. Turn On Malware Filtering (optional)
This is completely optional, but it's good to know that WARP use 1.1.1.1 as its DNS resolver by default. What I always use is 1.1.1.2, which is the same as 1.1.1.1 + malware filter at DNS level. So, why not? ๐
warp-cli dns families malware
7. Enable Proxy Mode and Config Proxy Port
Since we want to reroute all the host's internet connections to this container's WARP tunnel, we need to enable the proxy mode and config the proxy port (SOCKS5 - port 1080).
warp-cli mode proxy
warp-cli proxy port 1080
8. Connect to WARP
warp-cli connect
9. Export the Client (warp-svc
)
distobox-export -b /bin/warp-svc --sudo
Note,
warp-svc
needs to be run as root (inside the container, which is still rootless on the host level), so I specify--sudo
when exporting.If you want, you can create a desktop shortcut to enable the connection. It's exported to
~/.local/bin
. For the icon, you can grab it from Play Store. ๐
After this, we're free to leave the container. It's a one-time setup. We don't have to enter this container ever again. Exit the container by running exit
command.
exit
10. Reroute Our Host's Connection
Find your host's proxy settings. For example, in GNOME, change your proxy to manual mode, then put in localhost IP (127.0.0.1) and SOCKS5 port (1080) as shown in the screenshots below:
11. Verify WARP Connection
According to this Cloudflare blog post, we can check WARP connection on the host using their trace URL (in our case, from the host's terminal):
curl https://www.cloudflare.com/cdn-cgi/trace/ | grep warp
If you've set up WARP successfully, this should return:
warp=on
You can check your current IP and DNS resolvers that should now change to Cloudflare on dnscheck.tools.
12. Automatically Connect to WARP on System Startup + Kill Switch
Normally, you can connect to WARP in the container just by running the exported bin, ~/.local/bin/warp-svc
, from step #9, since we already set everything up inside the container. And to disconnect from WARP, we can either stop the container or closing the host's proxy.
However, if we want to connect to WARP automatically on boot, it's just as easy. All we need to do is using a user's systemd
service and a timer.
Note, for a rootless Podman container to statart successfully on boot, you'll have to use a user service (
--user
), as you can't start a rootless Podman container using a system service. This is the expected behavior, see Podman's troubleshooting #31, Podman issue #12778 and #19740.
12.1. The Service File
[Unit]
Description=Start cfw-dbx container for Cloudflare WARP connection.
RequiresMountsFor=/run/user/1000/containers
[Service]
Type=exec
ExecStart=/home/archerallstars/.local/bin/warp-svc
ExecStop=-bash -c "distrobox stop cfw-dbx"
RemainAfterExit=yes
- Replace
archerallstars
with your username. - Save this service file as
<container-name>.service
. For example, in my case, it'scfw-dbx.service
. Then, put the file in~/.config/systemd/user
.
12.2. The Timer File
[Unit]
Description=Start Cloudflare WARP connection with some delays.
[Timer]
OnStartupSec=10
RandomizedDelaySec=5
[Install]
WantedBy=timers.target
- The filename for both the
.service
and its.timer
must be the same. Therefore, save this file as~/.config/systemd/user/cfw-dbx.timer
. - I use some delays, as starting the container too soon without delay could crash the entire system, of which hard reboot would be required! It happens to me many times. And it seems this is not a specific Podman issue, as this also happens with Docker, as documented on this ArchWiki page.
12.3. Reload and Enable the Timer
systemctl --user daemon-reload && systemctl --user enable cfw-dbx.timer
Now, our container along with WARP connection will automatically start after we logged in to our user session.
You can see more about systemd
on the official man page, epecially this systemd.exec
man page.
Note,
RequiresMountsFor=/run/user/1000/containers
directive seems to be necessary for a rootless Podman container, see Podman issue #7330. Even though the path should be the same for everyone (it's the default path), you can check yours by running:podman info | grep runRoot
.For a rootfull Podman container, it's
RequiresMountsFor=%t/containers
.
In order to start a user service on boot, not after the login, we need to enable the user lingering:
sudo loginctl enable-linger archerallstars
Replace archerallstars
with your username. After this, your service will be started on boot with the system instead of after the user's login.
12.4. Kill Switch
The kill switch part... If we don't turn off the system proxy, we can't connect to the internet at all without the container running. ๐ฎ
In other word, we can flip-flop between WARP connection and our normal internet connection using our system's proxy switch.
13. Containers Maintenance
To make our setup more robust, it has to be able to upgrade itself. To update all Distrobox containers (the containers' OSes, drivers, packages, apps, etc.), we can simply run distrobox-upgrade --all
.
If you want the system to automatically upgrade all the containers everyday transparently in the background, you can use this systemd
service file along with its timer running as --user
:
dbx-upgrade.service
[Unit]
Description=Upgrade all rootless Distrobox containers.
RequiresMountsFor=/run/user/1000/containers
[Service]
Type=exec
ExecStart=-bash -c "distrobox-upgrade --all"
Restart=on-failure
RestartSec=60
TimeoutStopSec=5min
RemainAfterExit=yes
Save this file as ~/.config/systemd/user/dbx-upgrade.service
.
dbx-upgrade.timer
[Unit]
Description=Run distrobox-upgrade --all daily.
[Timer]
OnCalendar=daily
RandomizedDelaySec=5min
Persistent=true
[Install]
WantedBy=timers.target
Save this file as ~/.config/systemd/user/dbx-upgrade.timer
.
Then reload user's services and enable it with:
systemctl --user daemon-reload && systemctl --user enable dbx-upgrade.timer
The Reliability of the Proxy Connection on Linux
While we can leverage the container approach to deal with the limited platform availability of the client, it comes with another form of limitation due to unreliable support of the proxy connection on Linux.
To lump, system apps and Snap apps mostly use the proxy just fine. There are only a few exceptions, for example, curl
respects proxy by default, while wget
does not.
The issue becomes complicated with apps in containers. While it's true that Distrobox created a container with --network host
by default. Therefore, your system's proxy settings should be applied to apps in all Distrobox containers without issue, right? You can check your connection with curl --silent https://ipecho.net/plain ; echo
, this should return Cloudflare IP. However, I checked against Brave Browser in the container. It doesn't use the system's proxy connection by default, which is contrary to when it's run outside the container. This might be related to the browser's proxy settings detection method that doesn't work well with containerized environment. Nevertheless, you can use Proxy SwitchyOmega extension to manually config the browser's proxy connection, and the browser inside the container will be able to use the host's proxy just fine. It means that your system's proxy settings can be accessed in the container, but depending on how apps detect the system's proxy settings, the result could be varied.
Lastly, Flatpak. Apps in Flatpak don't respect system proxy settings by default, see Flatpak's xdg-desktop-portal issue #554. However, this also depends on how the app specifically handles proxy connection in Flatpak. For example, Firefox Flatpak version totally ignores system's proxy settings, while Brave Flatpak respects the system proxy just like the native version.
I hope proxy settings on Linux will improve overtime in the future. As things are today, the leak can happen even with the system's apps/packages. IMO, the system's proxy settings should be forced on every app, just like how WireGuard works.
Alternatives
Why do I recommend you all to set this up instead of all other alternatives out there?
Well, this method use the official Ubuntu image as a base for our container. It also uses the official WARP client from Cloudflare. So, you are not compromising your system security by running random images or scripts.
Therefore, I won't recommend the alternatives.
I hope this helps. If you like this article, please let me know in the comment section below. If you don't, feel free to tell me why. Thanks for reading, bye ๐จ
Cover Photo by Pawel Czerwinski on Unsplash
The Man is on the Call Photo by YUNAN WANG on Unsplash
Posted on April 15, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.