Introduction
Hey! I'm Bobby, a Docker Captain and the author of the free Introduction to Docker eBook. In this article, we'll cover 11 essential Docker security tips to help you protect your containerized applications.
Prerequisites
To follow this guide, you should have:
- Docker installed on your system
- Basic knowledge of Docker commands and concepts
1. Keep Docker Updated
Regularly updating your Docker engine is crucial for maintaining the security of your containerized environment. Each new release of Docker often includes security patches and fixes for vulnerabilities discovered in previous versions.
To update Docker on a Ubuntu system, you can use the following commands:
sudo apt-get update
sudo apt-get upgrade docker-ce
After updating, it's a good practice to restart the Docker daemon:
sudo systemctl restart docker
Make sure to test your applications after updating Docker to ensure compatibility with the new version.
2. Use Official Images
Official images from Docker Hub are maintained by the Docker team and the original software maintainers. They follow best practices for Docker image creation and are regularly updated with security patches.
When using official images in your docker-compose.yml
file, always specify a specific version tag rather than using latest
:
version: '3.8'
services:
web:
image: nginx:1.21.3 # Specific version of the official Nginx image
Using specific version tags ensures reproducibility and prevents unexpected changes when rebuilding your containers.
3. Scan Images for Vulnerabilities
Regularly scanning your Docker images for vulnerabilities is essential for maintaining a secure environment. Docker Scout is a powerful tool integrated into Docker Desktop and the Docker CLI for this purpose.
To scan an image using Docker Scout:
docker scout cve <image_name>:<tag>
For example:
docker scout cve nginx:1.21.3
This command will provide a detailed report of any known vulnerabilities in the image, including their severity levels and available fixes.
You can also integrate Docker Scout into your CI/CD pipeline. Here's an example of how you might do this in a GitHub Actions workflow:
name: Docker Image CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build the Docker image
run: docker build . --file Dockerfile --tag myapp:${GITHUB_SHA}
- name: Scan the Docker image
run: docker scout cve myapp:${GITHUB_SHA}
This workflow builds your Docker image and then scans it for vulnerabilities on every push to the repository.
4. Limit Container Resources
Setting Docker resource limits on your containers is crucial for preventing Denial of Service (DoS) attacks and ensuring fair resource allocation in multi-container environments.
In your docker-compose.yml
file, you can set these limits as follows:
version: '3.8'
services:
web:
image: nginx:1.21.3
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
This configuration limits the web
service to use a maximum of 0.5 CPU cores and 512MB of memory, while reserving a minimum of 0.25 CPU cores and 256MB of memory.
You can also set these limits when running a container using the Docker CLI:
docker run -d --name myapp --cpus=0.5 --memory=512m nginx:1.21.3
By setting these limits, you prevent a single container from consuming all available resources on the host, which could affect other containers or the host system itself.
5. Use Non-Root Users
Running containers as root is a significant security risk. If an attacker manages to break out of the container, they would have root access to the host system. To mitigate this risk, create a non-root user in your Dockerfile and switch to this user.
Here's an example Dockerfile that creates a non-root user:
FROM node:18
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm ci --only=production
# Bundle app source
COPY . .
# Create a non-root user and switch to it
RUN groupadd -r myapp && useradd -r -g myapp myuser
USER myuser
EXPOSE 8080
CMD [ "node", "server.js" ]
In this Dockerfile, we create a new user myuser
and a group myapp
, then switch to this user before running the application. This ensures that the application runs with limited permissions.
When running the container, you can also use the --user
flag to specify a non-root user:
docker run -d --name myapp --user 1000:1000 myimage
This runs the container as the user with UID 1000 and GID 1000.
6. Implement Secret Management
Storing sensitive information like passwords, API keys, and other secrets directly in your Dockerfile or Docker Compose file is a security risk. Instead, use Docker secrets for managing this sensitive data.
First, create a secret:
echo "mysecretpassword" | docker secret create db_password -
Then, in your docker-compose.yml
file, you can use this secret:
version: '3.8'
services:
db:
image: mysql:8.0
secrets:
- db_password
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
external: true
In this example, MySQL will read the root password from the file /run/secrets/db_password
inside the container, which contains the secret we created.
For non-swarm mode, you can use environment variables and a .env
file:
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
And in your .env
file (which should be added to .gitignore
):
DB_PASSWORD=mysecretpassword
This approach keeps secrets out of your version-controlled files while still allowing easy configuration.
7. Enable Content Trust
Docker Content Trust (DCT) allows you to verify the integrity and the publisher of all the data received from a registry over any channel. When DCT is enabled, you can be sure that the image you're pulling is the one that was pushed, without any tampering.
To enable DCT, set the DOCKER_CONTENT_TRUST
environment variable:
export DOCKER_CONTENT_TRUST=1
With DCT enabled, when you push an image, Docker signs it:
docker push myrepo/myimage:latest
When pulling images with DCT enabled, Docker will only pull signed images:
docker pull myrepo/myimage:latest
If the image isn't signed or the signature doesn't match, the pull will fail.
You can also enable DCT in your Docker daemon configuration file (/etc/docker/daemon.json
):
{
"content-trust": {
"mode": "enforced"
}
}
This enforces DCT for all operations on the Docker daemon.
8. Use Read-Only Containers
Running containers in read-only mode prevents attackers from making changes to the container's filesystem, even if they manage to execute arbitrary code within the container.
In your docker-compose.yml
file, you can set a container to read-only mode like this:
version: '3.8'
services:
web:
image: nginx:1.21.3
read_only: true
tmpfs:
- /tmp
- /var/cache/nginx
The tmpfs
mounts are necessary for Nginx to function correctly, as it needs to write to these directories. These are temporary filesystems that exist only in memory.
When using the Docker CLI, you can use the --read-only
flag:
docker run -d --name myapp --read-only nginx:1.21.3
For applications that need to write data, you can use volumes or bind mounts for specific directories that need write access, while keeping the rest of the filesystem read-only.
9. Implement Network Segmentation
Network segmentation in Docker helps to isolate containers and reduce the attack surface. By default, all containers on a Docker network can communicate with each other. However, you can create separate networks for different groups of containers.
Here's an example docker-compose.yml
file that implements network segmentation:
version: '3.8'
services:
frontend:
image: nginx:1.21.3
networks:
- frontend
backend:
image: app:latest
networks:
- backend
db:
image: mysql:8.0
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
In this setup, the frontend
service cannot communicate directly with the db
service, as they're on different networks. The backend
service acts as a bridge between the two.
You can also create and manage networks using the Docker CLI:
# Create networks
docker network create frontend
docker network create backend
# Run containers on specific networks
docker run -d --name nginx --network frontend nginx:1.21.3
docker run -d --name app --network frontend --network backend app:latest
docker run -d --name mysql --network backend mysql:8.0
This network segmentation adds an extra layer of security by limiting the potential spread of an attack if one container is compromised.
10. Regular Security Audits
Regular security audits are crucial for maintaining the security of your Docker environment. Docker Bench for Security is a script that checks for dozens of common best practices around deploying Docker containers in production.
To run Docker Bench for Security:
docker run -it --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-security
This command runs a series of checks and provides a report with recommendations for improving your Docker security posture.
It's a good practice to run this audit regularly, such as weekly or after any significant changes to your Docker environment. You can automate this process by integrating it into your CI/CD pipeline or setting up a cron job.
Here's an example of how you might integrate Docker Bench into a GitHub Actions workflow:
name: Docker Security Audit
on:
schedule:
- cron: '0 0 * * 0' # Run weekly on Sunday at midnight
jobs:
audit:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Run Docker Bench for Security
run: |
docker run --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-security
This workflow runs Docker Bench for Security every week and provides a report in the GitHub Actions log.
11. Implement Logging and Monitoring
Proper logging and monitoring are essential for detecting and responding to security incidents in your Docker environment. Docker provides built-in logging mechanisms that you can configure for each container:
In your docker-compose.yml
file, you can set up logging like this:
version: '3.8'
services:
web:
image: nginx:1.21.3
logging:
driver: "json-file"
options:
max-size: "200m"
max-file: "10"
This configuration uses the json-file
logging driver, which is the default. It sets a maximum size of 200MB for each log file and keeps up to 10 log files.
For more advanced logging, you can use the syslog
driver to send logs to a central syslog server:
logging:
driver: syslog
options:
syslog-address: "udp://192.168.0.42:1234"
For monitoring, consider using tools like Prometheus and Grafana. Here's an example docker-compose.yml
that sets up a basic monitoring stack:
version: '3.8'
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
depends_on:
- prometheus
ports:
- "3000:3000"
You'll need to configure Prometheus to scrape metrics from your Docker daemon and containers. Here's a basic prometheus.yml
configuration:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'docker'
static_configs:
- targets: ['docker.for.mac.localhost:9323']
To enable Docker metrics, you need to configure your Docker daemon. Add the following to /etc/docker/daemon.json
:
{
"metrics-addr" : "0.0.0.0:9323",
"experimental" : true
}
Remember to restart the Docker daemon after making these changes.
With this setup, you can create dashboards in Grafana to visualize your Docker metrics and set up alerts for any suspicious activity.
Conclusion
Security is an ongoing process that requires regular attention and updates!
These 11 security tips will significantly help you with your container security and Docker environment but you should always keep an eye on the latest security best practices and updates.
For more information on Docker, check out my free Introduction to Docker eBook.
If you're looking for a reliable platform to practice these security tips, consider using DigitalOcean. They offer a $200 free credit to get you started.