DEV Community

Cover image for Install Cloudflare WARP on any Linux Distro, Thanks to Distrobox!
Archer Allstars
Archer Allstars

Posted on • Updated on

Install Cloudflare WARP on any Linux Distro, Thanks to Distrobox!

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. ๐Ÿ‘‰๏ธ


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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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 latest version of the official Ubuntu image from Docker Hub. So, I create a container with:

distrobox create -i docker.io/library/ubuntu:latest -n cfw-dbx -H ~/distrobox/cfw-dbx --unshare-netns --additional-packages "systemd libpam-systemd" -I --additional-flags "-p 127.0.0.1:1080:1080"
Enter fullscreen mode Exit fullscreen mode
  • 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 use cfw-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.
  • --additional-packages "systemd libpam-systemd" is used to add required packages for systemd services in the container.
  • -I is used to tell Distrobox to create an init container. Basically, this allows the container to have its own systemd services separated from the host, which is required by the WARP client.
  • --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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

4. Enable WARP Service

First, we need to enable WARP service. Otherwise, we won't be able to use warp-cli to make any connection at all:

sudo systemctl enable --now warp-svc.service
Enter fullscreen mode Exit fullscreen mode

5. Register the Client

warp-cli registration new
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

8. Connect to WARP

warp-cli connect
Enter fullscreen mode Exit fullscreen mode

After that, 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
Enter fullscreen mode Exit fullscreen mode

9. 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:

GNOME's proxy settings #1

GNOME's proxy settings #2


10. Verify WARP Connection

According to this Cloudflare blog post, we can check WARP connection using their trace URL (in our case, from the host's terminal):

curl https://www.cloudflare.com/cdn-cgi/trace/ | grep warp
Enter fullscreen mode Exit fullscreen mode

If you've set up WARP successfully, this should return:

warp=on
Enter fullscreen mode Exit fullscreen mode

You can check your current IP and DNS resolvers that should now change to Cloudflare on dnscheck.tools.


11. Automatically Connect to WARP on System Startup + Kill Switch

Normally, you can connect to WARP in the container just by entering the container, since we already enable WARP service 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.

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.

After we're done setting up the container using Distrobox. We can also enter the container using native podman command. I find using the native container command is more reliable on boot. Therefore, we will use it in our service file.

11.1. The Service File

[Unit]
Description=Start cfw-dbx container for Cloudflare WARP connection.
RequiresMountsFor=/run/user/1000/containers

[Service]
Type=exec
ExecStartPre=-bash -c "until systemctl is-active network-online.target; do sleep 1; done"
ExecStart=-bash -c "podman start cfw-dbx"
ExecStop=-bash -c "podman stop cfw-dbx"
Restart=on-failure
RestartSec=2
TimeoutStopSec=60
RemainAfterExit=yes

[Install]
WantedBy=default.target
Enter fullscreen mode Exit fullscreen mode

Save this service file as <container-name>.service. For example, in my case, it's cfw-dbx.service. Then, put the file in ~/.config/systemd/user.

Reload the user service with:

systemctl --user daemon-reload
Enter fullscreen mode Exit fullscreen mode

Enable the service with:

systemctl --user enable cfw-dbx.service
Enter fullscreen mode Exit fullscreen mode

Now, our container along with WARP connection will automatically start after we logged in to our user session with the help of WantedBy=default.target in the [Install] section.

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 Podman regardless of the container priviledge, 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.

However

I do not recommend running a container too soon after the login without a timer, as it could crash the container (the container died issue), resulting in system crash and the system reboot would be required.

In order to start a user service on boot, not after the login, we need to enable the user lingering:

sudo loginctl enable-linger <your-username>
Enter fullscreen mode Exit fullscreen mode

Replace <your-username> with your username, for example, mine is archerallstars. After this, your service will be started on boot with the system instead of after the user's login.

11.2. 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.


12. 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 transparently in the background, you can use this user's systemd service file:

[Unit]
Description=Upgrade all rootless Distrobox containers.

[Service]
Type=oneshot
TimeoutStartSec=infinity
# Wait for 15 mins before running the upgrade.
ExecStartPre=-bash -c "sleep 900"
ExecStart=-bash -c "distrobox-upgrade --all"
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target
Enter fullscreen mode Exit fullscreen mode

You can save this file as dbx-upgrade.service in ~/.config/systemd/user. Then reload user's services and enable it with:

systemctl --user daemon-reload
systemctl --user enable dbx-upgrade.service
Enter fullscreen mode Exit fullscreen mode

Note, I put 15 minutes sleep before running the upgrade, as I don't want the containers upgrade to race with the system update. You can alter this duration however you like. But there are 2 things for consideration:

  1. I do not recommend upgrading the container immediately, as I found that running the container too soon after logged in is not reliable at all (container died issue).
  2. If you want the sleep to be more than 1 minute/60 seconds, TimeoutStartSec=infinity is required. Otherwise, you would hit a timeout, and your main upgrade command would never be executed.

The Reliability of the Proxy Connection on Linux

The Man is on the Call

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

Top comments (0)