Compose: Docker Compose monta volúmenes con nombre como 'root' exclusivamente

Creado en 5 abr. 2016  ·  33Comentarios  ·  Fuente: docker/compose

Se trata de volúmenes con nombre (por lo que no hay "contenedor de volumen de datos", no "volúmenes de") y docker-compose.yml.

El objetivo aquí es usar docker-compose para administrar dos servicios 'appserver' y 'server-postgresql' en dos contenedores separados y usar la función "volúmenes:" docker-compose.yml para hacer que los datos del servicio 'server-postgresql' sean persistentes .

El Dockerfile para 'server-postgresql' se ve así:

FROM        ubuntu:14.04
MAINTAINER xxx

RUN apt-get update && apt-get install -y [pgsql-needed things here]
USER        postgres
RUN         /etc/init.d/postgresql start && \
            psql --command "CREATE USER myUser PASSWORD 'myPassword';" && \
            createdb -O diya diya
RUN         echo "host all  all    0.0.0.0/0  md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN         echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
CMD         ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]

Y el docker-compose.yml se ve así:

version: '2'
services:
    appserver:
        build: appserver
        depends_on:
            - server-postgresql
        links:
            - "server-postgresql:serverPostgreSQL"
        ports:
            - "1234"
            - "1235"
        restart: on-failure:10
    server-postgresql:
        build: serverPostgreSQL
        ports:
            - "5432"
        volumes:
            - db-data:/volume_data
        restart: on-failure:10
volumes:
    db-data:
        driver: local

Luego comienzo todo con docker-compose up -d , entro en mi contenedor server-postgresql con docker-compose exec server-postgresql bash , un rápido ls revela /volume_data , luego cd en él e intente touch testFile y obtuvo "permiso denegado. Lo cual es normal porque un rápido ls -l muestra que volume_data es propiedad de root:root .

Ahora, lo que creo que está sucediendo es que, dado que tengo USER postgres en el Dockerfile, cuando ejecuto docker-compose exec estoy conectado como usuario 'postgres' (y el demonio postgresql se ejecuta como usuario 'postgres' también, por lo que no podrá escribir en /volume_data ).
Esto se confirma porque cuando corro en su lugar: docker-compose exec --user root server-postgresql bash y reintento a cd /volume_data y touch testFile , que hace el trabajo (no es un error de permiso entre el anfitrión y el contenedor, como A veces es el caso cuando el contenedor monta una carpeta de host, este es un error de permiso típico de Unix porque /volume_data está montado como 'root: root' mientras el usuario 'postgres' está intentando escribir).

Por lo tanto, debería haber una forma en docker-compose.yml para montar volúmenes con nombre como usuario específico, algo como:

version: '2'
services:
    appserver:
        [...]
    server-postgresql:
        [...]
        volumes:
            - db-data:/volume_data:myUser:myGroup
        [...]
volumes:
    db-data:
        driver: local

La única solución _dirty_ que se me ocurre es eliminar la directiva USER posgres del Dockerfile y cambiar ENTRYPOINT para que apunte a un "init_script.sh" personalizado (que se ejecutaría como 'root' ya que eliminado USER postgres ), este script cambiaría los permisos de /volume_data para que 'postgres' pueda escribir en él, luego su postgres y ejecutar el demonio postgresql (en primer plano). Pero esto en realidad es muy sucio, porque vincula Dockerfile y docker-compose.yml de una manera no estándar (el tiempo de ejecución ENTRYPOINT se basaría en el hecho de que un volumen montado está disponible mediante docker-compose.yml).

arevolumes statu0-triage

Comentario más útil

En realidad, vengo aquí con noticias, parece que lo que estoy tratando de lograr es factible, pero no sé si esto es una característica o un error. He aquí por qué cambié:

En mi Dockerfile , _antes de cambiar al usuario 'postgres'_ agregué estos:

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

Lo que hace es crear un directorio / volume_data y cambiar sus permisos para que el usuario 'postgres' pueda escribir en él.
Esta es la parte de Dockerfile .

Ahora no he cambiado nada en docker-compose.yml : entonces docker-compose aún crea el Volumen con nombre directory_name_db-data y lo monta en /volume_data y los permisos persisten! .
Lo que significa que ahora, tengo mi Volumen con nombre montado en el directorio _pre-existente_ /volume_data con los permisos preservados, para que 'postgres' pueda escribir en él.

Entonces, ¿este es el comportamiento previsto o una violación de la seguridad? (¡Sin embargo, me sirve en este caso!)

Todos 33 comentarios

No creo que esto sea compatible con el motor de la ventana acoplable, por lo que no hay forma de que podamos admitirlo en Compose hasta que se agregue a la API. Sin embargo, no creo que sea necesario agregar esta función. Siempre puede transferir los archivos al usuario correcto:

version: '2'
services:
  web:
    image: alpine:3.3
    volumes: ['random:/path']

volumes:
  random:
$ docker-compose run web sh
/ # touch /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 root     root             0 Apr  5 16:11 foo
/ # chown postgres:postgres /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo
/ # 
$ docker-compose run web sh
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo

El problema al que se enfrenta es acerca de la inicialización de un volumen con nombre. Es cierto que Compose no maneja esto (porque está algo fuera de alcance), pero puede usar fácilmente la ventana acoplable cli para inicializar un volumen con nombre antes de ejecutar docker-compose up .

De hecho, no estaba seguro de si se trataba de un problema de Docker o de Redacción, lo siento si lo archivé mal.
¿Es este un plan en la API de Docker? ¿Debería presentar un problema allí?

Entiendo la posibilidad de iniciar sesión manualmente en el contenedor y chown -ingresar el volumen al usuario 'postgres'. Pero la cuestión es que, en mi caso, estoy usando Compose para poder instanciar inmediatamente nuevos contenedores para nuevos clientes ( docker-compose -p client_name up ) y Compose creará una red personalizada client_name_default , creará los contenedores con los nombres client_name _appserver_1 y client_name _server-postgresql_1 y _más importante_, creará el volumen client_name_db-data . Todo lo cual no tengo que hacer manualmente, por lo que puede ser ejecutado por el script que maneja el registro del cliente.

Con la solución que describió (_manually_ "registrando" en el contenedor con sh y chown -ing la etiqueta de volumen), no puedo tener un procedimiento simple para agregar nuevos clientes, y esto debe ser atendido a mano.

Por eso creo que debería implementarse esta función. En la API de Docker, podemos especificar los permisos ro o rw (para solo lectura o lectura-escritura) al montar un volumen, creo que deberíamos poder especificar user:group .

¿Qué piensas? ¿Tiene sentido mi solicitud?

En realidad, vengo aquí con noticias, parece que lo que estoy tratando de lograr es factible, pero no sé si esto es una característica o un error. He aquí por qué cambié:

En mi Dockerfile , _antes de cambiar al usuario 'postgres'_ agregué estos:

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

Lo que hace es crear un directorio / volume_data y cambiar sus permisos para que el usuario 'postgres' pueda escribir en él.
Esta es la parte de Dockerfile .

Ahora no he cambiado nada en docker-compose.yml : entonces docker-compose aún crea el Volumen con nombre directory_name_db-data y lo monta en /volume_data y los permisos persisten! .
Lo que significa que ahora, tengo mi Volumen con nombre montado en el directorio _pre-existente_ /volume_data con los permisos preservados, para que 'postgres' pueda escribir en él.

Entonces, ¿este es el comportamiento previsto o una violación de la seguridad? (¡Sin embargo, me sirve en este caso!)

Creo que esto se agregó en Docker 1.10.x para que los volúmenes con nombre se inicialicen desde el primer contenedor que los usó. Creo que es el comportamiento esperado.

También estoy haciendo volúmenes con nombre con la propiedad establecida en el Dockerfile y en la composición estoy agregando user: postgres para que incluso el PID 1 sea propiedad de un usuario no root.

Docker-compose para volumes tiene driver_opts para proporcionar opciones.
Será muy bueno ver opciones como chmod , chown incluso para el controlador local .
Y especialmente, quiero que también se aplique a los directorios de host creados localmente si no está presente al inicio.

Relacionado (hasta cierto punto) https://github.com/moby/moby/pull/28499

¿Alguien ya abrió un problema en el proyecto Moby?

La respuesta de @dnephin no funciona. El problema es porque estamos ejecutando el contenedor como un usuario estándar, los comandos chown o chmod fallan, el volumen es propiedad de root, un usuario estándar no puede cambiar los permisos.

@jcberthon el método sugerido es ejecutar el contenedor con root como usuario inicial y luego usar el comando USER DESPUÉS del chown / chmod para que básicamente "pierda privilegios".

Eso está bien si tiene el control de la imagen de Docker, pero si está utilizando imágenes existentes, esa no es realmente una opción.

@ dragon788 y @micheljung , resolví mi problema.

En realidad, el problema real fue que en mi Dockerfile, declaré VOLUME y luego modifiqué la propiedad y los permisos de los archivos en ese volumen. Esos cambios se pierden. Simplemente moviendo la declaración VOLUME al final del Dockerfile (o eliminándolo, ya que es opcional), mi problema está resuelto. Los permisos son correctos.

Entonces el error fue:

FROM blabla
RUN do stuff
VOLUME /vol
RUN useradd foo && chown -R foo /vol
USER foo
CMD ["blabla.sh"]

Los chown en el Dockerfile de ejemplo anterior se pierden durante la compilación porque declaramos VOLUME antes. Al ejecutar el contenedor, dockerd copia dentro del volumen nombrado el contenido de /vol antes de la declaración VOLUME (es decir, con los permisos de root). Por lo tanto, los procesos en ejecución no pueden modificar o cambiar los permisos, por lo que incluso forzar chown en el script blabla.sh no puede funcionar.

Cambiando el archivo a:

FROM blabla
RUN do stuff
RUN useradd foo && chown -R foo /vol
USER foo
VOLUME /vol
CMD ["blabla.sh"]

el problema esta resuelto.

@jcberthon, ¿ podría compartir cómo vincula su volumen / vol con el sistema host en su docker-compose.yml?

Estoy trabajando con Docker en Fedora (por lo tanto, SELinux habilitado) y ninguno de los métodos mencionados anteriormente funcionó para mí. Idealmente, quiero ejecutar aplicaciones en mis Contenedores bajo el contexto de un usuario (sin root) pero este problema de Volumen es un obstáculo para eso.

La única solución que me funciona es eliminar el usuario de mi aplicación y ejecutar / poseer todo como usuario root.

Hola @renanwilliam y @ egee-irl

He estado usando la solución mencionada anteriormente en varios sistemas operativos incluidos. Fedora 26 y CentOS 7 ambos con SELinux forzado, Ubuntu 16.04, 17.10 y Raspbian 9 los tres con AppArmor activado (con una mezcla de plataformas amd64 y armhf).

Entonces, como dije, ahora moví la declaración VOLUME ... al final de mi Dockerfile, pero puede eliminarlo por completo, no es necesario. Lo que también suelo hacer es corregir el ID de usuario al crear el usuario en el Dockerfile (por ejemplo, useradd -u 8002 -o foo ). Luego, simplemente puedo reutilizar ese UID en el host para otorgar los permisos adecuados a la carpeta.

El siguiente paso es crear el "colgante" del directorio /vol en el host, digamos que es /opt/mycontainer1/vol , entonces eso es

$ sudo mkdir -p /opt/mycontainer1/vol
$ sudo chown -R 8002 /opt/mycontainer1/vol
$ sudo chmod 0750 /opt/mycontainer1/vol

Luego, cuando ejecute el contenedor como usuario foo, podrá escribir en el directorio /opt/mycontainer1/vol . Algo como:

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol mycontainer1-img

En hosts basados ​​en SELinux, es posible que desee agregar la opción :z :Z para el volumen para que Docker etiquete la carpeta de manera adecuada. La diferencia entre z y Z es que la minúscula z marcará el volumen para que potencialmente todos los contenedores de este host puedan ser permitidos por SELinux para acceder a ese directorio (pero obviamente solo si lo vincula a montarlo en otro contenedor), mientras que las mayúsculas Z lo etiquetarán para que solo ese contenedor específico pueda acceder al directorio. Entonces, en Fedora con SELinux, es posible que desee probar:

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol:Z mycontainer1-img

Actualización : puede consultar mi repositorio aquí https://github.com/jcberthon/unifi-docker Estoy usando este método y explicando cómo configurar el host y ejecutar su contenedor. Espero que esto pueda ayudar a resolver aún más sus problemas.

Por cierto, me disculpo @renanwilliam por la larga demora en responderte. No tengo mucho tiempo libre este fin de año ...

Entonces, cuento largo para los impacientes:

RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data

Crear el directorio del volumen de antemano y chown resuelve, porque el volumen conservará los permisos del directorio preexistente.

Este es un trabajo deficiente, ya que no es obvio (hacer un chown en un Dockerfile y luego heredar esa propiedad durante el montaje). Exponer el control del propietario y del grupo en la CLI de docker-compose y docker sería el camino de menor sorpresa para los comandos de estilo Unix.

@villasv

Un pequeño consejo: fusiona los 2 RUN ... en uno, esto evita crear capas adicionales y es una buena práctica. Entonces tus 2 líneas deberían ser

RUN mkdir /volume_data && chown postgres:postgres /volume_data

Pero tenga cuidado (como mencioné en un comentario anterior) que debe hacer el comando RUN ... anterior antes de declarar el volumen (o en realidad no declarar el volumen) usando VOLUME ... . Si hace (como hice yo) el cambio de propiedad después de declarar el volumen, esos cambios no se registran ni se pierden.

@colbygk sería realmente útil, pero no es así como funciona Linux. Docker usa el espacio de nombres de montaje de Linux para crear diferentes jerarquías de un solo directorio ( / y subcarpetas), pero AFAIK no hay asignaciones de usuarios / grupos o permisos que anulen actualmente en el espacio de nombres de montaje de Linux. Esos "montajes" dentro de un contenedor (y que incluyen volúmenes de montaje de enlace) están en un sistema de archivos en el host (a menos que use otros complementos de volumen de Docker, por supuesto), y este sistema de archivos respeta la capa VFS de Linux que hace todo la comprobación de permisos de archivo. Incluso podría haber en el host algún MAC (por ejemplo, SELinux, AppArmor, etc.) que podría interferir con un contenedor que accede a los archivos dentro del contenedor. En realidad, si hace chroot , puede encontrar problemas similares, ya que puede vincular carpetas de montaje dentro del chroot, también tiene el problema de que los procesos que se ejecutan dentro del entorno chroot pueden tener el UID / GID efectivo incorrecto para acceder a los archivos en el bind-mount.

Se aplican reglas simples de Linux (y en realidad también de Unix) para el contenedor. El truco consiste en ver y comprender las posibilidades y los límites de los espacios de nombres de Linux en la actualidad, y luego se está volviendo más claro cómo resolver problemas como este. Lo resolví completamente usando comandos clásicos de Unix.

@jcberthon Gracias por su

Yo diría que este debería ser un problema que se inserta en la capa de complemento como sugiere y, por lo tanto, podría convertirse en parte del complemento de controlador de volumen genérico que se envía con Docker. Me parece muy poco nublado / contenedor forzar a un recurso externo (externo a un contenedor en particular) a adherirse a relaciones esencialmente estáticas que se definen en la imagen de la que se deriva un contenedor.

Hay otros ejemplos de este tipo exacto de mapeo uid / gid que ocurre en otras áreas similares de "unix":

Por favor, corríjame si estoy equivocado; https://github.com/zfsonlinux/zfs/issues/4177 parece haber sido originado por el "líder de LXC / LXD" como un problema relacionado con ZFS en Linux que no proporciona correctamente información UID / GID para permitir el mapeo de esos en un contenedor casi de la manera _exacta_ que estamos discutiendo aquí. Mirando de cerca https://github.com/zfsonlinux/zfs/issues/4177 , parece que el tipo de volumen zfs en realidad ya podría admitir este mapeo uid / gid entre espacios de nombres, pero no expone los controles para hacerlo.

la mayoría de las personas que usan Docker es para dev / CI, usamos imágenes "genéricas" como php / nginx (corredor) o gradle / python (constructor), por lo que una buena solución:

  1. no es necesario crear / editar Dockerfile para anular la imagen
  2. use una sintaxis simple en el archivo yml de docker-compose

Dado que podemos decidir fácilmente el permiso de escritura de un volumen (X: X: OPTION_READ_WRITE), ¿qué hay de agregar de la misma manera, el propietario?

SOURCE:TARGET:RW:OWNER

Estoy teniendo el mismo problema. Probablemente haya una mejor práctica, y mi método es _no_:

version: '3.5'
services:
    something:
        image: someimage
        user: '1000'
        expose:
            - 8080
        volumes:
            - dev:/app

volumes:
    dev:

Esto causa EACCES: permission denied, access '/app' .

¿Cómo deberíamos hacer esto? ¿Es decir, definir un nuevo volumen y poder acceder a él con un usuario que no sea root?

Hola @Redsandro

Sería mejor que establezca en la imagen de Docker someimage el UID en 1000 por /app . O si no puede controlar eso, debe usar para user: ... en su archivo de redacción el UID o GID que pretendía el autor de la imagen.

Por supuesto, si el autor de la imagen usó el UID 0 y no desea ejecutar el servicio como root (y puede ejecutarse como un usuario sin privilegios), entonces plantee un problema al autor de la imagen de Docker.

dado que esto no es algo que Docker debe administrar, puede crear otro contenedor para aprovisionar sus volúmenes justo antes de que comiencen los contenedores relacionados, por ejemplo, usando https://hub.docker.com/r/hasnat/volumes-provisioner

version: '2'
services:
  volumes-provisioner:
    image: hasnat/volumes-provisioner
    environment:
      PROVISION_DIRECTORIES: "65534:65534:0755:/var/data/prometheus/data"
    volumes:
      - "/var/data:/var/data"

  prometheus:
    image: prom/prometheus:v2.3.2
    ports:
      - "9090:9090"
    depends_on:
      - volumes-provisioner
    volumes:
      - "/var/data/prometheus/data:/prometheus/data"

No entiendo por qué Docker no está arreglando este, estoy de acuerdo en que podemos piratearlo para propósitos de desarrollo, pero en producción, ¡de ninguna manera!

En mi humilde opinión, privilegios) (ver también aquí ) y probablemente resolverá esto. Alguien también trabaja en una solución de redacción y la API de Podman es intencionalmente compatible con Docker en la mayoría de las partes.

[podman] podría ayudar a algunas personas aquí, sin embargo, porque es compatible con Docker en gran parte.

Totalmente de acuerdo, desafortunadamente podman no funciona en Mac

Totalmente de acuerdo, desafortunadamente podman no funciona en Mac


Bueno, en mi humilde opinión ni Docker ni podman nunca funcionarán de forma nativa. Pero las instalaciones de Docker en OS / X ocultan muy bien las cosas de la máquina virtual.
Pero estoy de acuerdo en que configurar las máquinas virtuales manualmente para tener un sistema de desarrollo adecuado puede ser doloroso.
Sin embargo, aquí se está saliendo un poco del tema.

Ya no soy un usuario de OS / X, pero acabo de ver que hay un dmg de podman _experimental_ .

Supongo que algún ecosistema similar podría desarrollarse en un futuro más cercano porque ya es posible acceder a podman programmatic o incluso a podman-compose .

Esto se convierte especialmente en un problema cuando los usuarios sin derechos de sudo en un clúster compartido crean accidentalmente algunas carpetas propiedad de root a través de docker-compose y luego ni siquiera pueden eliminar esas carpetas ellos mismos.

También me encontré con este problema. Estamos intentando usar la ventana acoplable en la ventana acoplable como se publicación de jpetazzo .

Queremos iniciar este contenedor desde el archivo docker-compose y poder montar el socket de la ventana acoplable desde la máquina host en la máquina del contenedor en el grupo de la ventana acoplable. Esto es para que podamos usar otro usuario que no sea root para ejecutar la ventana acoplable desde el interior del contenedor.

Actualmente, dado que no puedo especificar la propiedad y el permiso de los archivos de montaje de enlace, esto no se puede lograr.

Pon mis dos centavos aquí:

Para una solución "nativa" de docker-compose, hice esto, ya que la mayoría de las personas ya tendrán alpine en su biblioteca de imágenes:

volumes:
  media:
services:
  mediainit:
    image: alpine
    entrypoint: /bin/sh -c "chown -v nobody:nogroup /mnt/media && chmod -v 777 /mnt/media"
    container_name: mediainit
    restart: "no"
    volumes: 
      - "media:/mnt/media"

No es el método más seguro, por supuesto, pero solo los contenedores a los que se les otorgan privilegios al contenedor lo verán, por lo que no es un gran problema, pero puede convertirlo fácilmente en usuario: usuario o hacer algo de setacl si su kernel lo admite. eso.

EDITAR: Parece que primero debes usar la carpeta antes de chmod o no se 'pega', al menos en mis pruebas.

la propiedad del volumen no está bajo el control de docker-compose, por lo que esta discusión debe tener lugar en el repositorio del proyecto moby.

Algunas notas para las personas que buscan aquí una solución alternativa:
El volumen de Docker, cuando un contenedor lo usa por primera vez, obtiene su contenido inicial y el permiso heredado del contenedor. Lo que significa que puedes configurar tu imagen así:

#Dockerfile
FROM alpine
RUN addgroup -S nicolas && adduser -S nicolas -G nicolas
RUN mkdir /foo && chown nicolas:nicolas /foo  
# empty, but owned by `nicolas`. Could also have some initial content
VOLUME /foo  
USER nicolas

Una imagen de este tipo, cuando se ejecuta sin un volumen explícito (que se creará a propósito con un ID aleatorio) o con un volumen con nombre que aún no existe, "propagará" los permisos al volumen:

➜  docker run --rm -it -v some_new_volume:/foo myimage
/ $ ls -al /foo
total 8
drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
drwxr-xr-x    1 root     root          4096 Oct 18 08:30 ..

Lo mismo se aplica cuando se utilizan volúmenes declarados en un archivo de redacción:

#docker-compose.yml
version: "3"
services:
  web:
    image: myimage
    command: ls -al /foo
    volumes:
      - db-data:/foo
volumes:
    db-data:

➜  docker-compose up
Creating volume "toto_db-data" with default driver
Creating toto_web_1 ... done
Attaching to toto_web_1
web_1  | total 8
web_1  | drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
web_1  | drwxr-xr-x    1 root     root          4096 Oct 18 08:37 ..
toto_web_1 exited with code 0

Esto no funcionará si vuelve a adjuntar un volumen que ya ha sido utilizado por otro contenedor. Cambiar la propiedad del volumen, o controlarlo en el momento de la creación, debe ser implementado por el motor o por controladores de volumen con algunas opciones específicas. De lo contrario, tendrá que confiar en algunos trucos de chown como se sugiere ^.

Espero que esto ayude.
Estoy cerrando este problema ya que componer no tiene control sobre la creación de volumen, pero la API del motor expuesta.

¿Fue útil esta página
0 / 5 - 0 calificaciones