Last Updated on 2nd Feb 2023.

Containers?

Why do we need containers over VMs -

  • Efficient Resource Consumption between containers
  • Once License for services/OS
  • Low Compute Overhead

What does docker engine does?

  • Emulates Filesystem
  • Gives each container unique process ID
  • Isolation of container process

Communication between the architecture components -

  • Components
    • Docker client (The one user interacts with)
    • Docker Host
      • Docker Daemon
      • Images
      • Containers
    • Registry
  • Docker client using serveral API calls sends the commands to Docker Engine which is being forwarded to containerd.
  • Commuication between docker daemon and containerd is facilated through gRPC calls.
  • Docker Client communicates with the Docker Daemon through a domain socket (if local) and through a TCP Socket (if remote).

What does runc do?

  • Upon getting instructions from the containerd, it will start the containers for you.
  • Kernel Primitives are being used to spin up the containers
    • cgroups (Set Resource Limits)
    • namespaces (Isolation of Containers)

What needs to be secured?

  • Host operating system
  • Docker Daemon
  • Containers
  • Authn. and Communication
  • Registry Security

Defensive/Best Practices

Running Docker Containers with an Unprivileged User

Add the following line in the Dockerfile -

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

Build the image and run the image as the newly created user -

$ docker build .
$ docker run -u <USER> -it --rm <IMAGE-ID> /bin/bash

Disabling Docker Container Root User

This can be done by changing the shell to nologin. The the following line in the Dockerfile -

RUN chsh -s /usr/sbin/nologin root

To run Docker in rootless mode:

  1. Install Docker in root mode - see instructions.
  2. Use the following command to launch the Daemon when the host starts:
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
  1. Here is how to run a container as rootless using Docker context:
docker context use rootless
docker run -d -p 8080:80 nginx

Prevent Privilege Esclation

Spin up the docker container using the flag --security-opt=no-new-privileges -

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

Limiting Docker Container Kernel Capabilities

Capabilities define if a container has what privileges, so to spin up a container, you can drop all the privileges -

docker run --cap-drop all <IMAGE-ID>

or limit the capabilities the user is going to have -

docker run --cap-drop all --cap-add <CAPABILITY> <IMAGE-ID>

Valid capabilities values can be found at - Capabilities Keys .

Container with a Read-Only File System

Use the --read-only flag to spin up the container -

docker run --read-only <IMAGE-ID>

Container with Specific Directory with Write Privileges

Use the --read-only flag to first make the entire file system as read only then use the --tmpfs flag to specify the directory to have the write privilege.

docker run --read-only --tmpfs /tmp <IMAGE-ID>

Disable Inter-Container Communication(ICC)

By default the ICC is set to true and the network drive to bridge, but if you want to disable the ICC then you need to create a network with com.docker.network.bridge.enable_icc option set to false.

docker network create 
  --driver bridge \
  -o "com.docker.network.bridge.enable_icc"="false" \
  <NETWORK-NAME>

and then run the container by specifying the same network created with the above mentioned option -

docker run --network <NETWORK-NAME> <IMAGE-ID> 

If the containers are in different networks, obviously it won’t be able to connect to the network. But also when the container is in the same network, which means they are in the same subnet, then also it won’t allow the inter-container communication.

Limit Container Resources

Limit % utilization of the CPU -

docker run -it --rm --cpus 0.25 <image name> /bin/bash

Update % utilization during runtime of containers -

docker update --cpus 0.25 <CONTAINER-ID>

Limit CPU Cores -

docker run -it --rm --cpuset-cpus=0 <image name> /bin/bash

Limit CPU Usage by Size -

docker run -it --rm -m 128m <image name> /bin/bash

Limit number of processes a container can fork (prevents fork bomb ) -

docker run -it --rm --pids-limit 5 <image name> /bin/bash

Implement Access Control

We must specify which system resources and functionality the containers can use and for that we need access control. In this section we will be talking about Mandatory Access Control (MAC).

Tools that can be used for implementation of MAC -

  • AppArmour
  • SELinux (Fedora/RedHat Based Distro.)
  • Seccomp

Using AppArmour

  1. Generate an AppArmour Profile based on your use-case (Recommended) or use a existing template (docker-default).
  2. Run the command to run containers with a AppArmour Profile mentioned -
docker run -d --security-opt=”apparmor:<profile-name>” <docker image>

Using Seccomp

Restricting System Calls inside the containers.

  1. Create a seccomp profile. (Ref: Seccomp Practice Lab , Docker Seccomp Security Profile Documentation )
  2. Run the command to spin up containers with a seccomp profiles -
docker run -d --security-opt seccomp:/path/to/profile/profile.json <docker image>

Building Golden Images for your Containers

This blog covers most of the pointers to consider while creating a Container - A practical guide to writing secure Dockerfiles by Madhu Akula .

  • TL;DR
    • Using Buildkit (BuildKit is a toolkit for converting source code to build artifacts in an efficient, expressive, and repeatable manner.)
    • Using Linters
    • dockle - Container Image Scanning for Security Issues (Generic + CIS Benchmark Checks)
    • docker-slim (Minify and Secure Docker Containers)
    • div (Explore each layer to shrink the size of Docker/OCI Image)
    • OPA (High-level declarative language that lets administrators specify policy as code and simple APIs to offload policy decision-making from their software.)

Adding HEALTHCHECK Instruction to Container Image

  • Add the following syntax instruction to the Dockerfile -
...
# Syntax - HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1
...

There can only be one HEALTHCHECK instruction in a Dockerfile. If you list more than one then only the last HEALTHCHECK will take effect. The command after the CMD keyword can be either a shell command (e.g. HEALTHCHECK CMD /bin/check-running) or an exec array (as with other Dockerfile commands; see e.g. ENTRYPOINT for details).

  • Adding --health-cmd to perform the health check -
# Check that the nginx config file exists
docker run --name=nginx-proxy -d \
        --health-cmd='stat /etc/nginx/nginx.conf || exit 1' \
        nginx:1.13

To check the health status -

docker inspect --format='{{json .State.Health}}' <container image>

Ref: Docker Documentation - HEALTHCHECK

Do Not Expose the Docker Daemon Socket

Docker daemon socket is a Unix network socket used to communicate with the Docker API. The root user owns this socket by default. If anyone else gains access to the socket, they will have the same privileges as the host’s root user.

Follow these best practises to avoid this problem:

  • Unless you’re using Docker’s encrypted HTTPS socket, which supports authentication, never make the daemon socket available for remote connections.
  • Do not run Docker images with the -v /var/run/docker. sock://var/run/docker.sock, which exposes the socket in the container that results.

Host Security

  • Minimal OS Distribution for smaller attack surface
  • Hardening of Host OS
  • Apply Regular Updates
    • OS Packages
    • Kernel
    • Docker Runtime
  • Use Lynis - Security auditing tool for Linux, macOS, and UNIX-based systems. Assists with compliance testing (HIPAA/ISO27001/PCI DSS) and system hardening. Agentless, and installation optional. It also provides recommendation steps.

Auditing

Linux Auditing

Handled by Linux Kernel. Used for configuring audit policies for user-space processes. Linux Audit Framework is made up from different components -

  • Auditd - Daemon. Saves audit events to audit logs.
  • Audit Log - Contains logs from all the defined rules.
  • Auditctl - Client software used to manage the framework (Create and Delete Rules)
  • Audit.rules - A config. file that contains audit rules.

Implementation : TBD

Log Collection

Audit should be conducted on the following -

  • Container daemon activities
  • These files and directories:
    • /var/lib/docker
    • /etc/docker
    • docker.service
    • docker.socket
    • /etc/default/docker
    • /etc/docker/daemon.json
    • /usr/bin/docker-containerd
    • /usr/bin/docker-runc

Get System Information in regards to Docker

To check the information of the system for Docker in relation to host operating system -

docker system info

Static Analysis Tools

Offensive

As a starting point, we can use the MITRE ATT&CK Framework for Containers to categorize the attacks that we are going to discuss.

Initial Access

References