¿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 Dockerfile
y expongo un volumen o uso --volumes-from
para 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 hostmyuser
, 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?
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.
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.
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.
-
Bind mount host volume
Host folder FOOBAR is mounted in container /volume/FOOBAR
-
Modifica el script de inicio de tu contenedor para encontrar el GID del volumen que te interesa
$ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)
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 quenobody
pertenece a un grupo con un id 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 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.
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
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
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.
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; $@"
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
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
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í:
- No puede escribir en el volumen montado si usuario no root porque el archivo compartido será propiedad de otro usuario en el host,
- 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
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"
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
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
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