Si bien los contenedores proporcionan un gran nivel de aislamiento, no son una solución mágica de seguridad. Es crucial seguir buenas prácticas para proteger tus aplicaciones y el host subyacente. La seguridad en Docker es una responsabilidad compartida que abarca desde la imagen base hasta la ejecución del contenedor.
Cada programa, librería o dependencia en tu imagen es una potencial puerta de entrada para un atacante. Si una de esas dependencias tiene una vulnerabilidad, tu contenedor es vulnerable. Por lo tanto, una de las reglas más importantes es: incluye solo lo estrictamente necesario en tu imagen.
Una forma efectiva de lograrlo es usar imágenes base minimalistas:
Al reducir el número de componentes, reduces la "superficie de ataque" y la probabilidad de que tu imagen contenga software vulnerable.
Por defecto, los procesos dentro de un contenedor se ejecutan como usuario `root`. Esto es un riesgo de seguridad significativo. Si un atacante logra explotar una vulnerabilidad en tu aplicación y obtiene acceso al contenedor, tendrá privilegios de `root` dentro de él. Aunque el contenedor está aislado, esto le da un poder considerable y aumenta el riesgo de un "escape" al host.
La solución es aplicar el principio de menor privilegio y ejecutar tus procesos como un usuario sin privilegios. Esto se hace en el Dockerfile:
FROM python:3.9-slim
WORKDIR /app
# Crear un usuario y grupo sin privilegios
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
# Copiar archivos y establecer permisos
COPY --chown=appuser:appgroup . /app
# Cambiar al usuario sin privilegios
USER appuser
# Instalar dependencias
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["flask", "run"]
En este ejemplo, creamos un usuario `appuser`, le damos la propiedad de los archivos de la aplicación y luego usamos la instrucción `USER` para asegurarnos de que el resto de las instrucciones y el proceso principal se ejecuten como ese usuario no privilegiado.
Nunca, bajo ninguna circunstancia, guardes contraseñas, tokens de API u otros secretos directamente en tu Dockerfile o en el código fuente. Hacerlo los expone a cualquiera que tenga acceso a la imagen o al repositorio de código.
Las formas correctas de manejar secretos son:
/run/secrets/
), residen en memoria y no se escriben en disco dentro del contenedor.Ejemplo con Docker Compose y un archivo `.env`:
# docker-compose.yml
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} # Lee la variable desde el archivo .env
# .env (este archivo NO se sube a Git)
DB_PASSWORD=mi-super-secreto-password
Incluso siguiendo todas las buenas prácticas, las dependencias de tu imagen pueden tener vulnerabilidades conocidas (CVEs). Es vital escanear tus imágenes regularmente para detectarlas.
Docker proporciona el comando `docker scan`, que utiliza el motor de Snyk para analizar tu imagen y reportar vulnerabilidades.
docker scan mi-app-flask:1.0
El comando te mostrará una lista de las vulnerabilidades encontradas, su severidad (baja, media, alta, crítica) y a menudo te sugerirá cómo solucionarlas (por ejemplo, actualizando una librería o usando una imagen base más reciente).
Integrar el escaneo de imágenes en tu pipeline de CI/CD es una práctica de seguridad moderna y fundamental (DevSecOps).