¿Cuál es la (mejor) forma de administrar permisos para volúmenes compartidos de Docker?

Resuelto Xabs asked hace 10 años • 15 respuestas

He estado jugando con Docker por un tiempo y sigo encontrando el mismo problema al tratar con datos persistentes.

Creo Dockerfiley expongo un volumen o lo uso --volumes-frompara montar una carpeta de host dentro de mi contenedor .

¿Qué permisos debo aplicar al volumen compartido en el host?

Puedo pensar en dos opciones:

  • Hasta ahora les he dado a todos acceso de lectura/escritura, para poder escribir en la carpeta desde el contenedor Docker.

  • Asigne a los usuarios del host al contenedor, para poder asignar permisos más granulares. Sin embargo, no estoy seguro de que esto sea posible y no he encontrado mucho al respecto. Hasta ahora, todo lo que puedo hacer es ejecutar el contenedor como algún usuario:, docker run -i -t -user="myuser" postgrespero este usuario tiene un UID diferente al de mi host myuser, por lo que los permisos no funcionan. Además, no estoy seguro de si mapear a los usuarios planteará algunos riesgos de seguridad.

¿Existen otras alternativas?

¿Cómo están lidiando ustedes con este problema?

Xabs avatar May 08 '14 21:05 Xabs
Aceptado

ACTUALIZACIÓN 2016-03-02 : A partir de Docker 1.9.0, Docker tiene volúmenes con nombre que reemplazan los contenedores de solo datos . La respuesta a continuación, así como mi publicación de blog vinculada, todavía tiene valor en el sentido de cómo pensar en los datos dentro de la ventana acoplable, pero considere usar volúmenes con nombre para implementar el patrón que se describe a continuación en lugar de contenedores de datos.


Creo que la forma canónica de resolver esto es mediante el uso de contenedores de solo datos . Con este enfoque, todo el acceso a los datos del volumen se realiza a través de contenedores que utilizan -volumes-fromel contenedor de datos, por lo que el uid/gid del host no importa.

Por ejemplo, un caso de uso que se proporciona en la documentación es realizar una copia de seguridad de un volumen de datos. Para hacer esto, se usa otro contenedor para hacer la copia de seguridad a través de tar, y también se usa -volumes-frompara montar el volumen. Así que creo que el punto clave para asimilar es: en lugar de pensar en cómo obtener acceso a los datos en el host con los permisos adecuados, piense en cómo hacer lo que necesite (copias de seguridad, navegación, etc.) a través de otro contenedor. . Los propios contenedores necesitan utilizar uid/gids consistentes, pero no necesitan asignarse a nada en el host, por lo que siguen siendo portátiles.

Esto también es relativamente nuevo para mí, pero si tiene un caso de uso particular, no dude en comentarlo e intentaré ampliar la respuesta.

ACTUALIZACIÓN : Para el caso de uso indicado en los comentarios, es posible que tenga una imagen some/graphitepara ejecutar grafito y una imagen some/graphitedatacomo contenedor de datos. Entonces, ignorando los puertos y demás, la Dockerfileimagen some/graphitedataes algo como:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
RUN mkdir -p /data/graphite \
  && chown -R graphite:graphite /data/graphite
VOLUME /data/graphite
USER graphite
CMD ["echo", "Data container for graphite"]

Construya y cree el contenedor de datos:

docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata

El some/graphiteDockerfile también debería tener el mismo uid/gids, por lo tanto, podría verse así:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
# ... graphite installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]

Y se ejecutaría de la siguiente manera:

docker run --volumes-from=graphitedata some/graphite

Bien, ahora eso nos da nuestro contenedor de grafito y el contenedor de solo datos asociado con el usuario/grupo correcto (tenga en cuenta que también puede reutilizar el some/graphitecontenedor para el contenedor de datos, anulando la entrada/cmd al ejecutarlo, pero teniéndolos como imágenes separadas en mi opinión son más claras).

Ahora, digamos que desea editar algo en la carpeta de datos. Entonces, en lugar de vincular el montaje del volumen al host y editarlo allí, cree un nuevo contenedor para hacer ese trabajo. Llamémoslo some/graphitetools. Creemos también el usuario/grupo apropiado, como en la some/graphiteimagen.

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]

Puede hacer que esto sea SECO heredando desde some/graphiteo some/graphitedataen el Dockerfile, o en lugar de crear una nueva imagen, simplemente reutilice una de las existentes (anulando el punto de entrada/cmd según sea necesario).

Ahora simplemente ejecuta:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools

y luego vi /data/graphite/whatever.txt. Esto funciona perfectamente porque todos los contenedores tienen el mismo usuario de grafito con uid/gid coincidente.

Como nunca monta /data/graphitedesde el host, no le importa cómo el uid/gid del host se asigna al uid/gid definido dentro de los contenedores graphiteand graphitetools. Esos contenedores ahora se pueden implementar en cualquier host y seguirán funcionando perfectamente.

Lo bueno de esto es que graphitetoolspodría tener todo tipo de utilidades y scripts útiles, que ahora también puedes implementar de forma portátil.

ACTUALIZACIÓN 2 : Después de escribir esta respuesta, decidí escribir una publicación de blog más completa sobre este enfoque. Espero que ayude.

ACTUALIZACIÓN 3 : corregí esta respuesta y agregué más detalles. Anteriormente contenía algunas suposiciones incorrectas sobre la propiedad y los permisos: la propiedad generalmente se asigna en el momento de la creación del volumen, es decir, en el contenedor de datos, porque es entonces cuando se crea el volumen. Ver este blog . Sin embargo, esto no es un requisito: puede usar el contenedor de datos como una "referencia/identificador" y establecer la propiedad/permisos en otro contenedor mediante chown en un punto de entrada, que termina con gosu para ejecutar el comando como el usuario correcto. Si alguien está interesado en este enfoque, comente y puedo proporcionar enlaces a una muestra que utiliza este enfoque.

Raman avatar Nov 19 '2014 15:11 Raman

Se puede ver una solución muy elegante en la imagen oficial de Redis y, en general, en todas las imágenes oficiales.

Descrito en el proceso paso a paso:

  • Crear usuario/grupo de Redis antes que nada

Como se ve en los comentarios de Dockerfile:

agregue nuestro usuario y grupo primero para asegurarse de que sus ID se asignen de manera consistente, independientemente de las dependencias que se agreguen

  • Instalar gosu con Dockerfile

gosu es una alternativa de su/ sudopara bajar fácilmente del usuario root. (Redis siempre se ejecuta con redisel usuario)

  • Configure /datael volumen y configúrelo como directorio de trabajo

Al configurar el volumen /data con el VOLUME /datacomando, ahora tenemos un volumen separado que puede ser un volumen acoplable o estar vinculado a un directorio de host.

Configurarlo como workdir ( WORKDIR /data) lo convierte en el directorio predeterminado desde donde se ejecutan los comandos.

  • Agregue el archivo docker-entrypoint y configúrelo como ENTRYPOINT con el servidor CMD redis predeterminado

Esto significa que todas las ejecuciones del contenedor se ejecutarán a través del script docker-entrypoint y, de forma predeterminada, el comando que se ejecutará es redis-server.

docker-entrypointes un script que realiza una función simple: cambiar la propiedad del directorio actual (/data) y bajar de rootusuario redispara ejecutarlo redis-server. (Si el comando ejecutado no es redis-server, ejecutará el comando directamente).

Esto tiene el siguiente efecto

Si el directorio /data está vinculado al host, el punto de entrada de la ventana acoplable preparará los permisos del usuario antes de ejecutar redis-server bajo redisel usuario.

Esto le brinda la tranquilidad de saber que no es necesaria ninguna configuración para ejecutar el contenedor en cualquier configuración de volumen.

Por supuesto, si necesita compartir el volumen entre diferentes imágenes, debe asegurarse de que utilicen el mismo ID de usuario/ID de grupo, de lo contrario, el último contenedor secuestrará los permisos de usuario del anterior.

Dimitris avatar Apr 22 '2015 13:04 Dimitris

Podría decirse que esta no es la mejor manera en la mayoría de las circunstancias, pero aún no se ha mencionado, por lo que quizás ayude a alguien.

  1. Vincular el volumen del host de montaje

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. Modifique el script de inicio de su contenedor para encontrar el GID del volumen que le interesa

    $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)

  3. Asegúrese de que su usuario pertenezca a un grupo con este GID (es posible que deba crear un grupo nuevo). Para este ejemplo, simularé que mi software se ejecuta como nobodyusuario cuando está dentro del contenedor, por lo que quiero asegurarme de nobodyque pertenece a un grupo con una identificación de grupo igual aTARGET_GID

  EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)

  # Create new group using target GID and add nobody user
  if [ $EXISTS == "0" ]; then
    groupadd -g $TARGET_GID tempgroup
    usermod -a -G tempgroup nobody
  else
    # GID exists, find group name and add
    GROUP=$(getent group $TARGET_GID | cut -d: -f1)
    usermod -a -G $GROUP nobody
  fi

Me gusta esto porque puedo modificar fácilmente los permisos de grupo en mis volúmenes de host y saber que esos permisos actualizados se aplican dentro del contenedor acoplable. Esto sucede sin ningún permiso o modificación de propiedad en mis carpetas/archivos de host, lo que me hace feliz.

No me gusta esto porque asume que no hay peligro al agregarse a grupos arbitrarios dentro del contenedor que están usando el GID que desea. No se puede usar con una USERcláusula en un Dockerfile (a menos que ese usuario tenga privilegios de root, supongo). Además, grita trabajo de hackeo ;-)

Si desea ser incondicional, obviamente puede extender esto de muchas maneras; por ejemplo, buscar todos los grupos en cualquier subarchivo, múltiples volúmenes, etc.

Hamy avatar Feb 19 '2015 00:02 Hamy

Al igual que usted, estaba buscando una forma de asignar usuarios/grupos desde el host a los contenedores acoplables y esta es la forma más corta que he encontrado hasta ahora:

  version: "3"
  services:
    my-service:
      .....
      volumes:
        # take uid/gid lists from host
        - /etc/passwd:/etc/passwd:ro
        - /etc/group:/etc/group:ro
        # mount config folder
        - path-to-my-configs/my-service:/etc/my-service:ro
        .....

Este es un extracto de mi docker-compose.yml.

La idea es montar (en modo de solo lectura) listas de usuarios/grupos desde el host al contenedor, de modo que después de que se inicie el contenedor tendrá las mismas coincidencias uid->nombre de usuario (así como para grupos) con el host. Ahora puede configurar los ajustes de usuario/grupo para su servicio dentro del contenedor como si estuviera funcionando en su sistema host.

Cuando decide mover su contenedor a otro host, solo necesita cambiar el nombre de usuario en el archivo de configuración del servicio al que tiene en ese host.

Alex Myznikov avatar Aug 11 '2017 17:08 Alex Myznikov

Intente agregar un comando a Dockerfile

RUN usermod -u 1000 www-data

los créditos van a https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

FDisk avatar Nov 09 '2015 18:11 FDisk