Aislar contenedores con un espacio de nombres de usuario
Los espacios de nombres de Linux proporcionan aislamiento para los procesos en ejecución, limitando su acceso a los recursos del sistema sin que el proceso en ejecución sea consciente de las limitaciones. Para obtener más información sobre los espacios de nombres de Linux, consulta espacios de nombres de Linux.
La mejor manera de prevenir ataques de escalada de privilegios desde dentro de un contenedor es configurar las aplicaciones de tu contenedor para que se ejecuten como usuarios sin privilegios. Para los contenedores cuyos procesos deben ejecutarse como el usuario root dentro del contenedor, puedes remapear este usuario a un usuario con menos privilegios en el anfitrión de Docker. Al usuario mapeado se le asigna un rango de UID que funcionan dentro del espacio de nombres como UID normales del 0 al 65536, pero que no tienen privilegios en la propia máquina anfitriona.
NoteCon
userns-remap, el demonio de Docker todavía se ejecuta como root. Para ejecutar tanto el demonio como los contenedores sin privilegios de root, consulta el modo Rootless en su lugar.
Acerca de la reasignación (remapping) y los ID de usuarios y grupos subordinados
La reasignación en sí se gestiona mediante dos archivos: /etc/subuid y /etc/subgid. Ambos archivos funcionan igual, pero uno se encarga del rango de ID de usuario y el otro del rango de ID de grupo. Considera la siguiente entrada en /etc/subuid:
testuser:231072:65536Esto significa que a testuser se le asigna un rango de ID de usuario subordinado de 231072 y los siguientes 65536 enteros en secuencia. El UID 231072 se mapea dentro del espacio de nombres (dentro del contenedor, en este caso) como UID 0 (root). El UID 231073 se mapea como UID 1, y así sucesivamente. Si un proceso intenta escalar privilegios fuera del espacio de nombres, el proceso se estará ejecutando como un UID de número alto sin privilegios en el anfitrión, el cual ni siquiera se mapea a un usuario real. Esto significa que el proceso no tiene ningún privilegio en el sistema anfitrión.
NoteEs posible asignar múltiples rangos subordinados para un usuario o grupo determinado añadiendo múltiples mapeos que no se superpongan para el mismo usuario o grupo en el archivo
/etc/subuido/etc/subgid. En este caso, Docker utiliza únicamente los primeros cinco mapeos, de acuerdo con la limitación del kernel de solo cinco entradas en/proc/self/uid_mapy/proc/self/gid_map.
Cuando configuras Docker para utilizar la característica userns-remap, puedes especificar opcionalmente un usuario y/o grupo existente, o puedes especificar default. Si especificas default, se crea y se utiliza un usuario y grupo dockremap para este propósito.
WarningAlgunas distribuciones no añaden automáticamente el nuevo grupo a los archivos
/etc/subuidy/etc/subgid. Si ese es el caso, es posible que debas editar manualmente estos archivos y asignar rangos que no se superpongan. Este paso se detalla en Requisitos previos.
Es muy importante que los rangos no se superpongan, para que un proceso no pueda obtener acceso en un espacio de nombres diferente. En la mayoría de las distribuciones de Linux, las utilidades del sistema gestionan los rangos por ti cuando añades o eliminas usuarios.
Esta reasignación es transparente para el contenedor, pero introduce cierta complejidad de configuración en situaciones en las que el contenedor necesita acceder a recursos en el anfitrión de Docker, como montajes de tipo bind en áreas del sistema de archivos en las que el usuario del sistema no puede escribir. Desde el punto de vista de la seguridad, lo mejor es evitar estas situaciones.
Requisitos previos
Los rangos de UID y GID subordinados deben estar asociados con un usuario existente, aunque la asociación sea un detalle de implementación. El usuario es el propietario de los directorios de almacenamiento con espacio de nombres bajo
/var/lib/docker/. Si no quieres utilizar un usuario existente, Docker puede crear uno por ti y usarlo. Si quieres utilizar un nombre de usuario o ID de usuario existente, este ya debe existir. Normalmente, esto significa que las entradas correspondientes deben estar en/etc/passwdy/etc/group, pero si utilizas un backend de autenticación diferente, este requisito puede traducirse de otra manera.Para verificar esto, utiliza el comando
id:$ id testuser uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)La forma en que se gestiona la reasignación de espacios de nombres en el anfitrión es mediante el uso de dos archivos,
/etc/subuidy/etc/subgid. Estos archivos normalmente se gestionan de forma automática cuando añades o eliminas usuarios o grupos, pero en algunas distribuciones, es posible que debas gestionar estos archivos manualmente.Cada archivo contiene tres campos: el nombre de usuario o el ID del usuario, seguido por un UID o GID inicial (que se trata como el UID o GID 0 dentro del espacio de nombres) y un número máximo de UID o GID disponibles para el usuario. Por ejemplo, dada la siguiente entrada:
testuser:231072:65536Esto significa que los procesos con espacio de nombres de usuario iniciados por
testuserpertenecen al UID del anfitrión231072(que parece el UID0dentro del espacio de nombres) hasta el 296607 (231072 + 65536 - 1). Estos rangos no deben superponerse para garantizar que los procesos con espacio de nombres no puedan acceder a los espacios de nombres de los demás.Después de añadir tu usuario, comprueba
/etc/subuidy/etc/subgidpara ver si tu usuario tiene una entrada en cada uno. Si no es así, debes añadirla, teniendo cuidado de evitar superposiciones.Si quieres utilizar el usuario
dockremapcreado automáticamente por Docker, busca la entradadockremapen estos archivos después de configurar y reiniciar Docker.Si hay alguna ubicación en el anfitrión de Docker en la que el usuario sin privilegios necesite escribir, ajusta los permisos de esas ubicaciones en consecuencia. Esto también se aplica si quieres utilizar el usuario
dockremapcreado automáticamente por Docker, pero no podrás modificar los permisos hasta después de configurar y reiniciar Docker.Habilitar
userns-remapenmascara de manera efectiva las capas de imágenes y contenedores existentes, así como otros objetos de Docker dentro de/var/lib/docker/. Esto se debe a que Docker necesita ajustar la propiedad de estos recursos y en realidad los almacena en un subdirectorio dentro de/var/lib/docker/. Lo más recomendable es habilitar esta característica en una nueva instalación de Docker en lugar de una existente.En la misma línea, si deshabilitas
userns-remap, no podrás acceder a ninguno de los recursos creados mientras estuvo habilitado.Revisa las limitaciones de los espacios de nombres de usuario para asegurarte de que tu caso de uso sea posible.
Habilitar userns-remap en el demonio
Puedes iniciar dockerd con la bandera --userns-remap o seguir este procedimiento para configurar el demonio utilizando el archivo de configuración daemon.json. Se recomienda el método de daemon.json. Si utilizas la bandera, usa el siguiente comando como modelo:
$ dockerd --userns-remap="testuser:testuser"
Edita
/etc/docker/daemon.json. Asumiendo que el archivo estaba vacío previamente, la siguiente entrada habilitauserns-remaputilizando el usuario y grupo llamadotestuser. Puedes hacer referencia al usuario y grupo por su ID o nombre. Solo necesitas especificar el nombre o ID del grupo si es diferente del nombre o ID del usuario. Si proporcionas tanto el nombre o ID de usuario como de grupo, sepáralos por un carácter de dos puntos (:). Los siguientes formatos funcionan para el valor, asumiendo que el UID y GID detestuserson1001:testusertestuser:testuser10011001:1001testuser:10011001:testuser
{ "userns-remap": "testuser" }NotePara utilizar el usuario
dockremapy que Docker lo cree por ti, establece el valor endefaulten lugar detestuser.Guarda el archivo y reinicia Docker.
Si utilizas el usuario
dockremap, verifica que Docker lo haya creado utilizando el comandoid.$ id dockremap uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)Verifica que la entrada se haya añadido a
/etc/subuidy/etc/subgid:$ grep dockremap /etc/subuid dockremap:231072:65536 $ grep dockremap /etc/subgid dockremap:231072:65536If these entries are not present, edit the files as the
rootuser and assign a starting UID and GID that is the highest-assigned one plus the offset (in this case,65536). Be careful not to allow any overlap in the ranges.Verifica que las imágenes anteriores no estén disponibles utilizando el comando
docker image ls. La salida debería estar vacía.Inicia un contenedor desde la imagen
hello-world.$ docker run hello-worldVerifica que exista un directorio con espacio de nombres dentro de
/var/lib/docker/nombrado con el UID y GID del usuario con espacio de nombres, propiedad de ese UID y GID, y que no sea legible por el grupo ni por cualquiera. Algunos de los subdirectorios todavía pertenecen arooty tienen permisos diferentes.$ sudo ls -ld /var/lib/docker/231072.231072/ drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/ $ sudo ls -l /var/lib/docker/231072.231072/ total 14 drwx------ 5 231072 231072 5 Jun 21 21:19 aufs drwx------ 3 231072 231072 3 Jun 21 21:21 containers drwx------ 3 root root 3 Jun 21 21:19 image drwxr-x--- 3 root root 3 Jun 21 21:19 network drwx------ 4 root root 4 Jun 21 21:19 plugins drwx------ 2 root root 2 Jun 21 21:19 swarm drwx------ 2 231072 231072 2 Jun 21 21:21 tmp drwx------ 2 root root 2 Jun 21 21:19 trust drwx------ 2 231072 231072 3 Jun 21 21:19 volumesTu listado de directorios puede tener algunas diferencias, especialmente si utilizas un controlador de almacenamiento de contenedores diferente a
aufs.Los directorios que pertenecen al usuario remapeado se utilizan en lugar de los mismos directorios directamente debajo de
/var/lib/docker/y las versiones no utilizadas (como/var/lib/docker/tmp/en este ejemplo) se pueden eliminar. Docker no los utiliza mientrasuserns-remapesté habilitado.
Deshabilitar la reasignación de espacios de nombres para un contenedor
Si habilitas los espacios de nombres de usuario en el demonio, todos los contenedores se iniciarán con los espacios de nombres de usuario habilitados por defecto. En algunas situaciones, como contenedores privilegiados, es posible que debas deshabilitar los espacios de nombres de usuario para un contenedor específico. Consulta las limitaciones conocidas de los espacios de nombres de usuario para conocer algunas de estas limitaciones.
Para deshabilitar los espacios de nombres de usuario para un contenedor específico, añade la bandera --userns=host al comando docker container create, docker container run o docker container exec.
Hay un efecto secundario al usar esta bandera: la reasignación de usuarios no se habilitará para ese contenedor pero, dado que las capas de solo lectura (imagen) se comparten entre los contenedores, la propiedad del sistema de archivos del contenedor seguirá remapeada.
Lo que esto significa es que todo el sistema de archivos del contenedor pertenecerá al usuario especificado en la configuración del demonio --userns-remap (231072 en el ejemplo anterior). Esto puede provocar comportamientos inesperados de los programas dentro del contenedor. Por ejemplo, sudo (que comprueba que sus binarios pertenezcan al usuario 0) o binarios con una bandera setuid.
Limitaciones conocidas de los espacios de nombres de usuario
Las siguientes características estándar de Docker son incompatibles con la ejecución de un demonio de Docker con espacios de nombres de usuario habilitados:
- Compartir espacios de nombres PID o NET con el anfitrión (
--pid=hosto--network=host). - Controladores externos (de volumen o almacenamiento) que no sean conscientes o no sean capaces de utilizar los mapeos de usuario del demonio.
- Utilizar la bandera de modo
--privilegedendocker runsin especificar también--userns=host.
Los espacios de nombres de usuario son una característica avanzada y requieren coordinación con otras capacidades. Por ejemplo, si se montan volúmenes desde el anfitrión, la propiedad de los archivos debe organizarse de antemano si necesitas acceso de lectura o escritura al contenido del volumen.
Aunque el usuario root dentro de un proceso de contenedor con espacio de nombres de usuario tiene muchos de los privilegios esperados del superusuario dentro del contenedor, el kernel de Linux impone restricciones basadas en el conocimiento interno de que se trata de un proceso con espacio de nombres de usuario. Una restricción notable es la imposibilidad de usar el comando mknod. Se deniega el permiso para la creación de dispositivos dentro del contenedor cuando es ejecutado por el usuario root.