Join us

[Docker security] An Overview of Docker Security Essentials

In this article, we will see the best practices for Docker host security based on 6 scenarios.

Best Practices for Docker Host Security

  1. Secure and harden your host OS.
  2. Ensure your host is kept updated.
  3. Ensure you have the latest version of Docker running.
  4. Consider the use of a minimal Linux distribution such as Alpine that offers a much smaller threat surface.
  5. Add your host and containers to a robust vulnerability management plan and constantly scan your host and containers for vulnerabilities.
  6. Only run the services you need to run.
  7. Ensure your kernel is up to date.
  8. Keep up with the latest vulnerability news for the Linux kernel and the Docker platform.

Tutorial prerequisites

                ubuntu@Docker-Clair:~$ cat /etc/os-release 
VERSION="18.04.6 LTS (Bionic Beaver)"
PRETTY_NAME="Ubuntu 18.04.6 LTS"

Setup (if you do it locally, just skip this setup)

Install Docker

Update the apt package index and install packages to allow apt to use a repository over HTTPS:

                sudo apt-get update

sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \

Add Docker’s official GPG key:

                curl -fsSL | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Use the following command to set up the stable repository. To add the nightly or test repository, add the word nightly or test (or both) after the word stable in the commands below.

                echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine

Update the apt package index, and install the latest version of Docker Engine and containerd, or go to the next step to install a specific version:

                sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli

List the versions available in your repo:

                apt-cache madison docker-ce

Install a specific version using the version string from the second column, for example,

                root@Docker-host:~# sudo apt-get install docker-ce=5:20.10.12~3-0~ubuntu-bionic docker-ce-cli=5:20.10.12~3-0~ubuntu-bionic
Reading package lists... Done
Building dependency tree       
Reading state information... Done is already the newest version (1.4.12-1).
docker-ce-cli is already the newest version (5:20.10.12~3-0~ubuntu-bionic).
docker-ce is already the newest version (5:20.10.12~3-0~ubuntu-bionic).
0 upgraded, 0 newly installed, 0 to remove and 13 not upgraded.

Verify that Docker Engine is installed correctly by running the hello-world image.

                sudo docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete 
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bashShare images, automate workflows, and more with a free Docker ID: more examples and ideas, visit:

check the status

                ubuntu@jenkins:~$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2021-12-31 23:40:27 UTC; 4min 55s ago
TriggeredBy: ● docker.socket
   Main PID: 6261 (dockerd)
      Tasks: 9
     Memory: 34.4M
     CGroup: /system.slice/docker.service
             └─6261 /usr/bin/docke

Scenario based demos

1:Running Docker Containers with an Unprivileged User

Running Docker containers with an unprivileged user instead of the default “root” user prevents privilege escalation attacks.

As you can see above, it allows a user to run as root.

So countermeasures are

1:reconfigure and build your own Docker images

2:Prior to building your Docker image, specify an unprivileged user in your Dockerfile by adding the following command, replacing <USER> with your username and <GROUP> with a non-sudo group

                RUN groupadd -r <USER> && useradd -r -g <GROUP> <USER>

This is an example of Dockerfile.

                root@Docker-host:~# vi Dockerfile


                FROM ubuntu:18.04
LABEL maintainer="Takahiro Oda"
RUN groupadd -r taka && useradd -r -g taka taka
# Environment Variables
ENV HOME /home/taka
ENV DEBIAN_FRONTEND=noninteractive

Lets build the secure version of Docker image

                docker build . -t taka

You can see the new image listed

We can see that the unprivileged user logged in

                root@Docker-host:~# docker run -u taka -it --rm 0d54dc8a72d7 /bin/bash

You can delete the image

                root@Docker-host:~# docker images
taka          latest    0d54dc8a72d7   7 minutes ago   63.5MB
ubuntu        18.04     5a214d77f5d7   3 months ago    63.1MB
hello-world   latest    feb5d9fea6a5   3 months ago    13.3kB
root@Docker-host:~# docker rmi 0d54dc8a72d7
Untagged: taka:latest
Deleted: sha256:0d54dc8a72d75abd4def95a2aee06b6c5be889cf1504c3a65154a385bfb8c0ca
Deleted: sha256:39969fe4cb7d136b0c25e030c78fe096e2bbe0df1f0f8c770a8388ebc31b97a5
Deleted: sha256:060cf2ed1ad45963e1ff957c9317c5290e094aa53542921d8939d273c0dc31a6
Deleted: sha256:0621ab0cb71e31febef7dbbd60fd2524b00d4dd004edd780fdb5b45981e9ca19
Deleted: sha256:d009197e55453607831eea0821bb43a54436c754fb3c1e232dadb77b33054846

2:Disabling the Docker Container “root” User

You can disable the “root” user by changing the default shell from /bin/bash to /usr/sbin/nologin. This prevents any user on the container from accessing the “root” account irregardless of whether they have the “root” password.

                FROM ubuntu:18.04
LABEL maintainer="Takahiro Oda"
RUN groupadd -r taka && useradd -r -g taka taka
RUN chsh -s /usr/sbin/nologin root
# Environment Variables
ENV HOME /home/taka
ENV DEBIAN_FRONTEND=noninteractive

We can see that it fails to become root user. This configuration is only applicable if you want to disable the “root” account completely.

3:Preventing Privilege Escalation Attacks

It is recommended to run your containers with specific permissions and ensure that they cannot escalate their privileges.

                docker run --security-opt=no-new-privileges <IMAGE-ID>

4. Limiting Docker Container Kernel Capabilities

it is always recommended to not run containers with the --privileged flag as it overrides any other user permission and security restrictions you have set.


1:Drop all kernel capabilities

                root@Docker-host:~# docker run  -u taka -it   --cap-drop all 697ce6d07386  /bin/bash

For example, specify a particular kernel capabilities to be used.

                root@Docker-host:~# docker run  -u taka -it   --cap-drop all  --cap-add NET_ADMIN  697ce6d07386  /bin/bash

5:File System Permissions and Access

The ability to specify file system permissions and access allows you to set up containers with a read only file system or a temporary file system.

1: Run a Docker container with a read-only file system

                root@Docker-host:~# docker run  -u taka -it   --read-only  697ce6d07386  /bin/bash

6:Disabling Inter-Container Communication

It is possible to isolate Docker containers from one another which prevents them from communicating with each other. This can be helpful if you want to isolate a particular Docker container. By default, Docker does not isolate containers, allowing them to communicate with each other.

1: In order to disable inter-container communication, create a new Docker network with the enable_icc option set to false and replacing <NETWORK-NAME> with any desired name.

                root@Docker-host:~# docker network create --driver bridge -o ""="false" taka-network
# new network 
#show the networks
root@Docker-host:~# docker network ls
2334de9e5a40   bridge         bridge    local
82e9ac2e3980   host           host      local
8538813c01f6   none           null      local
336bcb061bc6   taka-network   bridge    local

You can see the details

                root@Docker-host:~# docker network inspect taka-network
        "Name": "taka-network",
        "Id": "336bcb061bc6e86405d5d3e89a21ff50b38958461583b569f0d2a334b20cfd78",
        "Created": "2022-01-01T22:58:41.820967913Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                    "Subnet": "",
                    "Gateway": ""
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "": "false"
        "Labels": {}

2:You can now run an isolated container by including the --network flag

                root@Docker-host:~# docker run -it -u taka --rm  --network taka-network taka /bin/bash

Only registered users can post comments. Please, login or signup.

Start blogging about your favorite technologies, reach more readers and earn rewards!

Join other developers and claim your FAUN account now!


Takahiro Oda

cloud security engineer

Security Analyst(Full-time), Cloud security engineer(internship).
User Popularity



Total Hits



Mentioned tools