Harden a Docker daemon for production by securing the Docker socket, enabling user namespace remapping, implementing resource constraints, applying least privilege to containers, enabling TLS for remote API access, and integrating with Linux security modules.
Hardening the Docker daemon for production involves a defense-in-depth approach that addresses the Docker socket, daemon configuration, container runtime, and host integration. The Docker daemon (dockerd) runs with root privileges by default and controls all containers, making it a critical security boundary. Production hardening requires restricting access to the daemon, isolating container processes from the host, limiting resource consumption, and enforcing least privilege for all running containers.
The first line of defense is securing the Docker socket. By default, Docker uses a Unix socket at /var/run/docker.sock. This socket grants full administrative control over the Docker daemon, so proper ownership is critical. Set ownership to root:docker and permissions to 660 to ensure only the root user and members of the docker group can interact with it. Verify with ls -l /var/run/docker.sock and apply sudo chown root:docker /var/run/docker.sock and sudo chmod 660 /var/run/docker.sock . Avoid exposing the Docker daemon over HTTP without TLS. If remote access is required, enable TLS with client certificate authentication using the --tlsverify flag .
Configure resource constraints to prevent denial-of-service attacks where a single container consumes all host resources. Set CPU limits with --cpus, memory limits with --memory, and PID limits with --pids-limit at runtime. For example, docker run --cpus="1.5" --memory="512m" --pids-limit=100 limits a container to 1.5 CPU cores, 512MB memory, and 100 processes . These limits can also be set in orchestration tools or as defaults in the daemon.json configuration.
Implement the principle of least privilege for all containers. Never run containers with the --privileged flag unless absolutely necessary, as this gives containers almost all host capabilities . Instead, grant only specific capabilities using --cap-drop=ALL followed by --cap-add=NEEDED_CAP. For example, a web server might need --cap-add=NET_BIND_SERVICE but nothing else. Create a dedicated non-root user in your Dockerfile using RUN useradd -m appuser && USER appuser to avoid running processes as root inside containers .
Use --read-only to mount the container root filesystem as read-only, forcing all writes to designated volumes. Add --tmpfs /tmp for temporary writeable space .
Apply custom seccomp profiles with --security-opt seccomp=/path/to/profile.json to restrict system calls. Docker provides a default profile, but custom profiles can further reduce attack surface .
Enable AppArmor or SELinux on the host and apply profiles to containers. For Debian/Ubuntu, use --security-opt apparmor=your-profile. For CentOS/RHEL, use --security-opt label=type:docker_t .
Drop all Linux capabilities with --cap-drop=ALL and add back only required capabilities like --cap-add=CHOWN or --cap-add=DAC_OVERRIDE based on application needs.
Keep the Docker engine and all base images updated. Subscribe to Docker security announcements and regularly update with apt-get update && apt-get upgrade docker-ce or equivalent. Use automated vulnerability scanning tools like Trivy or Clair in your CI/CD pipeline to scan images before deployment . Enable Docker Content Trust (DCT) by setting export DOCKER_CONTENT_TRUST=1 to ensure only signed images are pulled and run . This prevents man-in-the-middle attacks and ensures image integrity.
Enable Docker daemon logging with appropriate log levels (log-level: info in daemon.json). Configure log rotation to prevent disk exhaustion .
Integrate with Linux audit framework using auditctl to monitor Docker binaries and the socket: auditctl -w /usr/bin/docker -p rwxa -k docker_audit and auditctl -w /var/run/docker.sock -p rwxa -k docker_audit .
Forward Docker logs to a centralized SIEM using tools like Filebeat or Fluent Bit for real-time alerting .