Rootless Docker & User Namespaces

By default, the Docker daemon (dockerd) runs as root. This is a massive security risk. If an attacker escapes a container (via a kernel exploit), they become root on the host, gaining full control over your server.

Rootless Docker mitigates this by running the daemon and containers inside a User Namespace (USERNS).

1. The “Root” Problem

In a standard Docker installation:

  1. Daemon: Runs as root (UID 0) on the host.
  2. Container: Processes inside often run as root (UID 0).
  3. Kernel: The kernel sees both as the same user (UID 0).

[!WARNING] If a containerized process exploits a vulnerability like Dirty COW or CVE-2019-5736 (runc overwrite), it breaks out of the container and lands on the host as root. Game over.

2. The Solution: User Namespaces (USERNS)

User Namespaces allow you to map a range of UIDs on the host to a different range of UIDs inside the container.

  • Inside the container: The process thinks it is root (UID 0).
  • On the Host: The process is actually an unprivileged user (e.g., UID 1000).

This is achieved via /etc/subuid and /etc/subgid.

How Mapping Works

We allocate a range of “subordinate UIDs” to a user. Example /etc/subuid:

alice:100000:65536

This means: User alice can use UIDs starting from 100,000 up to 165,535.

When alice runs a container:

  • Container UID 0 → Host UID 100000
  • Container UID 1 → Host UID 100001

If the attacker breaks out, they land on the host as UID 100000 (a nobody), not root.


3. Interactive: User Namespace Visualizer

See how User Namespaces map IDs between the Container and the Host.

Inside Container

root
→ Mapped To →

Host Perspective

100000
Unprivileged User

Configuration (/etc/subuid)

alice:100000:65536

4. Installing Rootless Docker

Run Docker as a standard user without sudo.

1. Prerequisites

Ensure uidmap is installed:

sudo apt-get install -y uidmap

2. Installation Script

Run the installation script as a non-root user:

curl -fsSL https://get.docker.com/rootless | sh

3. Environment Setup

Add the following to your ~/.bashrc:

export PATH=/home/alice/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock

4. Running the Daemon

Use systemd (user mode) to start the daemon:

systemctl --user start docker
systemctl --user enable docker

5. Configuration: daemon.json

Even in rootless mode, you might need to configure the daemon. The config file lives in ~/.config/docker/daemon.json.

{
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

[!NOTE] Overlay2 Support: Rootless Overlay2 requires Linux Kernel 5.11+ or fuse-overlayfs installed. Without it, Docker falls back to the slower vfs driver.

6. Trade-offs: Root vs Rootless

Feature Rootful Docker Rootless Docker
Security Low (Daemon runs as root) High (Daemon runs as user)
Port Binding Can bind ports < 1024 Cannot bind ports < 1024 (requires sysctl tweak)
Performance Native Slight overhead (USERNS translation)
Cgroups Full Support Limited (Requires Cgroup v2)
Complexity Easy (Default) Medium (Env vars, limited drivers)

When to use Rootless?

  • Always, if possible.
  • Multi-tenant environments (Shared CI runners).
  • Production servers where security is paramount.

[!TIP] Port 80/443: To bind privileged ports in rootless mode, allow it via sysctl: sudo sysctl net.ipv4.ip_unprivileged_port_start=0