It’s been over 6 years from the initial release of Docker on March 13, 2013. In the world of software development it looks more like a few centuries passed. Now, docker is widely regarded as mature and it is well working in many production environments. Also it has reasonably well functioning security controls enabled by default, namely namespaces, capabilities, cgroups. Nonetheless, due to the unavoidable differences in environments, it is not possible to make the best security configuration for everyone as a default.
If you want to take the security of your environment to the higher level, you can tweak controls that are already used by docker as well as add new ones. To make it easier to grasp one can split controls into two categories, namely related to docker host and related to docker images/containers.
Briefly speaking, everything which comes with OS hardening topic should be considered here.
- We should have a minimal system, meaning no unnecessary software, services, users etc.
- The system should be up to date, at least when it comes to security patches.
- Services should be up to date. Particularly the docker daemon should be updated (some examples of serious vulnerabilities of docker from the last 2 years are CVE-2019–5736, CVE-2018–11757, CVE-2018–11756, CVE-2018–9862, CVE-2018–8115).
- Additionally we might choose to harden OS and basic services. For example enforce thoughtful policy on passwords, for SSH access maybe avoid passwords at all allowing users to only use cryptographic keys (disabling root login by the way) or limit docker daemon’s privileges and access to resources (more on that in the next part of the article).
- Finally we can use some security extensions such as SELinux, Seccomp, AppArmor, GRSecurity or others. Most of those will require a lot of additional effort though.
Docker Daemon, Images and Containers
In general, for docker images and containers the same principles as with host hardening apply. From the hardening perspective, the image can be viewed as an OS with one or more services (a proper image should include only one service and the software that is required by the service). The new thing here is the separation layer between the container and the docker host.
When building images it is worthwhile to create a low privilege user and then use it when running the container. We can do this adding following lines in Dockerfile:
RUN adduser -D limited_user
The first one adds user named limited_user, the second one instructs docker to use this user inside the container. Apart from that we can specify the user when starting the container, like this:
docker run -u limited_user ubuntu
Of course the user specified in such way has to exist inside the container.
On a default centos host installation when running centos container with docker we get following output:
[root@localhost ~]# docker run -it centos /bin/bash
[root@58428c062898 /]# id
uid=0(root) gid=0(root) groups=0(root)
But when running with -u and user nobody (present inside the default centos image that is used), we see following:
[root@localhost ~]# docker run -u nobody -it centos /bin/bash
uid=99(nobody) gid=99(nobody) groups=99(nobody)
Docker allows to limit resource availability per container. To allow the container to use no more than 1 CPU and 512MB of memory we use following:
docker run -it — cpus 1 — memory 512Mb centos
Docker Content Trust
When building images it is safer to use a trusted base OS and add needed software oneself than to get a ready to go image prepared by someone else, unless it is trustworthy. To ensure that we’re pulling the image that we think we’re pulling, we might use Docker Content Trust.
When we have above low hanging fruits covered we can go further. A great place to start is docker-bench-security — an open source project that checks for dozens of common best-practices around deploying Docker containers in production. When run on default centos 7 installation gives the following output:
Docker is a great solution providing an additional layer of separation and increasing manageability in complex systems. Basic hardening of a default installation can be done within few minutes — don’t run as root and appropriately limit resources available to containers. If you want to address wider range of issues — try docker-bench-security.