Permiso denegado al acceder al directorio del host en Docker

283

En resumen: estoy tratando de montar un directorio de host en Docker, pero luego no puedo acceder desde el contenedor, incluso si los permisos de acceso se ven bien.

Los detalles:

estoy haciendo

sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash

y entonces

ls -al

Me da:

total 8892
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 .
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 ..
-rwxr-xr-x.   1 root root       0 Jun 18 14:34 .dockerenv
-rwx------.   1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x.  18 1000 1000   12288 Jun 16 11:40 Downloads
drwxr-xr-x.   2 root root    4096 Jan 29 18:10 bin
drwxr-xr-x.   2 root root    4096 Apr 19  2012 boot
drwxr-xr-x.   4 root root     340 Jun 18 14:34 dev
drwxr-xr-x.  56 root root    4096 Jun 18 14:34 etc
drwxr-xr-x.   2 root root    4096 Apr 19  2012 home

y muchas más líneas como esa (creo que esta es la parte relevante).

Si lo hago

cd /Downloads
ls

el resultado es

ls: cannot open directory .: Permission denied

El host es Fedora 20, con Docker 1.0.0 y go1.2.2.

¿Qué va mal?

usuario3753011
fuente

Respuestas:

269

Vea esta publicación del blog de Project Atomic sobre Volumes y SELinux para ver la historia completa.

Específicamente:

Esto se hizo más fácil recientemente, ya que Docker finalmente fusionó un parche que se mostrará en docker-1.7 (Hemos estado llevando el parche en docker-1.6 en RHEL, CentOS y Fedora).

Este parche agrega soporte para "z" y "Z" como opciones en los montajes de volumen (-v).

Por ejemplo:

docker run -v /var/db:/var/db:z rhel7 /bin/sh

Hará automáticamente lo chcon -Rt svirt_sandbox_file_t /var/db descrito en la página del manual.

Aún mejor, puedes usar Z.

docker run -v /var/db:/var/db:Z rhel7 /bin/sh

Esto etiquetará el contenido dentro del contenedor con la etiqueta MCS exacta con la que se ejecutará el contenedor, básicamente se ejecuta chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/dbdonde s0:c1,c2difiere para cada contenedor.

gregswift
fuente
18
Esto funciona como un encanto. Otras soluciones son en su mayoría soluciones alternativas.
tuxdna
44
cf. la sección de etiquetas de volumen en la documentación de la
ventana
Oh, hombre, realmente funciona. Finalmente encontré esto. ¡Muchas gracias! ¿Hay alguna documentación oficial sobre esto?
Kirby el
1
Upstream lo tiene como el último párrafo de esta sección docs.docker.com/engine/reference/commandline/run/…
gregswift
1
Es posible fijar los permisos bajo SELinux mientras que el montaje del volumen como de sólo lectura, al mismo tiempo mediante el uso de ambas opciones al mismo tiempo separados por una coma: -v $(pwd):/app:ro,Z. Esto debe marcarse como la respuesta correcta.
danirod
264

Es un problema de SELinux .

Puedes emitir temporalmente

su -c "setenforce 0"

en el host para acceder o agregar una regla SELinux ejecutando

chcon -Rt svirt_sandbox_file_t /path/to/volume
usuario3761313
fuente
3
¿Es / ruta / a / volumen la ruta del host? Si es así, ¿no parece que esta solución funcionaría con contenedores de datos?
Roy Truelove
66
no olvide hacer su -c "setenforce 1" ... de lo contrario funcionará solo porque SELinux todavía está desactivado
vcarel
Esto resolvió mi problema. gracias, espero que tengan una solución para esto.
Hokutosei
19
Agregar la regla selinux es la mejor manera, ya que no es una buena idea en la mayoría de los casos ejecutar contenedores con modo privilegiado.
Zoro_77
77
Como dijo Zoro_77, agregue una regla y stopdisablingselinux.com ;)
GabLeRoux
71

ADVERTENCIA: esta solución tiene riesgos de seguridad.

Intente ejecutar el contenedor como privilegiado:

sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash

Otra opción (que no he probado) sería crear un contenedor privilegiado y luego crear contenedores no privilegiados dentro de él.

John Phillips
fuente
1
@JBernardo ¿Cuál de las dos opciones resolvió el problema?
user100464
@ user100464--privileged=true
JBernardo
1
No ayudes en mi caso. Debian Whezzy con kernel 3.16 soportado pero sin configuración SELinux activada. :(
aholbreich
si está usando docker-composer agregue 'privilegiado: verdadero'
Lionel Morrison
35
No hagas esto. --privilegedes un riesgo de seguridad
Navin
38

Normalmente, los problemas de permisos con un montaje de volumen de host se deben a que el uid / gid dentro del contenedor no tiene acceso al archivo de acuerdo con los permisos uid / gid del archivo en el host. Sin embargo, este caso específico es diferente.

El punto al final de la cadena de permiso drwxr-xr-x., indica que SELinux está configurado. Al usar un montaje de host con SELinux, debe pasar una opción adicional al final de la definición de volumen:

  • La zopción indica que el contenido de montaje de enlace se comparte entre varios contenedores.
  • La Zopción indica que el contenido de montaje de enlace es privado y no compartido.

Su comando de montaje de volumen se vería así:

sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash

Vea más sobre los montajes de host con SELinux en: https://docs.docker.com/storage/#configure-the-selinux-label


Para otros que ven este problema con los contenedores que se ejecutan como un usuario diferente, debe asegurarse de que el uid / gid del usuario dentro del contenedor tenga permisos para el archivo en el host. En los servidores de producción, esto a menudo se realiza controlando el uid / gid en el proceso de creación de imágenes para que coincida con un uid / gid en el host que tiene acceso a los archivos (o mejor aún, no use montajes de host en producción).

A menudo se prefiere un volumen con nombre a los montajes de host porque inicializará el directorio del volumen desde el directorio de la imagen, incluidos los permisos y la propiedad del archivo. Esto sucede cuando el volumen está vacío y el contenedor se crea con el volumen con nombre.

Los usuarios de MacOS ahora tienen OSXFS que maneja uid / gid automáticamente entre el host de Mac y los contenedores. Un lugar con el que no ayuda son los archivos del interior de la VM incorporada que se montan en el contenedor, como /var/lib/docker.sock.

Para entornos de desarrollo donde el uid / gid del host puede cambiar por desarrollador, mi solución preferida es iniciar el contenedor con un punto de entrada que se ejecute como root, arreglar el uid / gid del usuario dentro del contenedor para que coincida con el volumen del host uid / gid, y luego use gosupara soltar desde la raíz al usuario del contenedor para ejecutar la aplicación dentro del contenedor. El guión importante para esto está fix-permsen mis guiones de imágenes base, que se pueden encontrar en: https://github.com/sudo-bmitch/docker-base

Lo importante del fix-permsscript es:

# update the uid
if [ -n "$opt_u" ]; then
  OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
  NEW_UID=$(stat -c "%u" "$1")
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

Eso lleva el uid del usuario dentro del contenedor y el uid del archivo, y si no coinciden, llama usermodpara ajustar el uid. Por último, realiza una búsqueda recursiva para corregir cualquier archivo que no haya cambiado los uid. Me gusta más que ejecutar un contenedor con un -u $(id -u):$(id -g)indicador porque el código del punto de entrada anterior no requiere que cada desarrollador ejecute un script para iniciar el contenedor, y cualquier archivo fuera del volumen que sea propiedad del usuario tendrá sus permisos corregidos.


También puede hacer que Docker inicialice un directorio de host desde una imagen utilizando un volumen con nombre que realiza un montaje de enlace. Este directorio debe existir de antemano y debe proporcionar una ruta absoluta al directorio del host, a diferencia de los volúmenes del host en un archivo de composición que pueden ser rutas relativas. El directorio también debe estar vacío para que Docker lo inicialice. Se ven tres opciones diferentes para definir un volumen con nombre para un montaje de enlace:

  # create the volume in advance
  $ docker volume create --driver local \
      --opt type=none \
      --opt device=/home/user/test \
      --opt o=bind \
      test_vol

  # create on the fly with --mount
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

  # inside a docker-compose file
  ...
  volumes:
    bind-test:
      driver: local
      driver_opts:
        type: none
        o: bind
        device: /home/user/test
  ...

Por último, si intenta usar espacios de nombres de usuario, encontrará que los volúmenes de host tienen problemas de permisos porque los uid / gid de los contenedores están desplazados. En ese escenario, probablemente sea más fácil evitar los volúmenes de host y solo usar volúmenes con nombre.

BMitch
fuente
32

Desde access.redhat.com:Sharing_Data_Across_Containers :

La configuración del volumen del host no es portátil, ya que dependen del host y podrían no funcionar en ninguna otra máquina. Por esta razón, no hay un equivalente de Dockerfile para montar directorios host en el contenedor. Además, tenga en cuenta que el sistema host no tiene conocimiento de la política SELinux del contenedor. Por lo tanto, si se aplica la política de SELinux, el directorio de host montado no se puede escribir en el contenedor, independientemente de la configuración de rw. Actualmente, puede solucionar este problema asignando el tipo de política SELinux adecuado al directorio del host ":

chcon -Rt svirt_sandbox_file_t host_dir

Donde host_dir es una ruta al directorio en el sistema host que está montado en el contenedor.

Parece ser solo una solución, pero lo intenté y funciona.

Thomas8
fuente
14

Verifiqué que chcon -Rt svirt_sandbox_file_t /path/to/volumefunciona y que no tiene que ejecutarse como un contenedor privilegiado.

Esto está en:

  • Docker versión 0.11.1-dev, compilación 02d20af / 0.11.1
  • CentOS 7 como host y contenedor con SELinux habilitado.
Jeff McCormick
fuente
2
Visite github.com/docker/docker/pull/5910 para obtener asistencia oficial para volver a etiquetar dentro de Docker.
cpuguy83
13

Tratar docker volume create.

mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash

Eche un vistazo al documento https://docs.docker.com/engine/reference/commandline/volume_create/

cupen
fuente
3
Intenté muchas respuestas sobre este tema en SO, pero en realidad esta ayudó. ¡Gracias!
Paul
Solucionó el error de permiso. Pero ahora, si estoy tratando de montar la ubicación física, se monta voulme ???? @ cupen
kunal verma
1
@kunalverma Sí. Si no te gusta, aquí está la respuesta más fácil. stackoverflow.com/a/31334443/4909388
cupen el
4

Tuve un problema similar, el mío fue causado por una falta de coincidencia entre el UID del host y el UID del usuario del contenedor. La solución fue pasar el UID del usuario como argumento para la construcción de la ventana acoplable y crear el usuario del contenedor con el mismo UID.

En el DockerFile:

ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER

En el paso de compilación:

docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID

Después de eso, ejecutar el contenedor y los comandos según el OP me dio el resultado esperado.

RoboCop87
fuente
1
¿Qué sucede si no conoce el UID hasta el tiempo de ejecución? (Estoy creando una imagen para colegas, para empaquetar algunas herramientas que escriben en su sistema de archivos, pero tienen UID diferentes). ¿Supongo que podría mantenerlo root y solo adduser en ejecución?
Inger
No tengo una buena respuesta para eso, desafortunadamente. Si alguien más tiene una solución, también me interesaría. Sospecho que la funcionalidad del punto de entrada de Docker podría proporcionar una solución.
RoboCop87
0

Resolví ese problema usando un contenedor de datos, esto también tiene la ventaja de aislar los datos de la capa de aplicación. Podrías ejecutarlo así:

docker run --volumes-from=<container-data-name> ubuntu

Este tutorial proporciona una buena explicación sobre el uso de contenedores de datos.

tmsss
fuente
-1

En mi situación, el problema era diferente. No sé por qué, pero incluso si el directorio en el host se había chmod 777ejecutado en él, dentro de la ventana acoplable estaba visible como 755.

Correr dentro del contenedor lo sudo chmod 777 my_volume_dirarregló.

CodeSandwich
fuente
55
chmod 777Casi nunca arregla nada.
Erki Aring,
Lo siento, pero has perdido el punto. El punto es que los privilegios del contenedor interno se redujeron y no se pudo arreglar desde el exterior.
CodeSandwich
-2

sudo -s hizo el truco para mí en MAC

Nachiket Joshi
fuente
1
Si está votando negativamente, deje un comentario y explique por qué. Encontré exactamente el mismo problema y pude resolver esto con sudo -s.
Nachiket Joshi
No todas las imágenes de Docker tienen sudo, y no es posible en todos los escenarios.
SOFe
2
No instales sudo en contenedores. Un atacante puede usar sudo dentro de un contenedor.
Arnold Balliu el