Docker Security

Docker security is a layered concern: the Docker daemon itself (which runs as root), the container runtime isolation, the container images, and application configuration inside containers. The most dangerous default is that the Docker socket (/var/run/docker.sock) grants root-equivalent access to anyone who can reach it. Understanding and addressing this and other defaults is essential before running Docker on production servers.

Docker attack surface

Docker security risks:
  1. Docker socket (/var/run/docker.sock)
     Mounting this gives container full host control
     → Never mount docker.sock in containers unless absolutely necessary

  2. Running as root inside container
     If container escape occurs, attacker has root on host
     → Use USER directive in Dockerfile, run as non-root

  3. Unpatched base images
     Old Alpine/Ubuntu base images have known CVEs
     → Rebuild images regularly, scan with trivy/grype

  4. Exposed Docker API (port 2375/2376)
     Unauthenticated Docker API = full server compromise
     → Never expose port 2375 publicly

Running containers as non-root

# In Dockerfile — add a non-root user:
FROM ubuntu:22.04
RUN useradd -m -u 1001 appuser
WORKDIR /app
COPY . .
RUN chown -R appuser:appuser /app
USER appuser    # Switch to non-root user
CMD ["./myapp"]

# Run existing image as non-root:
docker run --user 1000:1000 nginx:alpine

# Verify user inside container:
docker exec mycontainer id

docker exec id output

uid=1001(appuser) gid=1001(appuser) groups=1001(appuser)
# Good — not running as root (uid=0)

Resource limits

# Without resource limits, one container can starve all others
# Set CPU and memory limits:
docker run -d   --memory="512m"   --memory-swap="512m"   --cpus="0.5"   --name myapp   myapp:latest

# In docker-compose.yml:
# services:
#   app:
#     deploy:
#       resources:
#         limits:
#           cpus: '0.5'
#           memory: 512M
#         reservations:
#           cpus: '0.25'
#           memory: 256M

# Verify limits are applied:
docker inspect myapp | grep -i "memory\|cpu"

Image security

# Scan images for CVEs with Trivy (excellent free scanner):
sudo apt install -y wget
wget https://github.com/aquasecurity/trivy/releases/latest/download/trivy_*_Linux-64bit.deb
sudo dpkg -i trivy_*_Linux-64bit.deb

# Scan an image:
trivy image nginx:latest

trivy scan output

nginx:latest (debian 12.5)

Total: 78 (UNKNOWN: 0, LOW: 36, MEDIUM: 33, HIGH: 8, CRITICAL: 1)
CRITICAL: CVE-2024-XXXX openssl 3.0.x < 3.0.y...
# Use minimal base images (smaller attack surface):
# Instead of: FROM ubuntu:22.04  (1.4GB)
# Use:        FROM alpine:3.19   (5MB)
# Or:         FROM scratch       (empty, for static binaries)
# Or:         FROM distroless/base (Google's minimal runtime images)

Conclusion

Docker security priority list: (1) never expose the Docker socket to containers that don't need it; (2) run application containers as non-root using the USER directive; (3) set memory and CPU limits on every container to prevent resource exhaustion; (4) scan images with Trivy before pushing to production and rebuild regularly to pick up base image security patches. The Docker socket grants root access — treat mounting it with the same caution as giving root credentials.

Need help with Ubuntu administration?

Work directly with Muhammad Irfan Aslam for Ubuntu Server, Linux, cloud, Docker, DevOps, CI/CD, or infrastructure troubleshooting support.

Work with Muhammad Irfan Aslam