Skip to content

Instantly share code, notes, and snippets.

@sw4k
Last active April 16, 2024 03:34
Show Gist options
  • Save sw4k/abf8836c1acdebdadb452615e3945ca1 to your computer and use it in GitHub Desktop.
Save sw4k/abf8836c1acdebdadb452615e3945ca1 to your computer and use it in GitHub Desktop.
Install Docker on Qubes OS 4.2

Install Docker on Qubes OS 4.2

The primary reason to change the data directory for docker to the home directory for user is to have persistence across AppVM restarts. The secondary reason is to work around permission issues.

Here we provide "the Qubes way" of solving for both that does not leave "bind-dirs" nor "docker" in a broken state.

Step 1: TemplateVM - Perform installation of docker via your package manager, and ensure services are disabled

sudo apt install -y docker.io
sudo systemctl disable containerd
sudo systemctl disable docker

Step 2: TemplateVM - Add user to docker group

According to docker documentation, by adding user to a docker group there will not be a need to sudo docker to access docker engine via unix socket. This, in turn, will eliminate the situation where files created by docker cli have their ownership set to root:root.

This is ideal.

Most packaged versions of docker will create the docker group for you, but the below bash snippet will check for the group and create it if missing, then add user to the docker group:

[[ $(getent group docker) == '' ]] && sudo groupadd docker;
sudo usermod -aG docker user;

If you do not want this change on the TemplateVM for security reasons, you could add the above script to your AppVM-specific /rw/config/rc.local instead for the same effect, but anyone that has access to user has access to root and can do "all the things" anyway.

Step 3: TemplateVM - Modify Qubes "bind-dirs" for AppVM persistence

While it's possible to move the docker working directory via config into /home/user/ it's not necessary. It's a facility that exists for subsystems which do not allow rootfs modification. "The Qubes Way" is to configure "bind-dirs", this technique applies not only to docker but any and all services, tools, etc which normally operate off the rootfs for their configuration.

The following bash snippet will create the necessary "bind-dirs" configuration, all referenced directories should already exist and be populated with relevant files, but there are checks to create them anyway for anyone that has performed a "binary install" rather than a "package install":

# ensure bind-dirs exist with expected privs
[ ! -d /etc/docker ] && sudo mkdir -p /etc/docker && sudo chmod 755 /etc/docker;
[ ! -d /var/lib/docker ] && sudo mkdir -p /var/lib/docker && sudo chmod 710 /var/lib/docker;
# do bind-dirs setup
sudo mkdir -p /etc/qubes-bind-dirs.d
sudo touch /etc/qubes-bind-dirs.d/30_docker.conf
sudo chmod 644 /etc/qubes-bind-dirs.d/30_docker.conf
echo "binds+=( '/var/lib/docker' )" | sudo tee --append /etc/qubes-bind-dirs.d/30_docker.conf
echo "binds+=( '/etc/docker' )" | sudo tee --append /etc/qubes-bind-dirs.d/30_docker.conf

Step 4: dom0 - Enable docker daemon/service

Rather than run docker as user via rc.local consider using the systemd units and scripts which already exist for the purpose of running docker as a service. Personally, I do this from the "Services" tab of "Qube Manager" for the target Qube, but, here is the CLI equivalent to be run from a dom0 shell. Remember to change $QUBENAME to be the name of your target AppVM/Qube:

qvm-service --enable $QUBENAME docker

Step 5: AppVM - Finishing up... and verifying.

Exit the dom0 shell. Shutdown your TemplateVM, saving all changes. Launch a terminal in your AppVM to perform verifications.

In your AppVM, the following commands should execute without any errors as evidence you have set everything up correctly, systemctl status commands should show each service is "active / running" (you may get a journalctl warning because you are not running the command as root, which is fine.) Once you are in busybox you can uname -a for confirmation or just exit.

systemctl status containerd
systemctl status docker
docker run --rm -it busybox

After exiting busybox, you can verify "bind-dirs" is working as intended by restarting the AppVM (sudo reboot), then launching a new AppVM terminal verify the busybox image persisted between restarts (optionally removing the image):

docker images
docker rmi busybox

Why would I do it this way?

  1. It preserves the privilege model and run time environment intended by docker devs and/or package authors.
  2. It leverages package-supplied units for service start-up and configuration, which may change over time.
  3. It is DispVM friendly.

Generally, this eliminates any configuration steps from being "inside the AppVM", any time we have to modify /rw/ directly from "inside" an AppVM we're taking an "advanced approach" to administration of our Qubes meant for solving problems that can't be solved by any regular means. To illustrate: Why bootstrap docker from the TemplateVM? Why not bootstrap everything from rc.local? Because it is inconvenient, and not necessary, unless you are trying to keep everything outside of the TemplateVM and only need docker in a single AppVM.

These means and reasons apply to more than just docker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment