Enhancing Docker Security: Best Practices for Hardening
Docker has become increasingly popular due to its containerization capabilities. However, as with any widely used platform, there is an increased risk of security threats. Therefore, it is crucial to implement the necessary measures to secure your Docker environment. In this article, we will explore some best practices for hardening Docker and improving its overall security.
1. Run Docker as an Unprivileged User: Enabling Rootless Mode
Running Docker in rootless mode offers several advantages. It enhances the security of your system by limiting the privileges of the Docker daemon. Additionally, managing Docker containers and images becomes easier as you no longer need to run Docker commands as the root user. Running Docker in rootless mode ensures both security and ease of management.
To run Docker in rootless mode, follow these steps:
- Install Docker and its dependencies:
sudo apt-get update
sudo apt-get install -y docker.io
2. Create a new group called “docker”:
sudo groupadd docker
3. Add your user account to the “docker” group:
sudo usermod -aG docker $USER
4. Log out and log back in for the changes to take effect.
5. Test that Docker is running in rootless mode by running a container:
docker run hello-world
If Docker runs correctly in rootless mode, it should output “Hello from Docker!” This means that your user account now has the necessary permissions to interact with the Docker daemon, and you no longer need to run the “docker” command with sudo.
2. Set the Container’s User
Setting the container’s user is crucial for securing your Docker environment. By default, Docker containers run as the root user, which can pose a security risk. To set the container’s user, you can use the “USER” directive in your Dockerfile.
There are multiple ways to set the user for a container in Docker:
- Using the “-u” flag when running containers:
docker run -u 1001 nginx
This sets the user for the nginx container to user ID 1001.
2. When building the Docker image, use the “USER” Dockerfile directive:
FROM nginx
RUN useradd -r -u 1001 myuser
USER myuser
This creates a new user, “myuser,” with UID 1001 and sets it as the user for the container.
3. Enable user namespace support in the Docker daemon to map the user ID inside the container to a different user ID outside the container. This enhances security by mapping the user ID inside the container to a non-root user ID outside the container.
Setting the container’s user as an unprivileged user helps prevent privilege escalation attacks. However, additional security measures, such as restricting container capabilities and monitoring container activity, are essential for comprehensive security.
3. Do Not Expose the Docker Daemon Socket
Exposing the Docker daemon socket can lead to severe security risks, as it allows attackers to gain complete control over your Docker environment. Therefore, it is crucial to ensure that the Docker daemon socket is not exposed, even to the containers.
To prevent the exposure of the Docker daemon socket, follow these steps:
- Configure the Docker daemon to use a Unix socket instead of a TCP socket by modifying the Docker daemon configuration file (/etc/docker/daemon.json) with the following lines:
{
"hosts": ["unix:///var/run/docker.sock"]
}
This configuration sets the Docker daemon to use the Unix socket at /var/run/docker.sock.
2. Make sure that the Docker daemon socket is not mounted into any containers. Inspect the “docker run” command
to ensure that the “-v /var/run/docker.sock:/var/run/docker.sock” flag is not present.
Additionally, configure your Docker environment to use secure networking protocols, such as TLS, to prevent attackers from intercepting and manipulating network traffic.
You can verify if a container is already running with a specific configuration by using the following command:
docker inspect - format='{{.HostConfig.Binds}}' [container id]
Following these best practices will help ensure that your Docker environment is secure and protected against common threats.
4. Add the — no-new-privileges Flag
The — no-new-privileges flag is an important security feature that prevents privilege escalation attacks. When set, this flag restricts Docker containers from gaining additional privileges beyond what was granted at initialization.
You can add the — no-new-privileges flag to a Docker container in the following ways:
- When running the container:
docker run - no-new-privileges nginx
2. In the Dockerfile:
FROM nginx
CMD ["nginx", "-g", "daemon off;", " - no-new-privileges"]
3. In the Docker Compose file:
version: "3"
services:
nginx:
image: nginx
command: ["nginx", "-g", "daemon off;"]
security_opt:
- no-new-privileges
Adding the — no-new-privileges flag is a simple yet effective way to prevent privilege escalation attacks and enhance the security of your Docker environment.
5. Set Filesystem and Volumes to Read-only
Setting the filesystem and volumes to read-only helps prevent unauthorized modifications to your Docker environment, especially in production environments where security is a top priority.
To set the filesystem and volumes to read-only, follow these steps:
- Create a Docker container with a read-only filesystem by adding the “ — read-only” flag to the “docker run” command:
docker run - read-only -it alpine /bin/sh
2. Set the volumes to read-only by adding the “ — read-only” flag to the “docker run” command:
docker run - read-only -v /data:/data:ro -it alpine /bin/sh
3. Ensure that your application can function correctly with a read-only filesystem and volumes.
Setting the filesystem and volumes to read-only is a simple yet effective way to improve the security of your Docker environment and prevent unauthorized modifications to your containers and data.
6. Limit Capabilities
To prevent unauthorized access to your Docker environment, it is crucial to limit capabilities. Capabilities are special privileges granted to processes running on the host system, and by default, all Docker containers have access to all capabilities.
Follow these steps to limit capabilities:
1. Identify the capabilities required by your container. Run the container with the “ — cap-add=ALL” flag and inspect the capabilities listed in the output of the “capsh — print” command.
2. Modify your Dockerfile to include only the required capabilities. For example, if your container requires only the “NET_ADMIN” and “SYS_TIME” capabilities, add the following lines to your Dockerfile:
FROM ubuntu
RUN apt-get update && apt-get install -y nginx
USER nginx
CMD ["nginx", " - cap-add=NET_ADMIN", " - cap-add=SYS_TIME", " - security-opt=no-new-privileges"]
3. Build and run your Docker image as usual. Your container will now have access only to the specified capabilities, preventing unauthorized access and improving the security of your Docker environment.
Limiting capabilities is a powerful but complex security measure. It is important to thoroughly test your container to ensure it has all the required capabilities for correct functionality.
7. Add HEALTHCHECK to the Docker Image
Adding a HEALTHCHECK to your Docker image is crucial to ensure your application runs correctly. A HEALTHCHECK is a command that Docker runs to check the status of your application. If the command returns a non-zero exit code, Docker considers the container unhealthy.
To add a HEALTHCHECK to your Docker image, follow these steps:
1. Choose a command that checks the health of your application. This can be a simple HTTP GET request or a more complex command that checks the status of your application’s components.
2. Add the HEALTHCHECK directive to your Dockerfile. For example, to use an HTTP GET request to check the health of your application, add the following line to your Dockerfile:
HEALTHCHECK CMD curl - fail http://localhost:8080 || exit 1
This command makes a GET request to http://localhost:8080 and returns a non-zero exit code if the request fails.
3. Build and run your Docker image. Docker will automatically run the HEALTHCHECK command and update the container’s status accordingly.
Adding a HEALTHCHECK to your Docker image is a simple yet effective way to ensure your application runs correctly and detects any issues before they become critical.
8. Use Linux Security Modules (seccomp, AppArmor, or SELinux)
Linux Security Modules (LSMs) such as seccomp, AppArmor, or SELinux provide an additional layer of security for Docker environments by restricting the system calls that a container can make. This helps prevent attackers from executing malicious code or compromising the host system.
To use an LSM with Docker, follow these steps:
1. Choose an LSM compatible with your Linux distribution and Docker version. For example, Ubuntu supports AppArmor by default, while seccomp and SELinux may require additional configuration.
2. Configure the LSM to restrict the system calls that Docker containers can make. This can be done by creating a profile or policy that specifies the allowed and disallowed system calls. For example, an AppArmor profile may include the following lines:
/usr/bin/dockerd flags=(complain) {
# Allow read-only access to the host filesystem
/ r,
/sys/ r,
/sys/fs/cgroup/ r,
# Allow network access only to specified ports
network tcp,
network udp,
network inet dgram,
network inet6 dgram,
network inet stream,
network inet6 stream,
network raw,
network unix,
network packet,
network netlink,
# Deny all other system calls
deny /**,
}
3. Apply the profile or policy to your Docker containers by adding the “ — security-opt” flag to the “docker run” command and specifying the name of the profile or policy:
docker run - security-opt apparmor=docker-profile -it ubuntu /bin/bash
Using an LSM with Docker helps prevent attackers from exploiting vulnerabilities in your containers and compromising the host system.
Follow CIS Guidelines for Hardening: https://downloads.cisecurity.org/#/
By following these Docker hardening best practices, you can significantly improve the security of your Docker environment and protect against potential security threats.
#security #dockersecurity #cybersecurity #cloudsecurity #baselineconfig #aws #azure #gcp #infosec