Seguridad Docker: Mejores Prácticas - Guía Completa para Producción

La seguridad de contenedores Docker es crítica para proteger aplicaciones, datos e infraestructura. Esta guía completa cubre el endurecimiento de seguridad de Docker, gestión de vulnerabilidades, protección en tiempo de ejecución y mejores prácticas de cumplimiento para entornos de producción.

Tabla de Contenidos

Introducción

La seguridad de contenedores requiere un enfoque de múltiples capas que cubra imágenes, configuración en tiempo de ejecución, redes, secretos y el daemon de Docker mismo. Las prácticas de seguridad adecuadas previenen accesos no autorizados, violaciones de datos y abuso de recursos.

Superficie de Ataque de Seguridad

  • Imágenes: Dependencias vulnerables y malware
  • Tiempo de Ejecución: Escalación de privilegios y escape de contenedores
  • Red: Acceso no autorizado e interceptación de datos
  • Secretos: Credenciales expuestas y claves API
  • Daemon: Exposición del socket Docker y configuraciones incorrectas

Principios de Seguridad

Mínimo Privilegio

Ejecutar contenedores con los permisos mínimos necesarios:

# No ejecutar como root
FROM node:18-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

Defensa en Profundidad

Múltiples capas de seguridad:

  1. Imágenes base seguras
  2. Escaneo de vulnerabilidades
  3. Restricciones en tiempo de ejecución
  4. Aislamiento de red
  5. Encriptación de secretos

Infraestructura Inmutable

# Sistema de archivos raíz de solo lectura
docker run --read-only --tmpfs /tmp my-app

Seguridad de Imágenes

Usar Imágenes Oficiales

# Solo fuentes confiables
FROM node:18-alpine  # Imagen oficial de Node
FROM nginx:alpine    # Imagen oficial de Nginx

# Evitar fuentes desconocidas
# FROM randomuser/suspicious-image  # No usar

Fijar Versiones Específicas

# Malo - impredecible
FROM node:latest

# Bueno - reproducible
FROM node:18.17.0-alpine3.18

# Incluir digest SHA256
FROM node:18.17.0-alpine3.18@sha256:abc123...

Minimizar Tamaño de Imagen

# Imágenes más pequeñas = menor superficie de ataque
FROM alpine:3.18
# O distroless
FROM gcr.io/distroless/static-debian11

Construcciones Multi-Etapa

# Etapa de construcción con herramientas
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o app

# Runtime mínimo
FROM gcr.io/distroless/static-debian11
COPY --from=builder /app/app /
ENTRYPOINT ["/app"]

No Incluir Secretos en Imágenes

# Malo - secretos en imagen
ENV API_KEY=secret123
COPY .env /app/.env

# Bueno - inyectar en tiempo de ejecución
# docker run -e API_KEY=${API_KEY} my-app

Firmar y Verificar Imágenes

# Habilitar Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# Construir imagen firmada
docker build -t my-image:latest .

# Push de imagen firmada
docker push my-image:latest

Seguridad en Tiempo de Ejecución de Contenedores

Ejecutar como Usuario No Root

# Crear y usar usuario no root
FROM alpine:3.18
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
CMD ["./app"]
# O especificar en tiempo de ejecución
docker run --user 1000:1000 my-app

Sistema de Archivos Raíz de Solo Lectura

# Prevenir modificaciones al sistema de archivos del contenedor
docker run -d \
  --read-only \
  --tmpfs /tmp \
  --tmpfs /run \
  nginx
# Docker Compose
services:
  app:
    image: my-app
    read_only: true
    tmpfs:
      - /tmp
      - /run

Eliminar Capacidades

# Eliminar todas las capacidades, agregar solo las necesarias
docker run -d \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  nginx

Capacidades disponibles:

  • NET_BIND_SERVICE: Vincular a puertos < 1024
  • CHOWN: Cambiar propiedad de archivos
  • DAC_OVERRIDE: Omitir verificaciones de permisos de archivos
  • SETUID/SETGID: Cambiar ID de usuario/grupo

Desactivar Nuevos Privilegios

# Prevenir escalación de privilegios
docker run -d \
  --security-opt=no-new-privileges:true \
  my-app

Usar Perfiles de Seguridad

AppArmor

# Usar perfil AppArmor
docker run -d \
  --security-opt apparmor=docker-default \
  nginx

SELinux

# Etiquetas SELinux
docker run -d \
  --security-opt label=level:s0:c100,c200 \
  nginx

Seccomp

# Usar perfil seccomp
docker run -d \
  --security-opt seccomp=/path/to/seccomp-profile.json \
  my-app

Ejemplo de perfil Seccomp:

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["read", "write", "open", "close"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Limitar Recursos del Sistema

# Prevenir ataques DoS
docker run -d \
  --memory="512m" \
  --memory-swap="512m" \
  --cpus="1.5" \
  --pids-limit=100 \
  my-app

Aislar Contenedores

# Usar espacios de nombres de usuario
docker run -d --userns-remap=default my-app

Configurar en /etc/docker/daemon.json:

{
  "userns-remap": "default"
}

Seguridad de Red

Aislamiento de Red

# Crear redes aisladas
docker network create --driver bridge frontend
docker network create --driver bridge backend
docker network create --driver bridge database

# Conectar contenedores a redes apropiadas
docker run -d --name web --network frontend nginx
docker run -d --name api --network backend my-api
docker run -d --name db --network database postgres

Desactivar Comunicación Entre Contenedores

# Crear red con ICC desactivado
docker network create \
  --driver bridge \
  --opt com.docker.network.bridge.enable_icc=false \
  isolated-network

Redes Overlay Encriptadas

# Crear red overlay encriptada (Swarm)
docker network create \
  --driver overlay \
  --opt encrypted=true \
  secure-overlay

Restringir Acceso de Red del Contenedor

# Sin acceso a red
docker run -d --network none my-batch-processor

# Usar políticas de red en Kubernetes
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

TLS para Comunicación

# Configurar daemon Docker para TLS
dockerd \
  --tlsverify \
  --tlscacert=ca.pem \
  --tlscert=server-cert.pem \
  --tlskey=server-key.pem \
  -H=0.0.0.0:2376

Gestión de Secretos

Nunca Codificar Secretos en Duro

# Malo
ENV DB_PASSWORD=mysecretpassword

# Bueno - pasar en tiempo de ejecución
# docker run -e DB_PASSWORD=${DB_PASSWORD} my-app

Usar Docker Secrets (Swarm)

# Crear secreto
echo "mypassword" | docker secret create db_password -

# Usar en servicio
docker service create \
  --name app \
  --secret db_password \
  my-app

Acceder en contenedor:

# Secreto disponible en /run/secrets/db_password
cat /run/secrets/db_password

Variables de Entorno

# Pasar secretos vía entorno
docker run -d \
  -e DB_PASSWORD=${DB_PASSWORD} \
  -e API_KEY=${API_KEY} \
  my-app

Gestión Externa de Secretos

HashiCorp Vault

# Integrar con Vault
docker run -d \
  -e VAULT_ADDR=https://vault.example.com \
  -e VAULT_TOKEN=${VAULT_TOKEN} \
  my-app

AWS Secrets Manager

# Usar secretos de AWS
docker run -d \
  -e AWS_REGION=us-east-1 \
  -e SECRET_ARN=arn:aws:secretsmanager:... \
  my-app

Encriptar Variables de Entorno

# Usar archivos encriptados
docker run -d \
  --env-file encrypted.env \
  my-app

Límites de Recursos

Límites de Memoria

# Establecer límite de memoria
docker run -d \
  --memory="1g" \
  --memory-reservation="512m" \
  --memory-swap="2g" \
  --oom-kill-disable=false \
  my-app

Límites de CPU

# Limitar uso de CPU
docker run -d \
  --cpus="2.0" \
  --cpu-shares=1024 \
  --cpuset-cpus="0,1" \
  my-app

Límites de I/O de Disco

# Limitar operaciones de disco
docker run -d \
  --device-read-bps /dev/sda:1mb \
  --device-write-bps /dev/sda:1mb \
  my-app

Límites de Procesos

# Limitar número de procesos
docker run -d \
  --pids-limit=200 \
  my-app

Escaneo de Seguridad

Escáner Trivy

# Instalar Trivy
sudo apt-get install trivy

# Escanear imagen
trivy image my-app:latest

# Solo high y critical
trivy image --severity HIGH,CRITICAL my-app:latest

# Integración CI/CD
trivy image --exit-code 1 --severity CRITICAL my-app:latest

Escáner Clair

# Ejecutar Clair
docker run -d --name clair -p 6060:6060 quay.io/coreos/clair:latest

# Escanear con clair-scanner
clair-scanner --ip $(hostname -I | awk '{print $1}') my-app:latest

Docker Scan

# Usar escáner integrado de Docker
docker scan my-app:latest

# Escanear y mostrar recomendaciones de imagen base
docker scan --file Dockerfile my-app:latest

Anchore

# Escanear con Anchore
anchore-cli image add my-app:latest
anchore-cli image wait my-app:latest
anchore-cli image vuln my-app:latest all

Escaneo Continuo

# Ejemplo de GitHub Actions
name: Security Scan
on: [push]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Build image
        run: docker build -t my-app .
      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'my-app'
          format: 'sarif'
          output: 'trivy-results.sarif'

Control de Acceso

Protección del Socket Docker

# Nunca exponer socket Docker
# Malo:
# docker run -v /var/run/docker.sock:/var/run/docker.sock ...

# Usar Docker-in-Docker o enfoques alternativos
docker run --privileged docker:dind

Espacios de Nombres de Usuario

{
  "userns-remap": "default"
}
# Reiniciar Docker
sudo systemctl restart docker

Control de Acceso Basado en Roles

# Usar Docker Enterprise para RBAC
# O implementar plugin de autorización personalizado

# Ejemplo: limitar usuarios a operaciones específicas
{
  "authorization-plugins": ["docker-authz-plugin"]
}

Auditar Comandos Docker

# Habilitar registro de auditoría
sudo auditctl -w /usr/bin/docker -k docker
sudo auditctl -w /var/lib/docker -k docker
sudo auditctl -w /etc/docker -k docker

# Ver logs de auditoría
sudo ausearch -k docker

Seguridad del Daemon

Configuración Segura del Daemon Docker

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

Habilitar TLS

{
  "tls": true,
  "tlsverify": true,
  "tlscacert": "/etc/docker/ca.pem",
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem"
}

Restringir Acceso al Daemon

# Limitar quién puede ejecutar comandos Docker
sudo usermod -aG docker limited-user

# Mejor: usar sudo para comandos Docker
# No agregar usuarios al grupo docker en producción

Desactivar Características Legacy

{
  "disable-legacy-registry": true
}

Cumplimiento y Auditoría

CIS Docker Benchmark

# Ejecutar Docker Bench Security
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh

Escaneo de Seguridad Docker

# Habilitar Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# Verificar firmas de imágenes
docker pull my-registry.com/image:tag

Registrar Todo

{
  "log-driver": "syslog",
  "log-opts": {
    "syslog-address": "tcp://siem.example.com:514",
    "tag": "docker/{{.Name}}"
  }
}

Auditorías Regulares

# Verificar contenedores en ejecución
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"

# Revisar imágenes
docker images

# Verificar volúmenes
docker volume ls

# Revisar redes
docker network ls

Lista de Verificación para Producción

Seguridad de Imágenes

  • Usar imágenes base oficiales
  • Fijar versiones específicas con SHA256
  • Escanear vulnerabilidades
  • Sin secretos en imágenes
  • Firmar imágenes con Docker Content Trust
  • Imágenes base mínimas (Alpine/distroless)
  • Construcciones multi-etapa
  • Actualizaciones regulares de imágenes

Seguridad en Tiempo de Ejecución

  • Ejecutar como usuario no root
  • Sistema de archivos raíz de solo lectura
  • Eliminar todas las capacidades, agregar solo las necesarias
  • Habilitar no-new-privileges
  • Usar perfiles de seguridad (AppArmor/SELinux/Seccomp)
  • Límites de recursos (CPU, memoria, PIDs)
  • Health checks configurados
  • Políticas de reinicio adecuadas

Seguridad de Red

  • Aislamiento de red por aplicación
  • Sin exposición innecesaria de puertos
  • Redes overlay encriptadas
  • TLS para comunicación externa
  • Políticas de red implementadas

Gestión de Secretos

  • Sin secretos codificados en duro
  • Usar Docker secrets o vault externo
  • Encriptar secretos en reposo y en tránsito
  • Rotar secretos regularmente
  • Auditar acceso a secretos

Control de Acceso

  • Socket Docker nunca expuesto
  • Espacios de nombres de usuario habilitados
  • RBAC implementado
  • Registro de auditoría habilitado
  • Principio de mínimo privilegio

Monitoreo y Cumplimiento

  • Escaneo de seguridad en CI/CD
  • Monitoreo de seguridad en tiempo de ejecución
  • Agregación de logs configurada
  • Cumplimiento con benchmark CIS
  • Auditorías de seguridad regulares
  • Plan de respuesta a incidentes

Conclusión

La seguridad de Docker requiere atención continua a lo largo de todo el ciclo de vida del contenedor. Implementar estas mejores prácticas reduce significativamente los riesgos de seguridad en entornos de producción.

Puntos Clave

  • Defensa en Profundidad: Múltiples capas de seguridad
  • Mínimo Privilegio: Permisos mínimos necesarios
  • Inmutabilidad: Sistemas de archivos raíz de solo lectura
  • Escaneo: Evaluación continua de vulnerabilidades
  • Secretos: Nunca en imágenes, siempre encriptados
  • Monitoreo: Auditar y registrar todo

Jerarquía de Seguridad

  1. Imágenes Base Seguras: Comenzar con imágenes confiables y mínimas
  2. Seguridad de Construcción: Escanear y firmar imágenes
  3. Restricciones en Tiempo de Ejecución: Capacidades, solo lectura, no root
  4. Aislamiento de Red: Redes separadas, comunicación encriptada
  5. Control de Acceso: Proteger socket Docker, espacios de nombres de usuario
  6. Monitoreo: Escaneo y auditoría de seguridad continua

Comandos Rápidos de Seguridad

# Ejecución segura de contenedor
docker run -d \
  --read-only \
  --tmpfs /tmp \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt=no-new-privileges:true \
  --memory="512m" \
  --cpus="1" \
  --pids-limit=100 \
  --user 1000:1000 \
  my-app

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

# Auditoría de seguridad
docker run -it --rm --pid host --userns host --cap-add audit_control \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /etc:/etc:ro \
  --label docker_bench_security \
  docker/docker-bench-security

Próximos Pasos

  1. Auditar: Ejecutar CIS Docker Benchmark
  2. Escanear: Implementar escaneo de seguridad continuo
  3. Endurecer: Aplicar restricciones de seguridad en tiempo de ejecución
  4. Monitorear: Configurar monitoreo y alertas de seguridad
  5. Capacitar: Educar al equipo en mejores prácticas de seguridad
  6. Probar: Realizar pruebas de penetración
  7. Actualizar: Mantener Docker e imágenes actualizadas

La seguridad es un proceso continuo. Revisa y actualiza regularmente tus prácticas de seguridad para proteger contra amenazas en evolución.