Migrating Rootful Docker containers to Rootless Docker

Introduction

Rootless mode of docker allows a non-root user to run Docker daemon and containers. This helps in preventing potential vulnerabilities, like privilege escalation to root, in the Docker daemon and the container runtime. The best thing about rootless docker is that any user can install the Docker daemon without sudo(root) privileges. According to Docker docs,

Rootless mode does not use binaries with SETUID bits or file capabilities, except newuidmap and newgidmap, which are needed to allow multiple UIDs/GIDs to be used in the user namespace.

Source

Table of Contents

Prerequisites

  • An Ubuntu Server running latest kernel. Ubuntu kernel is recommended by Docker as it has overlay2 storage driver support and enabled by default which is required for rootless mode.
  • uidmap package which contains newuidmap and newgidmap programs required for rootless docker.
  • dbus-user-session package which helps in maintaining user sessions until all login sessions of a user has ended. For more info visit this link.
  • docker-ce-rootless-extras package in case dockerd-rootless-setuptool.sh is not present in /usr/bin.
  • Optional but recommended, install the systemd-container package which provides systemd’s tools for container management.

Install packages and create a rootless docker user

First start with installing the prereqs with the following command as root user:-

apt-get install uidmap dbus-user-session systemd-container docker-ce-rootless-extras

Next, stop and disable the system-wide rootful Docker daemon(if it is already running).

systemctl stop docker.socket docker.service
systemctl disable --now docker.socket docker.service

Next, remove the rootful Docker socket file /var/run/docker.sock as we no longer need it.

rm /var/run/docker.sock

Next, create a regular user without sudo privileges.

useradd -m -d /home/rootless -s $(which bash) rootless

Note:- I used the useradd command instead of adduser as it gives us fine-grained control over what privileges we want to give to the user. This also helps in creating a user account without a password which will prevent anybody(except root) to su or ssh into that user account. Remember, least privileges.

Login to rootless user account

The next step is important. Next, you must login into the rootless account(created above) using pam_systemd to follow the rest of the guide. This step is important because it helps to automatically create the XDG_RUNTIME_DIR environment variable and sets it to /run/user/$UID and also automatically cleans up on logout(I learned this the hard way.:)). You can get more info from here. TL;DR, if you didn’t use pam_systemd to login, then things will break and you’ll soon find yourself chasing bugs.:) Instead of logging in with sudo or su, you can log in using pam_systemd with one of the following ways:-

  • Through GUI access. (I’ve a headless server :P)
  • Through ssh rootless@localhost (Remember, we’ve not set a password. :P)
  • Through machinectl shell rootless@ (The machinectl program is included in the systemd-container package we installed above and this is the method we’re going to use to login as rootless. Yay!)

Next, login into the rootless account we created using the following command:-

machinectl shell rootless@
Login using machinectl command.

Install rootless Docker

Next, install rootless docker with the following command:-

dockerd-rootless-setuptool.sh install
Successful rootless Docker install

Next, append the environment variables given at the end of the successful installation to your ~/.bashrc file. Now, you can either logout and log back in or you can use “source ~/.bashrc” command to immediately apply the changes without logging out.

source ~/.bashrc

Usage

After installation you can start the Docker daemon with the following command:-

systemctl --user start docker

Next, enable the docker service to start at system startup with the following command:-

systemctl --user enable docker
Starting and enabling rootless docker.

The rootless Docker daemon doesn’t starts automatically. To start it automatically you need to enable lingering. According to loginctl manpage lingering means,

enable-linger [USER...], disable-linger [USER...]
           Enable/disable user lingering for one or more users. If enabled for a specific user, a
           user manager is spawned for the user at boot and kept around after logouts. This allows
           users who are not logged in to run long-running services. Takes one or more user names or
           numeric UIDs as argument. If no argument is specified, enables/disables lingering for the
           user of the session of the caller.

You can enable lingering with the following command:-

loginctl enable-linger $(whoami)

Next, use the following command to check whether it says “Linger=yes” at the end.

loginctl show-user $(whoami)
Lingering enabled.

At this point you can start all your docker containers normally using docker command. Also, one thing to note is I’ve faced some bugs in my containers when I first started them under rootless mode and after some troubleshooting I found out that the reason is because I didn’t stop and remove my previous containers started under rootful mode. Hope this tip helps you out.

Uninstall rootless Docker

Uninstalling rootless docker is pretty easy. Just run the following commands serially one by one(Note, you’ll be exited from shell of rootless user):-

systemctl --user stop docker.service
systemctl --user disable docker.service
exit
killall -u rootless
userdel -rf rootless
Uninstalling rootless Docker

Upgrading Rootless Docker

Currently there is no official way on how to do unattended-upgrades of rootless docker installations, which means unless you download the latest installation script and re-run it your docker won’t be updated to the latest version. So, after searching a bit online and putting everything together I came with this script for updating to latest rootless docker:-

#!/bin/bash

# Note:- pkill uses pgrep for process selection. If you've custom processes running with the word "docker" in them, it'll kill them. In that case uncomment the next line.
#/usr/bin/systemctl --user stop docker.service
pkill docker

# download rootless install script.
wget https://get.docker.com/rootless -O ~/rootless.sh

# env variables for above script
SKIP_IPTABLES=1
FORCE_ROOTLESS_INSTALL=1

# remove "Already installed verification" inside script.
sed -i 's#\-x\ \"\$BIN/\$DAEMON\"#\!\ \-x\ \"\$BIN/\$DAEMON\"#g' ~/rootless.sh

# execute the script.
/bin/bash ~/rootless.sh

Run the above script using the rootless user and it will work for docker run commands and you can add this as a cron job if you want. For people using docker-compose refer to this answer from docker forums upon which the above script is based.

And, that’s all for today. Hope you manage to migrate your containers to rootless mode.

Conclusion

If you’re a security conscious SysAdmin, then you should migrate your docker containers to rootless mode as soon as possible. While, this guide doesn’t adheres to the “Best practices” as mentioned in Docker docs, in my opinion this is the easiest way to migrate docker containers to rootless mode without any errors/issues in a “production environment”, i.e., if you strictly follow my guide. I migrated my own docker containers to rootless mode and after a fair amount of troubleshooting, I was able to successfully install rootless docker by following the above steps. If you still get any errors, then check the error in troubleshooting section of official Docker guide and if that didn’t help, then feel free to hit me up in the comments.

Thanks for reading. Stay secure, stay safe.

Leave a Reply

Your email address will not be published. Required fields are marked *