Rootless (non-root user) Docker and Docker Compose running NGINX on Debian 10

Rootless (non-root user) Docker and Docker Compose running NGINX on Debian 10

Running rootless Docker allows non-root users to run the Docker daemon and containers without special privileges. This adds an extra layer of security in an event a container gains access to its host.

Rootless mode has become part of Docker Engine on v20.10. There are some known limitations that you should be aware of, see here. And, if you want to learn more about how it works, check out this article.

Pre-requirements

I'm installing Rootless Docker on a vanilla Debian 10 (Buster) x86_64; but this mode is supported in other distributions as well, more details here.

lsb_release -d && arch
(out)Description:    Debian GNU/Linux 10 (buster)
(out)x86_64

Installing Rootless Docker

1. As root (or using sudo, if you have that configured), let's create a new user called docker with its home folder at /srv/docker:

adduser --home /srv/docker docker
(out)Adding user `docker` ...
(out)Adding new group `docker` (1001) ...
(out)Adding new user `docker` (1001) with group `docker` ...
(out)Creating home directory `/srv/docker` ...
(out)Copying files from `/etc/skel` ...
(out)New password:
(out)Retype new password:
(out)passwd: password updated successfully
(out)Changing the user information for docker
(out)Enter the new value, or press ENTER for the default
(out)        Full Name []:
(out)        Room Number []:
(out)        Work Phone []:
(out)        Home Phone []:
(out)        Other []:
(out)Is the information correct? [Y/n] Y

2. We need to change these kernel settings before we start:

  • kernel.unprivileged_userns_clone = 1 enables unprivileged users to create namespaces.
  • net.ipv4.ip_unprivileged_port_start = 0 allows users to open privileged ports below 1024.
Load the new attributes using sysctl after the changes.
cat > /etc/sysctl.d/50-rootless.conf <<EOL
(out)kernel.unprivileged_userns_clone = 1
(out)net.ipv4.ip_unprivileged_port_start = 0
(out)EOL
sysctl --system

3. We should also load the overlay2 module, the recommended storage driver:

Reload new system modules using the systemctl command.
echo "options overlay permit_mounts_in_userns=1" > \
(out)/etc/modprobe.d/50-rootless.conf
systemctl restart systemd-modules-load.service

4. That's all needed from the root for now. Let's open a new terminal or ssh session with the newly created docker user:

ssh [email protected]

5. Download and run the Rootless Docker install script:

curl -fsSL https://get.docker.com/rootless | sh
(out)...
(out)[INFO] Installed docker.service successfully.
(out)[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
(out)[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger docker`
(out)
(out)[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):
(out)
(out)export PATH=/srv/docker/bin:$
(out)export DOCKER_HOST=unix:///run/user/1001/docker.sock

6. If the install was successful, we want to add the Docker environment variables to .bashrc, so the Docker commands are easily available in future logins:

cat >> ~/.bashrc <<EOL
(out)
(out)# Docker Environment
(out)export PATH=/srv/docker/bin:$PATH
(out)export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
(out)EOL

7. Finally, to enable the service to start automatically during the system boot:

systemctl --user enable docker
su -c "loginctl enable-linger $(whoami)"

8. I'd recommend a reboot at this point, just to make sure everything is working. You should be able to run a systemctl status to confirm the Docker daemon is running:

systemctl --user status docker
(out)● docker.service - Docker Application Container Engine (Rootless)
(out)   Loaded: loaded (/srv/docker/.config/systemd/user/docker.service)
(out)   Active: active (running) since Sat 2021-03-20 00:01:53 EDT; 11h ago
(out)     Docs: https://docs.docker.com/engine/security/rootless/
(out) Main PID: 522 (rootlesskit)
(out)   CGroup: /user.slice/user-1001.slice/[email protected]/docker.service

Installing Docker Compose

Docker Compose makes it easier to document, configure and manage your Docker containers.

There are many different ways on how to install it but for our rootless Docker, I decided to go meta and run it inside a container as well; which is pretty simple to do.

1. Download the small install script:

At the time this article was written the latest Docker Compose released was v1.28.5, but you should check current version here.
curl -L --fail https://github.com/docker/compose/releases/download/1.28.5/run.sh \
(out)-o $HOME/bin/docker-compose

2. Make it executable:

chmod +x $HOME/bin/docker-compose

3. When you run docker-compose for the first time, it will download all required images:

docker-compose version
(out)docker-compose version 1.28.5, build c4eb3a1
(out)docker-py version: 4.4.4
(out)CPython version: 3.7.10
(out)OpenSSL version: OpenSSL 1.1.1j  16 Feb 2021

Installing NGINX

While I don't fully intent to go over a NGINX installation, I didn't want to leave this article without something less tangible to test our rootless Docker setup.

In fact, this NGINX setup doesn't differ from your typical, root-enabled, Docker.

1. Let's create some folders structure to house our new NGINX container:

mkdir -p /srv/docker/containers/nginx && cd /srv/docker/containers/nginx

2. Using a docker-compose.yml file, we will create a nginx container using the nginx:alpine image and open the port 80:80:

vi docker-compose.yml
(out)version: "3.9"
(out)services:
(out)  nginx:
(out)    container_name: nginx
(out)    image: nginx:alpine
(out)    ports:
(out)      - "80:80"

3. Finally, start your new container, when ran for the first time it will download any required images:

docker-compose start

4. On your browser, open http://your.docker.ip/ and you should be welcomed by NGINX's default page:


References