techlogia — AI and Web Development Berlin
All courses
Free to read – no sign-up

Secure Docker Containers

You harden a Docker installation following the CIS Docker Benchmark: eliminate root containers, restrict capabilities, enable read-only filesystems, configure network isolation, and secure the Docker daemon. Prerequisite: docker-basics module. 6 tasks, about 90 minutes.

Duration: 90 minLevel: AdvancedExercises: 6

Harden Docker installation per CIS Benchmark

Securing Docker containers (per CIS benchmark)

Docker packages applications into containers — isolated units that share the OS kernel but have their own filesystems and processes. By default, however, containers are insecurely configured: they run as root, are allowed a lot, and share a network.

The CIS Docker Benchmark is a recognised industry standard of hardening recommendations. You implement the most important ones.

Key terms

  • Daemon (dockerd): the background service that manages containers. Its config lives in /etc/docker/daemon.json.
  • Capabilities: finely graded partial rights of root (e.g. "open ports below 1024"). A container usually needs almost none.
  • User-namespace remapping: maps root inside the container to an unprivileged user on the host.

Your goal

You harden a Docker installation: user namespaces, non-root containers, dropping capabilities, a read-only filesystem, network isolation, and a hardened daemon. Prerequisite: the Docker Basics module.

Exercises

  1. 1. Enable user namespace remapping

    Concept: user-namespace remapping. If an attacker breaks out of a container running as root, they are root on the host too — total compromise. Remapping maps container root to an unprivileged host user: a breakout then lands as a harmless user. The single most important container hardening.

    Add to /etc/docker/daemon.json (sudo nano /etc/docker/daemon.json):

    {
      "userns-remap": "default"
    }

    Then restart Docker:

    sudo systemctl restart docker

    On a typo Docker will not start — then check with sudo journalctl -u docker -n 30.

    Check: /etc/docker/daemon.json contains userns-remap, and systemctl is-active docker reports active.

  2. 2. Run container as non-root

    Concept: the USER directive. Containers run as root by default — the most dangerous Docker default. With the USER instruction in the Dockerfile the process runs as an unprivileged user.

    Create /home/student/secure-app/Dockerfile (mkdir -p /home/student/secure-app && nano /home/student/secure-app/Dockerfile) with:

    FROM alpine:3.20
    RUN adduser -D appuser
    USER appuser
    CMD ["sleep", "3600"]

    The USER appuser line is the decisive part — the value must not be root or 0.

    Check: /home/student/secure-app/Dockerfile contains a USER directive.

  3. 3. Drop unnecessary capabilities

    Concept: capabilities. root is split into many capabilities (e.g. NET_ADMIN, SYS_TIME). Most containers need none of them. With --cap-drop=ALL you remove all and add back only what is needed (--cap-add) — drastically shrinking the attack surface.

    Build the image and start the container with capabilities dropped:

    cd /home/student/secure-app
    docker build -t secure-app .
    docker run -d --name secure-app --cap-drop=ALL secure-app

    Check: docker inspect --format '{{.HostConfig.CapDrop}}' secure-app contains ALL.

  4. 4. Enforce read-only filesystem

    Concept: read-only root filesystem. A write-protected container filesystem stops malware from modifying programs or nesting itself in. Where the container does need to write (e.g. /tmp), you deliberately provide an ephemeral tmpfs.

    Restart the container with a read-only filesystem (remove the old one first):

    docker rm -f secure-app
    docker run -d --name secure-app --cap-drop=ALL --read-only --tmpfs /tmp secure-app

    Check: docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' secure-app yields true.

  5. 5. Isolate container network

    Concept: internal network. A Docker network created with --internal has no connection to the internet. Perfect for a database that should only be reachable by the app container, never from outside — containment instead of free communication.

    Create an internal network:

    docker network create --internal secure-net

    Check: docker network ls lists secure-net.

  6. 6. Secure Docker daemon

    Concept: daemon hardening. Two important daemon options: no-new-privileges stops processes from gaining more rights later via SUID; icc: false forbids arbitrary containers from talking to each other unprompted (protection against lateral movement).

    Extend /etc/docker/daemon.json so it additionally contains:

    {
      "userns-remap": "default",
      "no-new-privileges": true,
      "icc": false
    }

    Then restart Docker:

    sudo systemctl restart docker

    Check: /etc/docker/daemon.json contains no-new-privileges (true) and icc (false).

Now practice it yourself

Reading is good – doing is better. Start this course on a real Linux VM, right in your browser. A free account is all it takes.

Start for free

Lab content under CC BY 4.0 – free to use with attribution (© TechLogia).

Secure Docker Containers