Buenas Prácticas de Seguridad en Docker
¿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
Ver más: Recursos adicionales de seguridad
- 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.