[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 
NAME="Ubuntu"
VERSION="18.04.6 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.6 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bioni
            

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 \
    lsb-release
            

Add Docker’s official GPG key:

                curl -fsSL https://download.docker.com/linux/ubuntu/gpg | 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] https://download.docker.com/linux/ubuntu \
  $(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 containerd.io
            

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 containerd.io
Reading package lists... Done
Building dependency tree       
Reading state information... Done
containerd.io 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.
    (amd64)
 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:
 https://hub.docker.com/For more examples and ideas, visit:
 https://docs.docker.com/get-started/
            

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
       Docs: https://docs.docker.com
   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
            

Inside

                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
taka@a877f0da0d2e:/$
            

You can delete the image

                root@Docker-host:~# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
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.

Countermeasures

1:Drop all kernel capabilities

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

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
taka@8c64eb91a58c:/$
            

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
taka@28436b13aec8:/$
            

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 "com.docker.network.bridge.enable_icc"="false" taka-network
# new network 
336bcb061bc6e86405d5d3e89a21ff50b38958461583b569f0d2a334b20cfd78
#show the networks
root@Docker-host:~# docker network ls
NETWORK ID     NAME           DRIVER    SCOPE
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": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.enable_icc": "false"
        },
        "Labels": {}
    }
]
root@Docker-host:~#
            

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 and get more readers

Join other developers and claim your FAUN account now!

Avatar

Takahiro Oda

cloud security engineer

@meibutuoyaji
Security Analyst(Full-time), Cloud security engineer(internship). https://www.linkedin.com/in/takahiro-oda-881423197/
Stats
18

Influence

704

Total Hits

1

Posts

Discussed tools