Compartir comentarios
Las respuestas se generan en base a la documentación.

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.

Note

Con 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:65536

Esto 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.

Note

Es 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/subuid o /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_map y /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.

Warning

Algunas distribuciones no añaden automáticamente el nuevo grupo a los archivos /etc/subuid y /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

  1. 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/passwd y /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)
    
  2. 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/subuid y /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:65536

    Esto significa que los procesos con espacio de nombres de usuario iniciados por testuser pertenecen al UID del anfitrión 231072 (que parece el UID 0 dentro 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/subuid y /etc/subgid para 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 dockremap creado automáticamente por Docker, busca la entrada dockremap en estos archivos después de configurar y reiniciar Docker.

  3. 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 dockremap creado automáticamente por Docker, pero no podrás modificar los permisos hasta después de configurar y reiniciar Docker.

  4. Habilitar userns-remap enmascara 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.

  5. 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"
  1. Edita /etc/docker/daemon.json. Asumiendo que el archivo estaba vacío previamente, la siguiente entrada habilita userns-remap utilizando el usuario y grupo llamado testuser. 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 de testuser son 1001:

    • testuser
    • testuser:testuser
    • 1001
    • 1001:1001
    • testuser:1001
    • 1001:testuser
    {
      "userns-remap": "testuser"
    }
    Note

    Para utilizar el usuario dockremap y que Docker lo cree por ti, establece el valor en default en lugar de testuser.

    Guarda el archivo y reinicia Docker.

  2. Si utilizas el usuario dockremap, verifica que Docker lo haya creado utilizando el comando id.

    $ id dockremap
    
    uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)
    

    Verifica que la entrada se haya añadido a /etc/subuid y /etc/subgid:

    $ grep dockremap /etc/subuid
    
    dockremap:231072:65536
    
    $ grep dockremap /etc/subgid
    
    dockremap:231072:65536
    

    If these entries are not present, edit the files as the root user 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.

  3. Verifica que las imágenes anteriores no estén disponibles utilizando el comando docker image ls. La salida debería estar vacía.

  4. Inicia un contenedor desde la imagen hello-world.

    $ docker run hello-world
    
  5. Verifica 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 a root y 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 volumes
    

    Tu 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 mientras userns-remap esté 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=host o --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 --privileged en docker run sin 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.