Los procesos en un contenedor Docker no deben ejecutarse como root. Es más seguro ejecutar sus aplicaciones como un usuario no root que especifica como parte de su Dockerfile o cuando usa 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 root
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. Puedes lograr esto con el USER
instrucción:
FROM base-image:latest RUN apt install demo-package USER demo-user:demo-group ENTRYPOINT ["demo-binary"]
Los contenedores iniciados a partir de esta imagen se ejecutarán como demo-user
. El usuario será miembro de la demo-group
grupo. Puede omitir el nombre del grupo si no necesita que el usuario esté en un grupo:
USER demo-user
Puede especificar una ID de usuario (UID) y una ID de grupo (GID) en lugar de nombres:
USER 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. los apt install
instrucción en el ejemplo anterior tiene una necesidad legítima de root. Si el USER
la instrucción se colocó encima de él, 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:
COPY initial-config.yaml /data/config.yaml USER demo-user:demo-group RUN chown demo-user:demo-group /data
En este ejemplo el /data
el directorio debe ser propiedad de demo-user
para que la aplicación pueda realizar cambios en su archivo de configuración. El anterior COPY
declaración habrá copiado el archivo como raíz. Una forma abreviada está disponible usando el --chown
bandera con copy
:
COPY --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 --user
marca cada vez que llamas 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 demo-image:latest
los --user
flag ejecuta el proceso del contenedor como el usuario especificado. Es menos seguro que el Dockerfile USER
instrucción porque tienes que aplicarla individualmente a cada docker run
dominio. 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:
FROM image-that-runs-as-root:latest USER demo-user
$ docker build . -t image-that-now-runs-as-non-root:latest
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 userns-remap
campo a tu /etc/docker/daemon.json
expediente:
"userns-remap": "default"
Usando default
como el valor para userns-remap
indica a Docker que cree automáticamente un nuevo usuario en su host llamado dockremap
. La raíz dentro de los contenedores se asignará de nuevo a dockremap
en su anfitrión. 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-user"
Reinicie el demonio de Docker después de aplicar su cambio:
$ sudo service docker restart
si estás usando nsuser-remap: default
la dockremap
el usuario ahora debería existir en su host:
$ id dockremap uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)
El usuario también debe aparecer en el /etc/subuid
y /etc/subgid
archivos de ID subordinados:
$ dockremap: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, ID 231500
está asignado a 0
, convirtiéndolo 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 utilizando el espacio de nombres de usuario reasignado a menos que opte por no hacerlo con docker run --userns=host
. El mecanismo funciona mediante la creación de directorios con espacios de nombres dentro /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 total 14 drwx------ 5 231500 231500 13 Jul 22 19:00 aufs drwx------ 3 231500 231500 13 Jul 22 19:00 containers ...
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 USER
instrucción 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 GID específico. 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
luego incluir en la lista blanca aquellos que son necesarios con --cap-add
banderas 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.