"Ya que los contenedores son espacios aislados, ¿no está bien usar root dentro de ellos?"

"Si creo un USUARIO, solo aumentarán las capas y será molesto..."

"Es un fastidio resolver los permisos para que el USUARIO del contenedor pueda acceder a los directorios del host que tienen un volumen montado..."

Muchos desarrolladores piensan así y utilizan el usuario root, que es el valor predeterminado en el Dockerfile.

Sin embargo, esto es una práctica muy peligrosa en términos de seguridad. El 'aislamiento' de los contenedores no es un cortafuegos perfecto, y ejecutar un contenedor con permisos de root puede poner en peligro todo el servidor.

En este artículo, explicaré claramente 'por qué' no se debe ejecutar un contenedor como root y 'cómo' evitarlo.


1. El peor de los escenarios: "Escape del Contenedor"



Esta es la razón más determinante para evitar el uso de root.

  • Mapeo básico: El usuario root dentro del contenedor de Docker (UID 0) es, por defecto, el mismo que el usuario root del servidor host (UID 0).

  • Cuando se rompe el aislamiento: Supongamos que un atacante explota una vulnerabilidad en la aplicación, en Docker o en el mismo núcleo de Linux, y logra 'escapar' del entorno aislado del contenedor.

  • Consecuencia: Si el contenedor se estaba ejecutando con permisos de root, el atacante obtendría inmediatamente los permisos de root del servidor host. Es como si el servidor entero fuera completamente comprometido.

Analogía: Ejecutar un contenedor como root es como alojar a un "huésped con la llave maestra del hotel" en una habitación. En el momento en que ese huésped abra la puerta de su habitación (contenedor), podrá moverse libremente por todo el hotel (host).

En cambio, ¿qué pasaría si el contenedor se ejecutara como un usuario normal sin privilegios, como appuser (UID 1001)? Si el atacante tiene éxito en la fuga, solo tendría los permisos de un usuario sin privilegios como appuser, limitando así el daño posible.


2. El principio de menor privilegio (Principle of Least Privilege)

El principio básico de la seguridad es que "todos los programas y usuarios solo deben tener los mínimos privilegios necesarios para realizar su trabajo".

Para ejecutar una aplicación web, no se necesitan privilegios de root en absoluto.

  • Cuando eres root: Si un atacante entra en el contenedor (incluso si no logra escapar), él es root dentro del contenedor.

    • Puede instalar libremente escáneres maliciosos y mineros de criptomonedas con apt-get install.

    • Puede modificar y eliminar todos los archivos, incluidos los archivos de configuración que contienen información de conexión de bases de datos (settings.py y otros).

  • Cuando eres appuser: Aunque el atacante entre, él solo es appuser.

    • apt-get install? Permiso Denegado.

    • ¿Modificar archivos de configuración del sistema? Permiso Denegado.

    • La magnitud del daño se limita estrictamente al directorio de código fuente de la aplicación que posee appuser.


3. Aclarando malentendidos comunes



🤔 "¿No solo aumenta la capa de la imagen el comando USER?"

No. Este es el mayor malentendido.

El comando USER en el Dockerfile no crea capas en el sistema de archivos. Este comando simplemente agrega una instrucción a los metadatos de la imagen que dice: "el usuario predeterminado para este contenedor cuando se inicie será 'appuser'".

El tamaño de la imagen no aumenta ni siquiera 1 KB, y no afecta la velocidad de construcción.

Por supuesto, el comando RUN useradd... crea un usuario y agrega una capa muy pequeña, pero este es un costo que vale la pena pagar por seguridad.

🤔 "Para abrir el puerto 80, se necesita root de todos modos, ¿no?"

Así es. En Linux, solo root puede abrir puertos por debajo de 1024 (por ejemplo: 80, 443). Pero esto ya no es un argumento para ejecutar contenedores como root.

El enfoque moderno es el siguiente:

  1. Ejecutar la aplicación en el contenedor con permisos de appuser en un puerto alto como el 8000.

  2. Desde el exterior, Docker realiza el mapeo de puertos (docker run -p 80:8000) o un proxy inverso como Nginx redirige las peticiones externas en el puerto 80 al puerto interno 8000.

No se necesitan permisos de root dentro del contenedor en absoluto.


4. CÓMO: Mejores prácticas del Dockerfile

Entonces, ¿cómo debemos aplicarlo? Agregar las siguientes líneas al final del Dockerfile no es un desperdicio, sino una regla de seguridad básica y fundamental.

Dockerfile

FROM python:3.12-slim

WORKDIR /app

# ... (apt-get install, pip install, etc.)

# 1. Crear un grupo y usuario no privilegiados
# -r: crear como usuario/grupo del sistema, --no-create-home: no crear directorio home
RUN groupadd -r appgroup && useradd -r -g appgroup --no-create-home appuser

# 2. Al copiar los archivos de la aplicación, asignar la propiedad a appuser
# (los archivos dentro de WORKDIR también heredarán esta propiedad)
COPY --chown=appuser:appgroup . .

# 3. Cambiar al usuario appuser para ejecutar los comandos siguientes
USER appuser

# 4. Ejecutar la aplicación en un puerto alto como el 8000
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

Resumen

Ejecutar un contenedor como root es renunciar al concepto de "defensa en profundidad". Es un acto de quitarse el segundo perímetro de defensa (USER) en caso de que se rompa el primer perímetro de aislamiento.

Revisa tu Dockerfile ahora mismo.