Docker Bench for Security es un script oficial del proyecto Docker que revisa un host con Docker frente a las recomendaciones del CIS Docker Benchmark: configuración del sistema, del daemon, permisos de archivos, imágenes y contenedores en ejecución. En este tutorial ejecutarás la auditoría en Ubuntu 24.04, aprenderás a leer el informe, corregirás los avisos con más impacto (auditoría con auditd, opciones del daemon y contenedores endurecidos) y dejarás una auditoría semanal programada con un temporizador de systemd.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Docker Engine instalado desde el repositorio oficial de Docker.
  • git instalado (sudo apt install git).

Paso 1: Descargar Docker Bench for Security

El proyecto también publica una imagen de contenedor, pero lleva años sin actualizarse y sus comprobaciones no coinciden con las versiones actuales de Docker. Lo recomendable es clonar el repositorio y ejecutar el script directamente en el host.

Clona el repositorio en /opt:

sudo git clone https://github.com/docker/docker-bench-security.git /opt/docker-bench-security

Comprueba que el script está disponible y consulta sus opciones:

cd /opt/docker-bench-security
sudo sh docker-bench-security.sh -h
Usage: docker-bench-security.sh [OPTIONS]
...
  -b           optional  Do not print colors
  -h           optional  Print this help message
  -l FILE      optional  Log output in FILE, inside container if run using docker
  -c CHECK     optional  Comma delimited list of specific check(s) id
  -e CHECK     optional  Comma delimited list of specific check(s) id to exclude
  -i INCLUDE   optional  Comma delimited list of patterns within a container or image name to check
  -x EXCLUDE   optional  Comma delimited list of patterns within a container or image name to exclude from check
...

Para actualizar el script más adelante, ejecuta sudo git -C /opt/docker-bench-security pull.

Paso 2: Ejecutar la primera auditoría

El script necesita privilegios de root para leer la configuración del daemon, los permisos de archivos y el estado de los contenedores. Debe ejecutarse desde su propio directorio, porque carga sus bibliotecas y pruebas con rutas relativas:

cd /opt/docker-bench-security
sudo sh docker-bench-security.sh

La auditoría tarda entre unos segundos y un par de minutos, según el número de contenedores e imágenes. La salida se organiza por secciones del benchmark:

[INFO] 1 - Host Configuration
[INFO] 1.1 - Linux Hosts Specific Configuration
[WARN] 1.1.1 - Ensure a separate partition for containers has been created (Automated)
[WARN] 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)
...
[INFO] 2 - Docker daemon configuration
[WARN] 2.2 - Ensure network traffic is restricted between containers on the default bridge (Scored)
...
Section C - Score

[INFO] Checks: 117
[INFO] Score: 4

Al terminar, el script guarda una copia del informe en log/docker-bench-security.log y otra en formato JSON en log/docker-bench-security.log.json, dentro de /opt/docker-bench-security.

Paso 3: Interpretar los resultados

Cada comprobación se marca con uno de estos estados:

EstadoSignificado
PASSLa configuración cumple la recomendación.
WARNNo cumple la recomendación. Revísala y decide si aplica a tu caso.
INFODato informativo o comprobación que requiere revisión manual.
NOTERecomendación que el script no puede evaluar automáticamente.

El Score final suma un punto por cada PASS y resta uno por cada WARN en las comprobaciones puntuables. No hace falta llegar a cero avisos: algunas recomendaciones no tienen sentido en un único servidor (por ejemplo, las de Docker Swarm) o chocan con tu arquitectura. Lo importante es entender cada aviso y corregir los que reducen riesgo real.

Para ver un resumen rápido de los avisos del último informe:

grep '\[WARN\]' /opt/docker-bench-security/log/docker-bench-security.log

Puedes limitar la auditoría a secciones concretas con -c. Los nombres de grupo disponibles incluyen host_configuration, docker_daemon_configuration, docker_daemon_files, container_images, container_runtime y docker_security_operations:

sudo sh docker-bench-security.sh -c docker_daemon_configuration

Paso 4: Activar la auditoría de archivos de Docker con auditd

La sección 1.1 pide registrar con auditd cualquier cambio en los binarios, la configuración y los datos de Docker, para que quede rastro de quién los modificó. Instala auditd:

sudo apt install auditd

Crea un archivo de reglas específico para Docker:

sudo nano /etc/audit/rules.d/docker.rules
-w /usr/bin/dockerd -k docker
-w /usr/bin/containerd -k docker
-w /usr/bin/runc -k docker
-w /var/lib/docker -k docker
-w /etc/docker -k docker
-w /usr/lib/systemd/system/docker.service -k docker
-w /usr/lib/systemd/system/docker.socket -k docker
-w /etc/containerd/config.toml -k docker
-w /run/containerd -k docker

Carga las reglas y comprueba que están activas:

sudo augenrules --load
sudo auditctl -l | grep docker
-w /usr/bin/dockerd -p rwxa -k docker
-w /usr/bin/containerd -p rwxa -k docker
...

A partir de ahora puedes consultar los eventos con sudo ausearch -k docker.

Paso 5: Endurecer el daemon de Docker

La sección 2 revisa las opciones del daemon. Cuatro de ellas se corrigen con daemon.json y apenas afectan a un servidor típico:

  • icc: false: los contenedores de la red bridge por defecto no pueden comunicarse entre sí. Las redes creadas por Docker Compose o con docker network create no se ven afectadas.
  • live-restore: true: los contenedores siguen funcionando mientras reinicias o actualizas el daemon. No es compatible con Docker Swarm.
  • no-new-privileges: true: los procesos de los contenedores no pueden ganar privilegios con binarios setuid.
  • userland-proxy: false: el reenvío de puertos se hace con reglas del kernel en lugar del proceso docker-proxy.

Además, conviene limitar el tamaño de los logs para que no llenen el disco.

Abre el archivo de configuración del daemon (créalo si no existe; si ya tiene contenido, añade las claves sin duplicarlas):

sudo nano /etc/docker/daemon.json
{
  "icc": false,
  "live-restore": true,
  "no-new-privileges": true,
  "userland-proxy": false,
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Valida el archivo antes de reiniciar para no dejar Docker parado por un error de sintaxis:

sudo dockerd --validate --config-file /etc/docker/daemon.json
configuration OK

Reinicia Docker y comprueba que las opciones están aplicadas:

sudo systemctl restart docker
docker info --format 'Live restore: {{.LiveRestoreEnabled}}  Log driver: {{.LoggingDriver}}'
Live restore: true  Log driver: local

El cambio de driver de logs solo se aplica a los contenedores creados a partir de ahora; recrea los existentes (por ejemplo con docker compose up -d --force-recreate) para que lo usen.

Vuelve a ejecutar la sección del daemon para confirmar la mejora:

cd /opt/docker-bench-security
sudo sh docker-bench-security.sh -c docker_daemon_configuration

Paso 6: Ejecutar contenedores endurecidos

La sección 5 (container_runtime) evalúa cada contenedor en ejecución: si usa un sistema de archivos de solo lectura, si limita memoria y CPU, si elimina capacidades del kernel, si expone puertos en todas las interfaces, etc. La corrección se hace al crear el contenedor, no en el host.

Este ejemplo lanza Nginx con la imagen sin privilegios (escucha en el puerto 8080 como usuario no root) aplicando las restricciones que pide el benchmark:

docker run -d --name web-hardened \
  --read-only --tmpfs /tmp \
  --cap-drop ALL \
  --security-opt no-new-privileges:true \
  --memory 256m --cpus 0.5 --pids-limit 100 \
  --restart on-failure:5 \
  -p 127.0.0.1:8080:8080 \
  nginxinc/nginx-unprivileged:stable-alpine

Comprueba que responde:

curl -sI http://127.0.0.1:8080 | head -n 1
HTTP/1.1 200 OK

Audita solo este contenedor con -i, que filtra por nombre de contenedor o imagen:

cd /opt/docker-bench-security
sudo sh docker-bench-security.sh -c container_runtime -i web-hardened

La mayoría de comprobaciones de la sección 5 deberían aparecer ahora como PASS. Si usas Docker Compose, las mismas opciones existen como claves del servicio: read_only, tmpfs, cap_drop, security_opt, pids_limit, mem_limit y cpus.

Elimina el contenedor de prueba cuando termines:

docker rm -f web-hardened

Paso 7: Programar una auditoría semanal

Una auditoría puntual pierde valor en cuanto cambias algo. Un temporizador de systemd ejecuta el script cada semana y guarda el informe en /var/log/docker-bench, donde puedes compararlo con el anterior.

Crea el servicio:

sudo nano /etc/systemd/system/docker-bench.service
[Unit]
Description=Docker Bench for Security audit
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
WorkingDirectory=/opt/docker-bench-security
LogsDirectory=docker-bench
ExecStart=/usr/bin/sh docker-bench-security.sh -b -l /var/log/docker-bench/docker-bench.log

LogsDirectory hace que systemd cree /var/log/docker-bench con los permisos correctos, y -b quita los códigos de color del informe.

Crea el temporizador:

sudo nano /etc/systemd/system/docker-bench.timer
[Unit]
Description=Weekly Docker Bench for Security audit

[Timer]
OnCalendar=Mon *-*-* 04:00:00
RandomizedDelaySec=30min
Persistent=true

[Install]
WantedBy=timers.target

Activa el temporizador y lanza una ejecución manual para probarlo:

sudo systemctl daemon-reload
sudo systemctl enable --now docker-bench.timer
sudo systemctl start docker-bench.service

Comprueba el resultado y la próxima ejecución programada:

grep -E 'Checks:|Score:' /var/log/docker-bench/docker-bench.log
systemctl list-timers docker-bench.timer
[INFO] Checks: 117
[INFO] Score: 21
NEXT                        LEFT     LAST                        PASSED  UNIT               ACTIVATES
Mon 2026-09-28 04:12:41 UTC 2 days   Fri 2026-09-25 10:03:12 UTC 1min ago docker-bench.timer docker-bench.service

Si algo falla, revisa la ejecución con journalctl -u docker-bench.service. El informe en JSON (/var/log/docker-bench/docker-bench.log.json) es el más cómodo para enviarlo a un sistema de monitorización o comparar puntuaciones entre semanas.

Solución de problemas

./functions/...: not found o errores al cargar pruebas: estás ejecutando el script desde otro directorio. Haz cd /opt/docker-bench-security antes de lanzarlo, o define WorkingDirectory en la unidad de systemd.

Docker no arranca tras editar daemon.json: revisa sudo journalctl -u docker -n 50. Suele ser un error de sintaxis JSON o una clave duplicada que también se pasa como argumento en la unidad de systemd. Valida siempre con dockerd --validate antes de reiniciar.

Contenedores de la red bridge por defecto dejan de verse entre sí: es el efecto de icc: false. Mueve esos contenedores a una red definida por el usuario (docker network create) o a un proyecto de Docker Compose, que crea su propia red.

Conclusión

Has auditado Docker con Docker Bench for Security, has registrado los cambios de Docker con auditd, has endurecido el daemon y un contenedor de ejemplo, y has programado una auditoría semanal. Como siguientes pasos, aplica las opciones de endurecimiento a tus archivos de Compose de producción, revisa las imágenes con un escáner de vulnerabilidades como Trivy y considera userns-remap o Docker en modo rootless en servidores nuevos.