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


He estado jugando con Docker durante un tiempo y sigo encontrando el mismo problema cuando se trata de datos persistentes.

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

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

Se me ocurren dos opciones:

  • Hasta ahora he dado a todos acceso de lectura / escritura, para que pueda escribir en la carpeta desde la ventana acoplable contenedor.

  • Asigne los usuarios del host al contenedor, para que pueda 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" postgres, pero este usuario tiene un UID diferente que mi host myuser, por lo que los permisos no funcionan. Además, no estoy seguro de si el mapeo de los usuarios planteará algunos riesgos de seguridad.

¿hay otras alternativas?

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

 277
Author: Peter Mortensen, 2014-05-08

13 answers

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


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

Por ejemplo, un caso de uso dado en la documentación es la copia de seguridad de un volumen de datos. Para hacer esto se utiliza otro contenedor para hacer la copia de seguridad a través de tar, y también utiliza -volumes-from para montar el volumen. Así que creo que el punto clave de grok es: en lugar de pensar en cómo obtener acceso a los datos en el host con los permisos adecuados, piense en cómo lo que necesites backups copias de seguridad, navegación, etc. -- a través de otro contenedor. Los propios contenedores necesitan usar uid/gids consistentes, pero no necesitan mapear a nada en el host, por lo que permanecen portátiles.

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

UPDATE: Para el caso de uso dado en los comentarios, es posible que tenga una imagen some/graphite para ejecutar graphite, y una imagen some/graphitedata como el contenedor de datos. Por lo tanto, ignorando los puertos y demás, el Dockerfile de la imagen some/graphitedata es algo así 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"]

Construir y crear el contenedor de datos:

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

El archivo Dockerfile some/graphite también debería obtener el mismo uid / gids, por lo que podría verse algo como esto:

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

Ok, ahora nos da nuestro contenedor de grafito y el contenedor de solo datos asociado con el usuario/grupo correcto (tenga en cuenta que podría reutilizar el contenedor some/graphite para el contenedor de datos también, sobreescribir el entrypoing / cmd al ejecutarlo, pero tenerlos como imágenes separadas IMO es más claro).

Ahora, digamos que desea editar algo en la carpeta de datos. Entonces, en lugar de enlazar el montaje del volumen al host y editarlo allí, cree un nuevo contenedor para hacer ese trabajo. Vamos a llamarlo some/graphitetools. También vamos a crear el usuario/grupo apropiado, al igual que la imagen some/graphite.

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"]

Usted podría hacer esto seco heredando de some/graphite o some/graphitedata en el Dockerfile, o en lugar de crear una nueva imagen simplemente reutilice una de las existentes (anulando entrypoint/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 coincidentes.

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

Lo bueno de esto es que graphitetools podría tener todo tipo de utilidades y scripts útiles, que ahora también puede implementar de manera portátil.

ACTUALIZACIÓN 2 : Después de escribir esta respuesta, decidí escribir una entrada 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 algunos incorrectos suposiciones sobre propiedad y perms usually la propiedad generalmente se asigna en el tiempo de creación del volumen, es decir, en el contenedor de datos, porque es cuando se crea el volumen. Ver este blog. Sin embargo, esto no es un requisito you puede usar el contenedor de datos como "referencia/manejador" y establecer la propiedad/permanente en otro contenedor a través de 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, por favor comente y puedo proporcione enlaces a una muestra utilizando este enfoque.

 149
Author: Raman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-02 16:48:09

Una solución muy elegante se puede ver 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 primero nuestro usuario y grupo 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 / sudo para un fácil paso hacia abajo desde el usuario root. (Redis siempre se ejecuta con redis usuario)

  • Configure /data volume y establézcalo como workdir

Configurando el volumen /data con el comando VOLUME /data ahora tenemos un volumen separado que puede ser volumen docker o montado en un directorio host.

Configurándolo como el workdir (WORKDIR /data) hace que sea el directorio predeterminado donde se ejecutan los comandos de.

  • Agregue el archivo docker-entrypoint y establézcalo como ENTRYPOINT con CMD predeterminado redis-server

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

docker-entrypoint es un script que hace una función simple: Cambiar la propiedad del directorio actual (/data) y pasar de root a redis usuario para ejecutar redis-server. (Si el comando ejecutado no es redis-server, ejecutará el comando directamente.)

Esto tiene el siguiente efecto

Si el directorio /data está montado en el host, el docker-entrypoint preparará los permisos de usuario antes de ejecutar redis-server bajo redis user.

Esto le da la facilidad de pensar que hay cero configuración para ejecutar el contenedor bajo cualquier configuración de volumen.

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

 39
Author: Dimitris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-24 09:26:42

Podría decirse que esta no es la mejor manera para la mayoría de las circunstancias, pero no se ha mencionado todavía, así que tal vez ayudará a alguien.

  1. Bind mount host volume

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. Modifica el script de inicio de tu contenedor para encontrar el GID del volumen que te interesa

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

  3. Asegúrese de que su usuario pertenece a un grupo con este GID (es posible que tenga que crear un nuevo grupo). Para este ejemplo pretenderé que mi software se ejecuta como el usuario nobody cuando está dentro del contenedor, así que quiero asegurarme de que nobody pertenece a un grupo con un id de grupo igual a TARGET_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 docker. Esto sucede sin ningún permiso o modificaciones de propiedad a mis carpetas/archivos de host, lo que me hace feliz.

No me gusta esto porque asume que no hay peligro en agregar usted mismo a un grupo arbitrario dentro del contenedor que pasa a estar utilizando un GID que desea. No se puede usar con una cláusula USER en un Dockerfile (a menos que ese usuario tenga privilegios de root, supongo). Además, grita trabajo hack; -)

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

 28
Author: Hamy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-14 18:13:49

Ok, esto ahora está siendo rastreado en docker issue #7198

Por ahora, estoy tratando con esto usando su segunda opción:

Asigna los usuarios del host al contenedor

Dockerfile

#=======
# Users
#=======
# TODO: Idk how to fix hardcoding uid & gid, specifics to docker host machine
RUN (adduser --system --uid=1000 --gid=1000 \
        --home /home/myguestuser --shell /bin/bash myguestuser)

CLI

# DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest

ACTUALIZACIÓN actualmente estoy más inclinado a Hamy respuesta

 15
Author: Leo Gallucci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:29

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

 8
Author: FDisk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-09 18:07:46

Al igual que tú, estaba buscando una forma de mapear usuarios/grupos desde contenedores host a contenedores docker 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, por lo que después de que el contenedor se inicie tendrá el mismo uid->username (así como para grupos) coincidencias con el host. Ahora puede configurar la configuración de usuario / grupo para su servicio dentro del contenedor como si estuviera trabajando en su sistema host.

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

 6
Author: alex_edev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 17:11:05

Este es un enfoque que todavía utiliza un contenedor de solo datos, pero no requiere que se sincronice con el contenedor de la aplicación (en términos de tener el mismo uid/gid).

Presumiblemente, desea ejecutar alguna aplicación en el contenedor como un USUARIO non no root sin un shell de inicio de sesión.

En el Dockerfile:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

Entonces, en entrypoint.sh:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"
 5
Author: Ethan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-11 19:56:13

Para asegurar y cambiar la raíz para el contenedor de docker un host de docker intente usar --uidmap y --private-uids opciones

Https://github.com/docker/docker/pull/4572#issuecomment-38400893

También puede eliminar varias capacidades (--cap-drop) en el contenedor docker para la seguridad

Http://opensource.com/business/14/9/security-for-docker

ACTUALIZACIÓN el soporte debe venir en docker > 1.7.0

UPDATE Version 1.10.0 (2016-02-04) add --userns-remap bandera https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2

 4
Author: umount,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-12 03:22:16

Imagen base

Usar esta imagen: https://hub.docker.com/r/reduardo7/docker-host-user

O

Importante: esto destruye la portabilidad de contenedores entre hosts.

1) init.sh

#!/bin/bash

if ! getent passwd $DOCKDEV_USER_NAME > /dev/null
  then
    echo "Creating user $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME"
    groupadd --gid $DOCKDEV_GROUP_ID -r $DOCKDEV_GROUP_NAME
    useradd --system --uid=$DOCKDEV_USER_ID --gid=$DOCKDEV_GROUP_ID \
        --home-dir /home --password $DOCKDEV_USER_NAME $DOCKDEV_USER_NAME
    usermod -a -G sudo $DOCKDEV_USER_NAME
    chown -R $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME /home
  fi

sudo -u $DOCKDEV_USER_NAME bash

2) Dockerfile

FROM ubuntu:latest
# Volumes
    VOLUME ["/home/data"]
# Copy Files
    COPY /home/data/init.sh /home
# Init
    RUN chmod a+x /home/init.sh

3) run.sh

#!/bin/bash

DOCKDEV_VARIABLES=(\
  DOCKDEV_USER_NAME=$USERNAME\
  DOCKDEV_USER_ID=$UID\
  DOCKDEV_GROUP_NAME=$(id -g -n $USERNAME)\
  DOCKDEV_GROUP_ID=$(id -g $USERNAME)\
)

cmd="docker run"

if [ ! -z "${DOCKDEV_VARIABLES}" ]; then
  for v in ${DOCKDEV_VARIABLES[@]}; do
    cmd="${cmd} -e ${v}"
  done
fi

# /home/usr/data contains init.sh
$cmd -v /home/usr/data:/home/data -i -t my-image /home/init.sh

4) Construir con docker

4) ¡Corre!

sh run.sh
 3
Author: Eduardo Cuomo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-25 15:20:11

Para compartir la carpeta entre el host de docker y el contenedor de docker, intente debajo del comando

$ docker run-v "$(pwd):$(pwd)" -i-t ubuntu

El indicador-v monta el directorio de trabajo actual en el contenedor. Cuando el directorio host de un volumen montado en bind no existe, Docker creará automáticamente este directorio en el host para usted,

Sin embargo, hay 2 problemas que tenemos aquí:

  1. No puede escribir en el volumen montado si usuario no root porque el archivo compartido será propiedad de otro usuario en el host,
  2. No debe ejecutar el proceso dentro de sus contenedores como root, pero incluso si se ejecuta como un usuario codificado, todavía no coincidirá con el usuario en su computadora portátil/Jenkins,

Solución:

Contenedor: crear un usuario decir 'testuser', por defecto id de usuario será a partir de 1000,

Host: crear un grupo decir 'testgroup' con el id de grupo 1000, y chown el directorio a la nuevo grupo (testgroup

 1
Author: Praveen Muthusamy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-16 17:29:54

Mi enfoque es detectar UID/GID actual y luego crear dicho usuario / grupo dentro del contenedor y ejecutar el script debajo de él, por lo que todos los archivos que creará coincidirán con el usuario que ejecuta el script:

# get location of this script no matter what your current folder is, this might break between shells so make sure you run bash
LOCAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# get current IDs
USER_ID=$(id -u)
GROUP_ID=$(id -g)

echo "Mount $LOCAL_DIR into docker, and match the host IDs ($USER_ID:$GROUP_ID) inside the container."

docker run -v $LOCAL_DIR:/host_mount -i debian:9.4-slim bash -c "set -euo pipefail && groupadd -r -g $GROUP_ID lowprivgroup && useradd -u $USER_ID lowprivuser -g $GROUP_ID && cd /host_mount && su -c ./runMyScriptAsRegularUser.sh lowprivuser"
 1
Author: muni764,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-08 15:00:26

En mi caso específico, estaba tratando de construir mi paquete de nodos con la imagen de la ventana acoplable de nodos para que no tuviera que instalar npm en el servidor de implementación. Funcionó bien hasta que, fuera del contenedor y en la máquina host, traté de mover un archivo al directorio node_modules que la imagen de docker de nodo había creado, al que se me denegaron los permisos porque era propiedad de root. Me di cuenta de que podía solucionar esto copiando el directorio del contenedor en el host equipo. Vía docker docs...

Los archivos copiados en la máquina local se crean con el UID: GID de la usuario que invocó el comando docker cp.

Este es el código bash que utilicé para cambiar la propiedad del directorio creado por y dentro del contenedor docker.

NODE_IMAGE=node_builder
docker run -v $(pwd)/build:/build -w="/build" --name $NODE_IMAGE node:6-slim npm i --production
# node_modules is owned by root, so we need to copy it out 
docker cp $NODE_IMAGE:/build/node_modules build/lambda 
# you might have issues trying to remove the directory "node_modules" within the shared volume "build", because it is owned by root, so remove the image and its volumes
docker rm -vf $NODE_IMAGE || true

Si es necesario, puede eliminar el directorio con un segundo contenedor docker.

docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
 0
Author: johnklawlor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-21 19:58:23

Si utiliza Docker Compose, inicie el contenedor en modo con privilegios previos:

wordpress:
    image: wordpress:4.5.3
    restart: always
    ports:
      - 8084:80
    privileged: true
 -2
Author: Renato Alencar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-07 20:01:53