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.

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.