Securing Docker Containers
As Docker becomes more and more popular, the number of security exploits found is only likely to go up. It is quite important to configure a Docker container properly to prevent major security problems, yet this is not often done in practice. The following rules of thumb should help harden a Docker container and make it more secure in production.
Do not run Docker as root. If you are in the habit of prefixing docker commands with sudo, don't - especially avoid in production. Instead run containers as a regular user, this way any Docker exploits will at least run only with user privileges.
Create a Docker group and add your user to it.
sudo groupadd docker sudo usermod -aG docker $USER
And generally follow the steps on the Docker website.
- Processes inside the Docker should not be run as root, which is the default behaviour. The following lines at the top of your Dockerfile will ensure the application does not run as root:
RUN groupadd --gid 9999 appuser && \ useradd --system --uid 9999 -g appuser --create-home appuser WORKDIR /home/appuser USER appuserIt is a good idea to assign a group and user id, otherwise subsequent builds will have ids assigned non-deterministically and the image is not fully reproducible.
- Reproducible DOCKERFILE: It is crucial for the purposes of creating reproducible builds of the Docker image, to specify exact versions - eg.,
FROM python:3.7.3-alpine3.9as opposed to
FROM python:3The latter will get the latest Python docker at the time of build depending on the machine the command is run on, and could result in developers, qa, prod having sightly different versions of the Docker image.
- Reproducible Packages:This is a general rule that applies also outside the context of Docker. But individual packages inside the Docker - npm, pip, maven, and others alike - also need to be reproducible, and this means specifying exact versions. Read more on why this is a good idea.
- Do not inadvertently expose /var/run/docker.sock. This is the Docker Daemon socket and should be hidden from everything and especially anything inside the container. This socket file is the main API to control any of the docker containers running on that host and having write access to this is the same as having root access to the host. Many guides tell you to expose this - eg. to launch a docker container from within docker. This should be avoided.
- Linux kernel capabilities determine what a process can do - for example, to chown files or kill processes. These are selectively granted to a Docker container by the host operating system. By default only a subset of commands are available. The defaults are good for most use-cases, but for added security it is a good idea to drop all capabilities and add the ones you need back:
--cap-drop all --cap-add CHOWNDocker capabilities reference for --cap-add.
- Going off the above, the --privileged flag grants All capabilities. This is best avoided entirely.
- Disable default inter-container communication with --icc=false and use --link=CONTAINER_NAME_or_ID:ALIAS to explicitly link containers together.
- Consider limits on memory, cpu, number of restarts (--restart=on-failure:
), maximum number of file descriptors (--ulimit nofile= ) and maximum number of processes (--ulimit nproc= ). Ulimits reference
- If the application permits it, always use read-only filesystems with the --read-only to prevent modifications. This can be combined with tmpfs if the container needs to persist files temporarily.
--read-only --tmpfs /tmp
- Run Docker containers with --security-opt=no-new-privileges in order to prevent privilege escalation if a vulnerability does get exploited.
- Keep the host and Docker up to date. This should be part of the regular deploy process but bears repeating - up-to-date systems help thwart container escape vulnerabilities.
- Lastly, regularly audit the containers with tools such as Clair, Synk. These can check Docker images for vulnreabilities.
This is a pretty long list, but most of the advice boils down to this: Think carefully about exactly what you need your containers to do and make sure they are not capable of doing anything more. Careful application design can save you from many problems down the line.