Harden Docker with CIS – (P3) Docker daemon configuration – Part 2

So in P3 of the Harden Docker with CIS series, I’ll continue with the hardening process of the Docker installation which we set up in the P1. We’ll continue with module two of the benchmark (CIS Docker Benchmark v1.2.0) i.e. Docker daemon configuration. In this post, we’ll cover the remaining items of the Docker daemon configuration.

Not Scored

CIS ControlDescription
2.7Ensure the default ulimit is configured appropriately

Scored

CIS ControlDescription
2.1Ensure network traffic is restricted between containers on the default bridge
2.2Ensure the logging level is set to ‘info’
2.3Ensure Docker is allowed to make changes to iptables
2.4Ensure insecure registries are not used
2.5Ensure aufs storage driver is not used
2.6Ensure TLS authentication for Docker daemon is configured
2.8Enable user namespace support
2.9Ensure the default cgroup usage has been confirmed
2.10Ensure base device size is not changed until needed
2.11Ensure that authorization for Docker client commands is enabled
2.12Ensure centralized and remote logging is configured
2.13Ensure live restore is enabled
2.14Ensure Userland Proxy is Disabled
2.15Ensure that a daemon-wide custom seccomp profile is applied if appropriate
2.16Ensure that experimental features are not implemented in production
2.17Ensure containers are restricted from acquiring new privileges

2.10 Ensure base device size is not changed until needed

Under certain circumstances, you might need containers larger than 10G, which is the default size of the base device. The base device size can be increased on the daemon restart. Increasing the base device size allows all future images and containers to be of the new base device size. This can lead to denial of service if the allocated partition becomes full at any given time.

Add the following to increase the base device size to 15G.

"storage-opts": ["dm.basesize=15G"]
Code language: JSON / JSON with Comments (json)

2.11 Ensure that authorization for Docker client commands is enabled

This can only be set up with an authorization plugin. This will need a separate post of its own, thus not covered here. I might make a separate post altogether this CIS control. However, it can be set up using the following command

"authorization-plugin": "PLUGIN_ID"
Code language: JSON / JSON with Comments (json)

2.12 Ensure centralized and remote logging is configured

This option can be set up to ensure that logs are stored in the central location i.e. SIEM tool or log collector. By default, JSON-file is used to store the logs, which can be verified with the following command.

$ docker info --format '{{ .LoggingDriver }}' json-file
Code language: Bash (bash)

A new logging driver can be setup by adding the following the in /etc/docker/daemon.json file

{ "log-driver": "syslog", "log-opts": { "syslog-address": "tcp://192.xxx.xxx.xxx" } }
Code language: JSON / JSON with Comments (json)

2.13 Ensure live restore is enabled

The --live-restore option enables full support of daemon-less containers within Docker. It ensures that Docker does not stop containers on shutdown or restore and that it properly reconnects to the container when restarted.

Verify if live restore is enabled using the following command

$ docker info --format '{{ .LiveRestoreEnabled }}' false
Code language: Bash (bash)

Enable live restore by adding the following to the /etc/docker/daemon.json file and restarting the Docker daemon for the change to take effect

"live-restore": true
Code language: JSON / JSON with Comments (json)

Once we restart the Docker daemon we can verify the changes

$ docker info --format '{{ .LiveRestoreEnabled }}' true
Code language: Bash (bash)

2.14 Ensure Userland Proxy is Disabled

The Docker engine provides two mechanisms for forwarding ports from the host to containers, hairpin NAT, and the use of a userland proxy. In most circumstances, the hairpin NAT mode is preferred as it improves performance and makes use of native Linux iptables functionality instead of using an additional component.

Where hairpin NAT is available, the userland proxy should be disabled on startup to reduce the attack surface of the installation.

By default userland proxy is enabled thus it should be disabled by adding the following option to the /etc/docker/daemon.json file

"userland-proxy": false
Code language: JSON / JSON with Comments (json)

2.15 Ensure that a daemon-wide custom seccomp profile is applied if appropriate

Seccomp profiles on Docker limit the system calls a container/process can make. There is a default seccomp profile enabled by Docker which limits a few critical system calls from containers. We can verify if this is enabled by default using the following command

$ docker info --format '{{ .SecurityOptions }}' [name=apparmor name=seccomp,profile=default name=userns]
Code language: Bash (bash)

A custom seccomp profile can be created, which can then be utilized by the Docker daemon. This custom profile can be set using the following option in /etc/docker/daemon.json file.

"seccomp-profile": "PATH_TO_SECCOMP_PROFILE"
Code language: JSON / JSON with Comments (json)

This CIS item was “Not scored” however, as per my understanding it is very critical and should always be considered when setting things up. This vastly limits the attack surface of a container.

2.16 Ensure that experimental features are not implemented in production

This is a no brainer, experimental features should not be enabled in production. These can be disabled by adding the following to the daemon configuration file.

"experimental": false
Code language: JSON / JSON with Comments (json)

2.17 Ensure containers are restricted from acquiring new privileges

A process can set the no_new_priv bit in the kernel and this persists across forks, clones and execve. The no_new_priv bit ensures that the process and its child processes do not gain any additional privileges via suid or sgid bits. By default containers are not restricted thus his option should be explicitly set in the daemon configuration file.

"no-new-privileges": true
Code language: JSON / JSON with Comments (json)

This completes part 2 of our Docker daemon configuration section of the CIS Docker Benchmarks. We’ll start with other sections in the next post.

If you have questions or need help setting things up, reach out to me @jtnydv