Cómo manejar los permisos y propietarios de volumenes / bind mount en docker
Manual rápido y completo de Dockerfile:guia_rapida_de_dockerfile
Manual rápido y completo de Docker (linea de comando):guia_rapida_de_la_linea_de_comandos_de_docker
Manual rápido y completo de Docker Swarm: guia_rapida_de_docker_swarm
En el ecosistema docker se usan volúmenes y bind mounts para guardar datos de forma que no sean eliminados cuando los contenedores finallicen. Los volúmenes pueden ser configurados para que usen un determinado propietario y este coincida con el del punto de montaje. Pero no así los antiguos bind mounts, los cuales montan carpeta del host anfitrión y deberían usarse solo cuando son 100% necesarios, en resumen, cuando se debe acceder a determinado directorio del host anfitrión. Con los bind mounts siempre es necesario que el usuario del contenedor sea compatible con la configuración de la carpeta del host que se quiere montar. En la actualidad solo deberían ser usados para visualizar determinados directorios del host anfitrión, para todo lo demás siempre es mejor usar volúmenes.
Cuando no se usan contenedores que utilizan root para ejecutar el proceso y se quiere hacer uso de “bind mounts” o volúmenes, es común que empiecen los problemas de acceso si no se ha configurado correctamente el tema permisos. La linea de comandos de Docker, Docker-compose o cualquier otra aplicación que implemente bind mounts, no permite configurar sus permisos / propietario. Por lo que hay que apoyarse siempre en algo externo para poder configurar los puntos de montaje como se quiera. Se presentan en esta pequeña guía algunas posibilidades para adaptar el usuario del contenedor al del bind mount.
Ejemplo de montaje de volúmenes y bind mounts (directorios del host anfitrión) desde la linea de comandos usando la sintaxis “mount”.
# Se monta el volumen con el mismo UID y GID del usuario que ejecutó el comando. docker run --mount source=volumen1,destination=/opt/volumen1 -it -u $UID:$UID alpine sh # Los bind mounts no permiten configurar el UID/GID y se montará en el contenedor con el UID/GID del anfitrión, exista o no en el contenedor. docker run -it --mount type=bind,src=/root/,dst=/root -w /root/ alpine sh # Siempre se puede arranca el contenedor indicando el usuario compatible con el directorio del host (bind mount) a montar. docker run --user antonio -it --mount type=bind,src=/hme/antonio/,dst=/home/antonio -w /home/antonio/ alpine sh
Cuando se trata de volúmenes, desde la linea de comandos es simple especificar un usuario y grupo como ya vimos (Leer guía rápida de docker.), pero también sería posible hacerlo desde el Dockerfile como veremos a continuación, siendo esta la opción más recomendada al usar docker-compose.
Montar volúmenes (no bind mounts) a partir de un fichero Dockerfile
Al margen de la linea de comandos, este es posiblemente el mejor método de configurar un punto de montaje dentro del contenedor con los permisos deseados cuando se usan aplicaciones estilo docker-compose. El proceso se basa en crear una imagen mediante un fichero Dockerfile donde se define el usuario y los permisos del directorio que monta el volumen. Una vez se monte el volumen, ya sea desde la linea de comandos o docker-compose, el punto de montaje en el contenedor heredará los permisos y propietario definidos previamente en la imagen. No es necesario que el usuario exista en el host, puede perfectamente solo existir en el contenedor.
En el ejemplo se crea una imagen docker llamada “alpine_volumen” que usará un usuario no privilegiado “testuser”.
ARG USR=testuser FROM alpine RUN addgroup -S $USR && adduser -S $USR -G $USR # Se crea el usuario y el grupo para luego aplicarlo al punto de montaje del volumen. RUN mkdir /VOL_testuser && chown $USR:$USR /VOL_testuser # Se crea el directorio donde será montado el volumen y su propietario VOLUME /VOL_testuser # Se hace una referencia al volumen que será montado desde linea de comando o docker-compose. USER $USR # Usuario del proceso.
Montar el volumen desde docker-compose, el volumen se crea automáticamente si no existe. El contenedor creado solo ejecutará una shell.
version: "3" services: web: image: alpine_volumen # Imagen creada a partir del Dockerfile mostrado anteriormente. stdin_open: true # docker run -i tty: true # docker run -t command: /bin/sh volumes: - XXXX:/VOL_testuser # El volumen será montado como el usuario testuser. volumes: XXXX:
También se puede montar el volumen desde la linea de comando (se creará el volumen XXXX si no existe).
docker run --rm -it -v XXXX:/VOL_testuser alpine_volumen
Montar directorios (bind mount) conociendo el usuario del contenedor y aplicándolo al directorio host
Obtener el id del usuario que corre en el contenedor.
# docker docker exec XXXX id uid=100(testuser) gid=101(testuser) groups=101(testuser) # docker-compose docker-compose ps --services web docker-compose exec web id uid=100(testuser) gid=101(testuser) groups=101(testuser)
Una vez se conoce el usuario y grupo, se puede usar en el host el comando chown y chmod. Por supuesto el usuario debe existir en el host.
Montar directorios (bind mount) conociendo el usuario del directorio del host y aplicándolo al contenedor
Se crea un fichero .env con el usuario y grupo del directorio se sabe se va a montar en el contenedor.
UID=100 GID=101
En el fichero de docker-compose se define el usuario y grupo que debe correr el proceso para que sea compatible con el directorio montado
version: "3" services: web: image: alpine_volumen user: "${UID}:${GID}" stdin_open: true # docker run -i tty: true # docker run -t command: /bin/sh volumes: - /home/XXXX:/DIR_testuser
Aplicar la configuración de propietario y permisos en el ENTRYPOINT
Se aplica la configuración dentro del ENTRYPOINT. Normalmente se usará un script, pero para el ejemplo se ha usado un comando y una shell como proceso final.
version: "3" services: web: image: alpine stdin_open: true # docker run -i tty: true # docker run -t container_name: test volumes: - /home/XXXX:/DIR_testuser entrypoint: /bin/sh -c "adduser -D testuser && chown -Rv testuser:testuser /DIR_testuser && sh"