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!

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:

  1. Install Distrobox
  2. Create a Distrobox Container
  3. Install Cloudflare WARP client in the Container
  4. Enable WARP Service
  5. Register the Client
  6. Turn On Malware Filtering (optional)
  7. Enable Proxy Mode and Config Proxy Port
  8. Connect to WARP
  9. Export the Client (warp-svc)
  10. Reroute Our Host's Connection
  11. Verify WARP Connection
  12. Automatically Connect to WARP on System Startup + Kill Switch
  13. 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


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 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"


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.
  • --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 about Failed 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


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

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Or we can simply click on the container's icon, which Distrobox automatically created for us when we created the container:

Containers

Run this command in the container to 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

9. Export the Client (warp-svc)



distobox-export -b /bin/warp-svc --sudo


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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:

GNOME's proxy settings #1

GNOME's proxy settings #2


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


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.


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


Enter fullscreen mode Exit fullscreen mode
  • Replace archerallstars with your username.
  • 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.

12.2. The Timer File



[Unit]
Description=Start Cloudflare WARP connection with some delays.

[Timer]
OnStartupSec=10
RandomizedDelaySec=5

[Install]
WantedBy=timers.target


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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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)