Docker may be a new thing for many environments but it has been around long enough to be considered ready for production use. Like all technologies, it is not inherently insecure but there are issues that should be addressed and points to focus on in order to keep a docker environment safe.
To use Docker safely, you need to be aware of potential security issues, tools, and techniques for securing systems and infrastructure which is based on container technology.
Find below a list of docker security practices for securely configuring your Docker containers and images.
- Always use the most up-to-date version of Docker.
- Allow only trusted users control of the Docker daemon by making sure only trusted users are members of Docker group. Check out this article for more information about decreasing your Docker daemon attack surface.
- Make sure you have rules in place that give you an audit trail for:
- Docker daemon
- Docker files and directories:
- /var/lib/docker
- /etc/docker
- Docker.service
- Docker.socket
- /etc/default/docker
- /etc/docker/daemon.json
- /etc/sysconfig/docker
- /usr/bin/containerd
- Secure all Docker files and directories (see 4.2 above) by ensuring they are owned by the appropriate user (usually the root user) and their file permissions are set to a restrictive value (see the CIS benchmarks section on Docker daemon configuration files).
- Use registries that have a valid registry certificate or ones that use TLS to minimize the risk of traffic interception.
- If you are using containers without an explicit container user defined in the image, you should enable user namespace support, which will allow you to re-map container user to host user.
- Disallow containers from acquiring new privileges. By default, containers are allowed to acquire new privileges so this configuration must be explicitly set. Another step you can take to minimize a privilege escalation attack is to remove the setuid and setgid permissions in the images.
- As a best practice, run your containers as a non-root user (UID not 0). By default, containers run with root privileges as the root user inside the container.
- Use only trusted base images when building your containers. This tip might seem like an obvious one, but third-party registries often don’t have any governance policies for the images stored in them. It’s important to know which images are available for use on the Docker host, understand their provenance, and review the content in them. You should also enable Content trust for Docker for image verification and install only verified packages into images.
- Use minimal base images that don’t include unnecessary software packages that could lead to a larger attack surface. Having fewer components in your container reduces the number of available attack vectors, and a minimal image also yields better performance because there are fewer bytes on disk and less network traffic for images being copied. BusyBox and Apline are two options for building minimal base images.
- Implement a strong governance policy that enforces frequent image scanning. Stale images or images that haven’t been scanned recently should be rejected or rescanned before moving to the build stage.
- Build a workflow that regularly identifies and removes stale or unused images and containers from the host.
- Don’t store secrets in images/Dockerfiles. By default, you’re allowed to store secrets in Dockerfiles, but storing secrets in an image gives any user of that image access to the secret. When a secret is required, use a secrets management tool.
- When running containers, remove all capabilities not required for the container to function as needed. You can use Docker’s CAP DROP capability to drop a specific container’s capabilities (also called Linux capability), and use CAP ADD to add only those capabilities required for the proper functioning of the container.
- Don’t run containers with –privileged flag, as this type of container will have most of the capabilities available to the underlying host. This flag also overwrites any rules you set using CAP DROP or CAP ADD.
- Don’t mount sensitive host system directories on containers, especially in writable mode could expose them to being changed maliciously in a way that could lead to host compromise.
- Don’t run sshd within containers. By default, the ssh daemon will not be running in a container, and you shouldn’t install the ssh daemon to simplify the security management of the SSH server.
- Don’t map any ports below 1024 within a container as they are considered privileged because they transmit sensitive data. By default, Docker maps container ports to one that’s within the 49153 – 65525 range, but it allows the container to be mapped to a privileged port. As a general rule of thumb, ensure only needed ports are open on the container.
- Don’t share the host’s network namespace, process namespace, IPC namespace, user namespace, or UTS namespace, unless necessary, to ensure proper isolation between Docker containers and the underlying host.
- Specify the amount of memory and CPU needed for a container to operate as designed instead of relying on an arbitrary amount. By default, Docker containers share their resources equally with no limits.
- Set the container’s root filesystem to read-only. Once running, containers don’t need changes to the root filesystem. Any changes made to the root filesystem will likely be for a malicious objective. To preserve the immutable nature of containers – where new containers don’t get patched but rather recreated from a new image – you should not make the root filesystem writable.
- Impose PID limits. One of the advantages of containers is tight process identifier (PID) control. Each process in the kernel carries a unique PID, and containers leverage the Linux PID namespace to provide a separate view of the PID hierarchy for each container. Putting limits on PIDs effectively limits the number of processes running in each container. Limiting the number of processes in the container prevents excessive spawning of new processes and potential malicious lateral movement. Imposing PID limits also prevent fork bombs (processes that continually replicate themselves) and anomalous processes. Mostly, the benefit here is if your service always runs a specific number of processes, then setting the PID limit to that exact number mitigates many malicious actions, including reverse shells and remote code injection – really, anything that requires spawning a new process.
- Don’t configure your mount propagation rules as shared. Sharing mount propagation means that any changes made to the mount will propagate to all instances of that mount. Instead set the mount propagation in slave or private mode so that a necessary change made to a volume isn’t shared with (or propagated to) containers that don’t require that change.
- Don’t use docker exec command with privileged or user=root option, since this setting could give the container extended Linux capabilities
- Don’t use the default bridge “docker0.” Using the default bridge leaves you open to ARP spoofing and MAC flooding attacks. Instead, containers should be on a user-defined network and not the default “docker0” bridge.
- Don’t mount Docker socket inside containers, since this approach would allow a process within the container to execute commands that give it full control of the host.
Dimitris is an Information Technology and Cybersecurity professional with more than 20 years of experience in designing, building and maintaining efficient and secure IT infrastructures.
Among others, he is a certified: CISSP, CISA, CISM, ITIL, COBIT and PRINCE2, but his wide set of knowledge and technical management capabilities go beyond these certifications. He likes acquiring new skills on penetration testing, cloud technologies, virtualization, network security, IoT and many more.