Por qué los procesos en contenedores Docker no deberían ejecutarse como root

Publicado: 2022-08-19

Gráfico que muestra el logotipo de Docker

Los procesos en un contenedor Docker no deben ejecutarse como root. Es más seguro ejecutar sus aplicaciones como un usuario no root que especifique como parte de su Dockerfile o cuando use docker run . Esto minimiza el riesgo al presentar una superficie de ataque reducida para cualquier amenaza en su contenedor.

En este artículo, aprenderá sobre los peligros de ejecutar aplicaciones en contenedores como root. También verá cómo crear un usuario no raíz y configurar el espacio de nombres en situaciones en las que esto no es posible.

¿Por qué es peligroso ejecutar como raíz?

Los contenedores se ejecutan como raíz de forma predeterminada. El demonio Docker se ejecuta como root en su host y los contenedores en ejecución también serán root.

Aunque puede parecer que la raíz dentro del contenedor es un usuario independiente, en realidad es lo mismo que la cuenta raíz en su host. La separación solo la proporcionan los mecanismos de aislamiento de contenedores de Docker. No hay un límite físico fuerte; su contenedor es otro proceso ejecutado por el usuario root en el kernel de su host. Esto significa que una vulnerabilidad en su aplicación, el tiempo de ejecución de Docker o el kernel de Linux podría permitir a los atacantes salir del contenedor y realizar operaciones con privilegios de root en su máquina.

Hay algunas protecciones integradas que reducen el riesgo de que esto suceda. La raíz dentro del contenedor no tiene privilegios y tiene capacidades restringidas. Esto evita que el contenedor use los comandos de administración del sistema a menos que agregue capacidades manualmente o use el modo privilegiado cuando inicie sus contenedores.

A pesar de esta mitigación, permitir que las aplicaciones se ejecuten como raíz sigue siendo un peligro. Al igual que restringiría el uso de la raíz en un entorno tradicional, no es aconsejable usarlo innecesariamente dentro de sus contenedores. Estás proporcionando un entorno privilegiado que les da a los atacantes más puntos de apoyo en caso de que ocurra una infracción.

Ejecución de aplicaciones en contenedores como usuario no raíz

Es una buena práctica que las aplicaciones en contenedores se ejecuten como un usuario normal. La mayoría del software no necesita acceso a la raíz, por lo que cambiar el usuario proporciona una capa de defensa inmediata contra la ruptura del contenedor.

Debe crear una nueva cuenta de usuario como una de las etapas finales en su Dockerfile. Puede lograr esto con la instrucción USER :

 DESDE imagen base: más reciente
EJECUTAR apt install paquete de demostración
USUARIO demo-usuario:demo-grupo
PUNTO DE ENTRADA ["demo-binario"]

Los contenedores iniciados a partir de esta imagen se ejecutarán como demo-user . El usuario será miembro del grupo demo-group group. Puede omitir el nombre del grupo si no necesita que el usuario esté en un grupo:

 USUARIO usuario de demostración

Puede especificar una ID de usuario (UID) y una ID de grupo (GID) en lugar de nombres:

 USUARIO 950:950

La asignación de un UID y GID conocido suele ser la forma más segura de proceder. Evita que el usuario del contenedor sea asignado a una cuenta de host con privilegios excesivos.

USER a menudo se especifica como la penúltima etapa en un Dockerfile. Esto significa que aún puede ejecutar operaciones que requieren root antes en la creación de la imagen. La instrucción apt install en el ejemplo anterior tiene una necesidad legítima de root. Si la instrucción USER se colocara encima, apt se ejecutaría como demo-user que carecería de los permisos necesarios. Como las instrucciones de Dockerfile solo se aplican a las compilaciones de imágenes, no a los contenedores en ejecución, es seguro dejar el cambio de usuario para más adelante en su Dockerfile.

Cambiar el usuario que ejecuta su contenedor puede requerir que actualice los permisos en los archivos y carpetas a los que accede. Establezca la propiedad en cualquier ruta que utilizará su aplicación:

 COPIAR initial-config.yaml /data/config.yaml

USUARIO demo-usuario:demo-grupo
EJECUTAR chown demo-user:demo-group /data

En este ejemplo, el directorio /data debe ser propiedad demo-user para que la aplicación pueda realizar cambios en su archivo de configuración. La instrucción COPY anterior habrá copiado el archivo como raíz. Hay una forma abreviada disponible usando la --chown con copy :

 COPIA --chown=demo-user:demo-group initial-config.yaml /data/config.yaml

Cambiar el usuario al iniciar un contenedor

Si bien puede cambiar fácilmente el usuario en sus propios Dockerfiles, muchas aplicaciones de terceros continúan ejecutándose como raíz. Puede reducir el riesgo asociado con el uso de estos configurando el indicador --user cada vez que llama a docker run . Esto anula el conjunto de usuarios en el Dockerfile de la imagen.

 $ docker run -d --user demo-user:demo-group demo-image:latest
$ docker run -d --user demo-user demo-image:latest
$ docker run -d --user 950:950 imagen de demostración: más reciente

El indicador --user ejecuta el proceso del contenedor como el usuario especificado. Es menos seguro que la instrucción USER de Dockerfile porque debe aplicarlo individualmente a cada comando de docker run . Una mejor opción para las imágenes de uso regular es crear su propia imagen derivada que pueda establecer una nueva cuenta de usuario:

 DESDE imagen-que-se-ejecuta-como-root:último
USUARIO usuario de demostración
 $ compilación de la ventana acoplable. -t imagen-que-ahora-se-ejecuta-como-no-root:último

Cambiar el usuario de una imagen de terceros puede causar problemas: si el contenedor espera ejecutarse como root o necesita acceder a las rutas del sistema de archivos propiedad del root, verá errores mientras usa la aplicación. Puede intentar cambiar manualmente los permisos en las rutas que causan problemas. Como alternativa, verifique si el proveedor tiene un método compatible para ejecutar la aplicación con una cuenta de usuario sin privilegios.

Manejo de aplicaciones que tienen que ejecutarse como raíz

El espacio de nombres de usuario es una técnica para tratar con aplicaciones que necesitan algunos privilegios de root. Le permite asignar la raíz dentro de un contenedor a un usuario no raíz en su host. La raíz simulada dentro del contenedor tiene los privilegios que necesita, pero una ruptura no proporcionará acceso de raíz al host.

La reasignación de espacios de nombres se activa agregando un campo userns-remap a su archivo /etc/docker/daemon.json :

 {
    "userns-remap": "predeterminado"
}

El uso default como valor para userns-remap le indica a Docker que cree automáticamente un nuevo usuario en su host llamado dockremap . La raíz dentro de los contenedores volverá a mapear a dockremap en su host. Opcionalmente, puede especificar un usuario y grupo existente en su lugar, utilizando una combinación de nombre de usuario/grupo o UID/GID:

 {
    "userns-remap": "demo-usuario"
}

Reinicie el demonio de Docker después de aplicar su cambio:

 $ reinicio de la ventana acoplable del servicio Sudo

Si está utilizando nsuser-remap: default , el usuario de dockremap ahora debería existir en su host:

 $ id dockremap

uid=140(dockremap) gid=119(dockremap) grupos=119(dockremap)

El usuario también debe aparecer en los archivos de ID subordinados /etc/subuid y /etc/subgid :

 $ muelleremap:231500:65535

Al usuario se le ha asignado un rango de 65 535 ID subordinados a partir de 231500. Dentro del espacio de nombres de usuario, el ID 231500 se asigna a 0 , lo que lo convierte en el usuario raíz en sus contenedores. Al ser un UID de número alto, 231500 no tiene privilegios en el host, por lo que los ataques de ruptura de contenedores no podrán infligir tanto daño.

Todos los contenedores que inicie se ejecutarán con el espacio de nombres de usuario reasignado, a menos que se excluya con docker run --userns=host . El mecanismo funciona mediante la creación de directorios con espacio de nombres dentro de /var/lib/docker que son propiedad del UID y GID subordinado del usuario con espacio de nombres:

 $ sudo ls -l /var/lib/docker/231500.231500

14 en total
drwx------ 5 231500 231500 13 22 de julio 19:00 auf
drwx------ 3 231500 231500 13 22 de julio 19:00 contenedores
...

El espacio de nombres de usuario es una forma efectiva de aumentar el aislamiento del contenedor, evitar rupturas y preservar la compatibilidad con aplicaciones que necesitan privilegios de raíz. Sin embargo, existen algunas ventajas y desventajas: la característica funciona mejor en una instancia nueva de Docker, los volúmenes montados desde el host deben tener sus permisos ajustados y algunos controladores de almacenamiento externo no admiten el mapeo de usuarios en absoluto. Debe revisar la documentación antes de adoptar esta opción.

Resumen

Ejecutar aplicaciones en contenedores como raíz es un riesgo de seguridad. Aunque es fácil pasarlo por alto, el aislamiento proporcionado por los contenedores no es lo suficientemente sólido como para separar por completo a los usuarios del kernel de los usuarios de los contenedores. La raíz en el contenedor es lo mismo que la raíz en su host, por lo que un compromiso exitoso podría proporcionar el control de su máquina.

Como autor de una imagen, debe incluir la instrucción USER en su Dockerfile para que su aplicación se ejecute sin root. Los usuarios de imágenes pueden anular esto con docker run --user para asignar un UID y un GID específicos. Esto ayuda a mitigar los casos en los que la imagen normalmente usa la raíz.

Puede reforzar aún más la seguridad eliminando todas las capacidades del contenedor usando --cap-drop=ALL y luego agregando a la lista blanca aquellas que son necesarias con las banderas --cap-add . La combinación de estas técnicas ejecutará su aplicación como un usuario no root con el conjunto mínimo de privilegios que necesita, mejorando su postura de seguridad.