¿Por qué es crítica la seguridad en Docker?
En un entorno de producción, los contenedores Docker pueden ser el vector de ataque más vulnerable si no se configuran correctamente. A diferencia de las máquinas virtuales tradicionales, los contenedores comparten el kernel del host, lo que significa que una vulnerabilidad en un contenedor podría afectar a todo el sistema.
1. Selección y Construcción de Imágenes Seguras
Usar imágenes base mínimas
Una de las primeras líneas de defensa es elegir imágenes base que contengan únicamente lo necesario para tu aplicación.
alpine, distroless o slim sobre imágenes completas que incluyen herramientas innecesarias que pueden ser explotadas.# ❌ Imagen pesada con múltiples herramientas
FROM ubuntu:latest
# ✅ Imagen mínima específica para Node.js
FROM node:18-alpine
# ✅ Aún mejor: imagen distroless de Google
FROM gcr.io/distroless/nodejs18-debian11
Utilizar imágenes firmadas (Content Trust)
Docker Hub y otros registries soportan Docker Content Trust para verificar la autenticidad de las imágenes.
# Habilitar Content Trust
export DOCKER_CONTENT_TRUST=1
# Solo se podrán extraer imágenes firmadas
docker pull mi-imagen:tag
2. Gestión de Usuarios y Permisos
No ejecutar como root
Por defecto, Docker permite que los procesos dentro del contenedor se ejecuten como root. Esto es extremadamente peligroso si el contenedor es comprometido.
# ❌ Peligroso: ejecutando como root
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
# ✅ Seguro: ejecutando como usuario no root
FROM node:18-alpine
WORKDIR /app
# Crear usuario sin privilegios
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY package*.json ./
RUN npm install && chown -R appuser:appgroup /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["node", "server.js"]
--user en docker run para mapear a un UID sin privilegios.Configurar capacidades Linux mínimas
# Quitar todas las capacidades y solo dar las necesarias
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE \
-u 1000:1000 \
mi-aplicacion:seura
3. Gestión de Secretos y Datos Sensibles
No usar ENV para secretos
# ❌ NUNCA hagas esto
FROM node:18-alpine
ENV DATABASE_PASSWORD=mi-contraseña-secreta
ENV API_KEY=sk-live-xxxxx
# ✅ Usar Docker Secrets (Swarm) o servicios externos
# Para Kubernetes: usar Sealed Secrets, HashiCorp Vault, AWS Secrets Manager
Usar volúmenes para datos sensibles
# Montar secretos desde archivos seguros
docker run -v /run/secrets/my_secret:/run/secrets/my_secret:ro \
mi-aplicacion
# O usar Docker Secrets en Swarm
docker secret create db_password password.txt
docker service create --secret db_password mi-db
Ver más: Ejemplo completo de gestión de secretos# docker-compose.yml con secrets externos
version: '3.8'
services:
api:
image: mi-api:production
secrets:
- db_password
- api_key
environment:
- NODE_ENV=production
volumes:
- ./config:/config:ro
db:
image: postgres:15-alpine
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
En este ejemplo, los secrets se montan como archivos en /run/secrets/ dentro del contenedor, nunca como variables de entorno.
4. Seguridad en Tiempo de Ejecución (Runtime)
Limitación de recursos
docker run \
--memory="256m" \
--memory-swap="256m" \
--cpus="0.5" \
--pids-limit=50 \
--ulimits nproc=10:20 \
mi-aplicacion
Modo read-only del filesystem
docker run --read-only \
--tmpfs /tmp \
mi-aplicacion
Esto previene que malware escriba en el filesystem del contenedor, dificultando la persistencia de ataques.
5. Network Security
Segmentación de redes
# Crear redes isoladas para diferentes servicios
docker network create --driver bridge backend-net
docker network create --driver bridge frontend-net
# Los contenedores solo pueden comunicarse dentro de su red
docker run -d --network backend-net --name db postgres:15-alpine
docker run -d --network backend-net --name api mi-api
docker run -d --network frontend-net --name web mi-web
Bloquear comunicaciones externas
# Evitar que los contenedores puedan cambiar el estado de la red del host
docker run --network none mi-servicio
# O denegar automáticamente el tráfico saliente (excepto lo explícitamente permitido)
ipTables -A FORWARD -i docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
6. Escaneo de Vulnerabilidades
Integrar escaneo en el pipeline CI/CD
# Usar Trivy para escanear imágenes
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image mi-imagen:1.0.0
# Output en formato JSON para automatización
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image --format json mi-imagen:1.0.0 > scan-results.json
- Configurar Trivy o Snyk en el pipeline de CI/CD como paso obligatorio
- Definir políticas de severidad (rechazar imágenes con vulnerabilities HIGH o CRITICAL)
- Configurar notificaciones automáticas al equipo de seguridad
- Programar rescaneos periódicos de imágenes en producción
- Mantener un inventario actualizado de todas las imágenes desplegadas
7. Hardening del Docker Daemon
# Configuración segura de daemon en /etc/docker/daemon.json
{
"icc": false,
"userland-proxy": false,
"no-new-privileges": true,
"seccomp-profile": "/etc/docker/seccomp.json",
"apparmor-profile": "docker-default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2"
}
sudo systemctl restart docker8. Checklist de Seguridad para Producción
| Práctica | Estado | Prioridad |
|---|---|---|
| Usar usuario no-root | ✅ Implementado | Crítica |
| Imágenes firmadas (Content Trust) | ⏳ Pendiente | Alta |
| Escaneo de vulnerabilidades en CI | ✅ Implementado | Crítica |
| Secrets externos (no ENV) | ✅ Implementado | Crítica |
| Filesystem read-only | ⏳ Pendiente | Alta |
| Limitación de recursos | ✅ Implementado | Media |
| Redes isoladas | ✅ Implementado | Alta |
| Healthchecks definidos | ⏳ Pendiente | Media |
9. Healthchecks y Monitoreo
Los healthchecks no solo mejoran la disponibilidad, también permiten detectar comportamientos anómalos.
# Dockerfile con healthcheck
FROM node:18-alpine
WORKDIR /app
# ... copia de archivos ...
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
"La seguridad no es un producto, es un proceso. No se trata de implementar una solución y olvidarse, sino de establecer prácticas continuas de revisión, actualización y mejora." — Bruce Schneier
¿Cuál es la forma más segura de manejar contraseñas o API keys en contenedores Docker?
- A) Usar variables de entorno ENV en el Dockerfile
- B) Hardcodearlas directamente en el código fuente
- C) Usar Docker Secrets o servicios externos de gestión de secretos (Vault, AWS Secrets Manager)
- D) Guardarlas en comentarios dentro del código
- CIS Docker Benchmark: Guía completa de hardening para Docker Engine
- Docker Security Cheat Sheet: OWASP Docker Security Cheat Sheet
- Herramientas recomendadas: Trivy, Snyk, Clair, Anchore, Falco
- Cursos complementarios: Docker Security Essentials, Kubernetes Security
Mantén actualizado este checklist y revísalo trimestralmente para adaptarte a nuevas amenazas.