Docker containers have been an essential part of the developer's toolbox for several years now. They allow developers to build, distribute, and deploy their applications in a standardized way.
However, this widespread adoption brings about significant security challenges. With containers becoming a potential attack surface, it's imperative to adopt robust security measures.
In this guide, we'll explore Docker Security Best Practices, ensuring your deployments are secure and resilient against threats. Plus, we've included a comprehensive cheat sheet to help you quickly implement these practices.
Download the Docker security cheatsheet
Understanding Docker Security Fundamentals
Docker secures applications by encapsulating them within containers, but this isolation isn't foolproof. Misconfigurations and misconceptions around container isolation can lead to vulnerabilities. It's crucial to understand that container security is built on the supporting infrastructure, software components, and runtime configurations.
The key here is that containers don’t have any security dimension by default. Their security completely depends on:
- the supporting infrastructure (OS and platform)
- their embedded software components
- their runtime configuration
Container security represents a broad topic, but the good news is that many best practices are low-hanging fruits one can harvest to quickly reduce the attack surface of their deployments.
Top Docker Security Best Practices
Image Security
Use Trusted Images
Ensure you're using images from trusted sources, like Docker Official Images. Alpine Linux is a recommended base due to its minimalistic nature.
Example:
docker pull python:3.9-alpine
Unprivileged User
By default, the process inside a container is run as root (id=0). Run containers with a non-root user to limit access levels.
Example:
FROM base_image
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
User ID Namespace
Segregate namespaces to prevent container privilege escalation from affecting the host.
By default, the Docker daemon uses the host's user ID namespace. As a result, any successful privilege escalation inside a container would also grant root access to both the host and other containers.
To mitigate this risk, configure your host and the Docker daemon to use a separate namespace with the --userns-remap
option.
Container Runtime Security
Forbid New Privileges
Your container should never run as privileged, as this would grant it all root capabilities on the host machine.
For enhanced security, it is recommended to explicitly forbid the addition of new privileges after container creation using this option: --security-opt=no-new-privileges
.
Define Fine-grained Capabilities
By default, your containers run with a set of enabled capabilities, many of which you likely don't need.
It's recommended to explicitly specify required Linux capabilities, dropping unnecessary ones.
For example, a web server would probably only need the NET_BIND_SERVICE
capability to bind the port 80.
Control Group Utilization
Limit CPU and memory usage using cgroups
to prevent a single container from overwhelming host resources.
Example:
docker run --memory="400m" --cpus="0.5"
Control Groups are the mechanisms used to control access to CPU, memory, and disk I/O for each container. By default, a container is associated with a dedicated cgroup
, but if the option --cgroup-parent
is present, you are putting the host resources at risk of a DoS attack because you are allowing shared resources between the host and the container.
Host System Security
Sensitive Filesystem Parts
Avoid sharing sensitive parts of the host filesystem, such as root (/), device (/dev), process (/proc), and virtual (/sys) mount points.
If you need access to host devices, be careful to selectively enable the access options with the [r|w|m]
flags (read
, write
, and use mknod
).
Container Filesystem Access
Containers should have read-only access to the host filesystem, and sensitive data should be securely handled. Use a temporary filesystem for non-persistent data.
Example:
docker run --read-only --tmpfs /tmp:rw ,noexec,nosuid
Persistent Storage
If you need to share data with the host filesystem or other containers, you have two options:
- Create a bind mount with limited useable disk space (
--mount type=bind,o=size
) - Create a bind volume for a dedicated partition (
--mount type=volume
)
In either case, if the shared data doesn’t need to be modified by the container, use the read-only option.
Network Security
Docker Daemon Socket
The UNIX socket used by the Docker should not be exposed: /var/run/docker.sock
Giving access to it is equivalent to giving unrestricted root access to your host.
Custom Network Bridges
Avoid Docker's default docker0
network bridge; create custom networks for container isolation.
When a container is created, Docker connects it to the docker0
network by default. Therefore, all containers are connected to docker0
and are able to communicate with each other. Example:
docker network create custom_network
docker run --network=custom_network
Here is a simple example: you have a container hosting a web server that needs to connect to a database running in another container.
The secure way to connect these two containers involves creating two bridge networks:
- A
WEB
bridge network to route incoming traffic from the host network interface. - A
DB
bridge network used exclusively to connect the database and web containers.
This approach ensures a more secure and isolated communication between the containers.
Implementing Docker Security Tools
Numerous tools can aid in securing Docker environments:
- Vulnerability Scanners like Trivy or Snyk.
- Secret Scanning Tools like ggshield to detect hard-coded secrets.
Common Docker Security Pitfalls and How to Avoid Them
- Using Unverified Images: Always opt for official or verified resource images.
- Over-exposed Ports and Sockets: Validate the necessity of port exposures and avoid unnecessary permissions.
- Environment Variable Mismanagement: Secure sensitive information using Docker secrets or environment variables securely.
Unfortunately, public Docker images often leak secrets. Did you know that 8.5% of Docker images expose API and private keys?
It is evident that scanning images for hidden secrets, whether they are self-created or supplied by third parties, has become crucial in protecting against supply chain attacks.
- Scan Dockerfile, build args and Docker image layers’ filesystem
- Integrate with your CI/CD pipelines
- Find 450+ types of secrets and sensitive files
FAQ
Is Docker good for security?
Docker can be a useful tool for security if used correctly, but it's not a security solution in and of itself. Docker's containerization technology can help to isolate and contain potentially vulnerable applications, but it's important to remember that Docker images can still contain security vulnerabilities or be misconfigured. Here are some ways Docker can help with security:1. Isolation: Docker containers provide a level of isolation between applications running on the same server or host. This means that if one container is compromised, it's less likely that the attack will spread to other containers or the host itself.
2. Portability: Docker images can be easily moved between environments, making it easier to deploy applications consistently and securely.
3. Versioning: Docker images can be versioned and tracked, making it easier to roll back to a known-good state if a security issue is discovered.
4. Resource limits: Docker allows you to set resource limits for each container, preventing one container from consuming all available resources and affecting other containers or the host itself.
5. Image scanning: Docker images can be scanned for known vulnerabilities before deployment, helping to prevent the deployment of potentially vulnerable applications.
It's important to note, however, that Docker is just one part of a comprehensive security strategy. Other security measures, such as network security, access controls, and monitoring, should also be implemented to fully protect your applications and infrastructure.
How do I ensure Docker security?
If you are looking for the best practices regarding Docker security, you are in the right place! Here is a helpful summary of the security measures we mentionned:1. Use official images: Use official images from Docker Hub or other trusted repositories rather than third-party images.
2. Keep your images up to date: Regularly update your Docker images to ensure that they have the latest security patches.
3. Use strong passwords: Use strong and unique passwords for all services running inside the container.
4. Limit container privileges: Limit container privileges by running containers as non-root users.
5. Avoid running unnecessary services: Avoid running unnecessary services inside the container, as this can create security vulnerabilities.
6. Use network segmentation: Use network segmentation to separate your containers from the host system and other containers.
7. Enable Docker Content Trust: Enable Docker Content Trust to ensure the integrity of your images.
8. Use a firewall: Use a firewall to restrict network access to your containers.
9. Monitor container activity: Monitor container activity for signs of suspicious behavior.
10. Regularly audit container configurations: Regularly audit container configurations to ensure they adhere to security best practices.
What is the best practice for handling secrets in Docker?
Handling secrets in Docker involves securely managing sensitive information such as passwords, API keys, and other credentials that are required by applications running inside Docker containers. Here are some best practices for handling secrets in Docker:1. Use environment variables: One way to handle secrets is to use environment variables to store sensitive information. You can pass the values of these variables to your Docker containers at runtime, without exposing them in your Dockerfile or on your host system.
2. Use Docker secrets: Docker provides a built-in secrets management system that allows you to securely store and manage sensitive information. You can use the Docker CLI or Docker Compose to create and manage secrets, and then pass them to your containers at runtime.
3. Use third-party secrets management tools: There are also third-party secrets management tools that you can use to securely store and manage sensitive information. These tools typically provide features such as encryption, access control, and audit logging.
4. Keep secrets out of source control: To avoid exposing secrets to unauthorized users, never store them in source control systems such as Git.
5. Use multi-stage builds: When building Docker images, use multi-stage builds to separate build-time dependencies from runtime dependencies. This helps reduce the risk of exposing secrets during the build process.
6. Implement access controls: Implement access controls to restrict access to secrets only to authorized users.
Which tag to use for Docker Hub images, latest or a fixed tag?
First, you should understand that Docker tagging works from less to more specific, that’s the reason why : ``` python:3.9.6-alpine3.14 python:3.9.6-alpine python:3.9-alpine python:alpine ``` all refer to the same image (at the moment of writing) for example. By being very specific and pinning down a version, you are shielding yourself from any future breaking change. On the other hand, using the latest version ensures that more vulnerabilities are patched. **This is a tradeoff**, but pinning to a stable release is what is generally recommended. Considering that, we would pick `python:3.9-alpine` here. The same applies to packages installed during the build process of your image.How to use the ENV directive in a Dockerfile?
It's important to remember that the ENV directive will allow the environment variable to be read from the container at any moment. Therefore, you should never include sensitive information in an ENV directive! For example, unsetting a secret like this is unsafe: ```Dockerfile ENV $SECRET RUN unset $SECRET ``` In this example, $SECRET will be readable in the containers at runtime, posing a security risk. To prevent runtime read access, use a single RUN command to set and unset the variable in a single layer (don't forget the variable can still be extracted from the image). ```Dockerfile RUN export ADMIN_USER="admin" \ && ... \ && unset ADMIN_USER ``` More idiomatically, use the ARG directive (ARG values are not available after the image is built).Before you go
Enhance your container security strategy by trying out tools like ggshield for seamless secret detection. Join 300,000+ developers, ensuring their secrets are well-protected.
Download the full Docker Security Cheat Sheet and give your containerization strategy a fortified edge.
Ensure your Docker environments are robust, secure, and prepared to handle the shifting threat landscape. Let's build secure applications together!
Consider exploring related posts for deep dives: