Quelle est la (meilleure) façon de gérer les permissions pour les volumes partagés Docker?

J'ai joué avec Docker pendant un certain temps et de continuer à trouver le même problème en traitant des données persistantes.

je crée mon Dockerfile et expose un volume ou utilise --volumes-from à monte un dossier hôte à l'intérieur de mon conteneur .

quelles permissions dois-je appliquer au volume partagé sur l'hôte?

je peux penser à deux options:

  • jusqu'à présent, j'ai donné à tout le monde un accès en lecture/écriture, donc je peux écrire dans le dossier à partir du conteneur Docker.

  • " mappez les utilisateurs de l'hôte dans le conteneur, de sorte que je puisse attribuer des permissions plus granulaires. Pas sûr que ce soit possible cependant et n'ont pas trouvé beaucoup à ce sujet. Jusqu'à présent, tout ce que je peux faire est d'exécuter le conteneur comme un utilisateur: docker run -i -t -user="myuser" postgres , mais cet utilisateur a un UID différent de mon hôte myuser , donc les permissions ne fonctionnent pas. De plus, je ne suis pas certain que la cartographie des utilisateurs posera des risques de sécurité.

Existe-t-il d'autres solutions?

Comment êtes-vous les gars / filles face à cette question?

277
demandé sur Peter Mortensen 2014-05-08 18:11:10

13 réponses

mise à jour 2016-03-02 : à partir de Docker 1.9.0, Docker a volumes nommés qui remplacer les conteneurs de données . La réponse ci-dessous, ainsi que mon blog lié, a encore la valeur dans le sens de comment penser les données à l'intérieur docker mais envisager d'utiliser des volumes nommés pour mettre en œuvre le modèle décrit ci-dessous plutôt que des conteneurs de données.


je crois que la façon canonique de résoudre ceci est en utilisant conteneurs de données seulement . Avec cette approche, tout l'accès aux données de volume est via des conteneurs qui utilisent -volumes-from le conteneur de données, de sorte que l'hôte uid/gid n'a pas d'importance.

Par exemple, un cas d'utilisation donné dans la documentation est la sauvegarde d'un volume de données. Pour ce faire , un autre conteneur est utilisé pour faire la sauvegarde via tar , et il utilise aussi -volumes-from pour monter volume. Donc je pense que le point clé de grok est: plutôt que de penser à la façon d'accéder aux données sur l'hôte avec les permissions appropriées, pensez à la façon de faire tout ce dont vous avez besoin -- sauvegardes, navigation, etc. -- via un autre récipient. Les conteneurs eux-mêmes doivent utiliser UID/gids cohérente, mais ils n'ont pas besoin de la carte à quoi que ce soit sur l'hôte, ce qui reste portable.

C'est relativement nouveau pour moi, mais si vous avez un cas d'utilisation particulier hésitez pas à commentaire et je vais essayer d'étoffer la réponse.

mise à jour : pour le cas d'utilisation donné dans les commentaires, vous pourriez avoir une image some/graphite pour exécuter graphite, et une image some/graphitedata comme le conteneur de données. Donc, en ignorant les ports et autres, le Dockerfile de l'image some/graphitedata est quelque chose comme:

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

construire et créer le conteneur de données:

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

le some/graphite Dockerfile devrait également obtenir le même uid/ gids, donc il pourrait ressembler à quelque chose comme ceci:

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

et il serait exécuté comme suit:

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

Ok, maintenant que nous donne notre conteneur de graphite et données associées-seulement le conteneur avec l'utilisateur/groupe correct (notez que vous pourriez réutiliser le conteneur some/graphite pour le conteneur de données ainsi, en dépassant l'entrée / cmd lors de l'exécution, mais en les ayant comme images séparées IMO est adhérent).

Maintenant, disons que vous souhaitez modifier quelque chose dans le dossier de données. Ainsi, plutôt que de lier le montage du volume à l'hôte et de l'éditer là-bas, créer un nouveau conteneur pour faire ce travail. Appelons-le some/graphitetools . Permet également de créer l'utilisateur/groupe approprié, tout comme l'image 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"]

vous pouvez le sécher en héritant de some/graphite ou some/graphitedata dans le fichier Dockerfile, ou au lieu de créer une nouvelle image il suffit de réutiliser l'un des points existants (en passant par entrypoint/cmd si nécessaire).

Maintenant, vous courez simplement:

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

puis vi /data/graphite/whatever.txt . Cela fonctionne parfaitement parce que tous les conteneurs ont le même utilisateur de graphite avec uid/gid correspondant.

puisque vous ne montez jamais /data/graphite depuis l'hôte, vous ne vous souciez pas comment l'hôte uid/gid correspond à l'uid/gid défini à l'intérieur des conteneurs graphite et graphitetools . Ces conteneurs peuvent désormais être déployés sur n'importe quel hôte, et ils continueront à fonctionner parfaitement.

la bonne chose à propos de cela est que graphitetools pourrait avoir toutes sortes d'utilités et de scripts utiles, que vous pouvez maintenant également déployer de manière portable.

mise à jour 2 : après avoir écrit cette réponse, j'ai décidé d'écrire un" 151971092020 "plus complet blog post à propos de cette approche. J'espère que cela aide.

mise à jour 3 : j'ai corrigé cette réponse et ajouté plus de détails. Il contenait auparavant des hypothèses erronées au sujet de la propriété et des perms -- la propriété est habituellement attribuée au moment de la création du volume, c.-à-d. dans le conteneur de données, parce que c'est à ce moment que le volume est créé. Voir ce blog . Ce n'est pas une exigence cependant -- vous pouvez juste utiliser le conteneur de données comme une "référence / poignée" et définir la propriété / perms dans un autre conteneur via chown dans un point d'entrée, qui se termine avec gosu pour exécuter la commande que l'utilisateur approprié. Si quelqu'un est intéressé par cette approche, s'il vous plaît commenter et je peux fournir des liens à un échantillon en utilisant cette approche.

149
répondu Raman 2016-03-02 16:48:09

une solution très élégante peut être vue sur le Redis image et en général dans toutes les images officielles.

décrit dans le processus étape par étape:

  • Créer redis utilisateur/groupe, avant toute autre chose

Comme on le voit sur Dockerfile commentaires:

ajouter notre utilisateur et groupe d'abord pour s'assurer que leurs ID sont attribués de façon uniforme, quelles que soient les dépendances ajoutées

  • Installer gosu avec Dockerfile

gosu est une alternative de su / sudo pour le step-down facile de l'utilisateur root. (Redis est toujours exécuté avec redis user)

  • Configurer /data le volume et la définir comme workdir

en configurant le / volume de données avec la commande VOLUME /data , nous avons maintenant un volume séparé qui peut être soit le volume du docker, soit relié à un dir hôte.

le configure comme le workdir ( WORKDIR /data ) le rend le répertoire par défaut d'où les commandes sont exécutées.

  • Ajouter un menu fixe-point d'entrée de fichier et de le définir comme point d'entrée par défaut CMD redis-server

par le script docker-entrypoint, et par défaut la commande à exécuter est redis-server.

docker-entrypoint est un script qui fait une fonction simple: changer la propriété du répertoire courant (/données) et passer de root à redis utilisateur pour exécuter redis-server . (Si la commande exécutée n'est pas redis-server, elle sera exécutée directement.)

cela a l'effet suivant

si le répertoire /data est monté par bind sur l'hôte, le docker-entrypoint préparera les permissions de l'utilisateur avant d'exécuter redis-server sous redis user.

cela vous donne la facilité d'esprit qu'il y a zero-setup afin d'exécuter le conteneur sous n'importe quelle configuration de volume.

bien sûr, si vous avez besoin de partager le volume entre différentes images, vous devez vous assurer qu'ils utilisent le même userid / groupid sinon le dernier conteneur sera détourner les permissions de l'utilisateur de la précédente.

39
répondu Dimitris 2017-08-24 09:26:42

ce n'est sans doute pas la meilleure façon pour la plupart des circonstances, mais il n'a pas été mentionné encore, donc peut-être qu'il aidera quelqu'un.

  1. Lier mont volume hôte

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. modifier le script de démarrage de votre conteneur pour trouver GID du volume vous êtes intéressé par

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

  3. Assurer votre utilisateur appartient à un groupe avec ce GID (vous pourriez avoir à créer un nouveau groupe). Pour cet exemple, je vais prétendre que mon logiciel fonctionne comme l'utilisateur nobody à l'intérieur du conteneur, donc je veux m'assurer que nobody appartient à un groupe avec un id de groupe égal à 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

j'aime cela parce que je peux facilement modifier les permissions de groupe sur mes volumes hôtes et je sais que ces permissions mises à jour appliquer à l'intérieur du conteneur docker. Cela se produit sans aucune permission ou modification de propriété de mes dossiers/fichiers hôtes, ce qui me rend heureux.

Je n'aime pas cela parce qu'il suppose qu'il n'y a aucun danger à s'ajouter à un groupe arbitraire à l'intérieur du conteneur qui se trouve à utiliser un GID que vous voulez. Il ne peut pas être utilisé avec une clause USER dans un fichier Dockerfile (à moins que cet utilisateur ait les privilèges root je suppose). Aussi, il crie travail de piratage; -)

si vous voulez être hardcore vous pouvez évidemment étendre cela de plusieurs façons - par exemple, rechercher tous les groupes sur n'importe quels sous-Fichiers, plusieurs volumes, etc.

28
répondu Hamy 2015-08-14 18:13:49

Ok, c'est maintenant suivi à docker question n ° 7198

pour l'instant, je m'en occupe en utilisant votre deuxième option:

cartographier les utilisateurs de l'hôte dans le conteneur

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

mise à JOUR je ne suis plus enclin à Hamy réponse

15
répondu Leo Gallucci 2017-05-23 11:47:29

essayez d'ajouter une commande à Dockerfile

RUN usermod -u 1000 www-data

les crédits vont à https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

8
répondu FDisk 2015-11-09 18:07:46

tout comme vous, je cherchais un moyen de cartographier les utilisateurs/groupes de l'hôte aux conteneurs docker et c'est le moyen le plus court que j'ai trouvé jusqu'à présent:

  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
        .....

ceci est un extrait de ma Docker-composition.yml.

l'idée est de monter (en mode Lecture seule) des listes d'utilisateurs/groupes de l'hôte vers le conteneur ainsi, après le démarrage du conteneur, il aura le même UID->nom d'utilisateur (ainsi que pour les groupes) appariements avec l'hôte. Maintenant, vous pouvez configurez les paramètres utilisateur/groupe pour votre service à l'intérieur du conteneur comme s'il fonctionnait sur votre système hôte.

lorsque vous décidez de déplacer votre conteneur vers un autre hôte, vous avez juste besoin de changer le nom d'utilisateur dans le fichier de configuration service en ce que vous avez sur cet hôte.

6
répondu alex_edev 2017-08-11 17:11:05

Voici une approche qui utilise toujours un conteneur de données seulement mais qui n'exige pas qu'il soit synchronisé avec le conteneur d'application (en termes d'avoir le même uid/gid).

probablement, vous voulez exécuter une application dans le conteneur comme un utilisateur $non-root sans shell de login.

dans le fichier Dockerfile:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

puis, en entrypoint.sh:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"
5
répondu Ethan 2015-06-11 19:56:13

pour sécuriser et changer la racine pour Docker container un hôte docker essayer d'utiliser --uidmap et --private-uids options

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

vous pouvez également supprimer plusieurs fonctionnalités ( --cap-drop ) dans Docker container pour la sécurité

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

mise à JOUR soutien doit venir dans docker > 1.7.0

mise à JOUR Version 1.10.0 (2016-02-04) ajouter --userns-remap drapeau https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2

4
répondu umount 2016-03-12 03:22:16

Image De Base

utilisez cette image: https://hub.docker.com/r/reduardo7/docker-host-user

ou

Important: cela détruit la portabilité des conteneurs entre les hôtes .

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) Construire avec docker

4) courez!

sh run.sh
3
répondu Eduardo Cuomo 2017-07-25 15:20:11

pour partager le dossier entre l'hôte docker et le conteneur docker, essayez sous la commande

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

le drapeau-v monte le répertoire courant dans le conteneur. Lorsque le répertoire hôte d'un volume monté sur bind n'existe pas, Docker créera automatiquement ce répertoire sur l'hôte pour vous,

cependant, il y a 2 problèmes que nous avons ici:

  1. vous ne pouvez pas écrire au volume monté si vous étiez utilisateur non root parce que le fichier partagé sera la propriété d'un autre utilisateur dans l'hôte,
  2. vous ne devriez pas exécuter le processus à l'intérieur de vos conteneurs en tant que root, mais même si vous exécutez comme un utilisateur codé dur, il ne correspond toujours pas à l'utilisateur sur votre ordinateur portable/Jenkins,

Solution:

conteneur: créer un utilisateur dire 'testuser', par défaut l'ID de l'Utilisateur sera à partir de 1000,

Host: créer un groupe Dire 'testgroup' avec le groupe id 1000, et parcourir le répertoire vers le nouveau groupe(testgroup

1
répondu Praveen Muthusamy 2018-05-16 17:29:54

mon approche est de détecter uid/GID courant, puis de créer un tel utilisateur / groupe à l'intérieur du conteneur et d'exécuter le script sous lui, alors tous les fichiers qu'il va créer correspondra à l'utilisateur qui exécute le 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
répondu muni764 2018-06-08 15:00:26

dans mon cas particulier, j'essayais de construire mon paquet node avec l'image node docker pour ne pas avoir à installer npm sur le serveur de déploiement. Cela a bien fonctionné jusqu'à ce que, à l'extérieur du conteneur et sur la machine hôte, j'ai essayé de déplacer un fichier dans le répertoire node_modules que l'image du docker node avait créé, auquel on m'a refusé les permissions parce qu'il appartenait à root. J'ai réalisé que je pouvais contourner ce problème en copiant le répertoire le contenant sur l'ordinateur hôte machine. Via docker docs ...

les fichiers copiés sur la machine locale sont créés avec L'UID: GID du utilisateur qui a invoqué la commande docker cp.

c'est le code bash que j'ai utilisé pour changer la propriété du répertoire créé par et dans le conteneur 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 nécessaire, vous pouvez supprimer le répertoire avec un deuxième conteneur docker.

docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
0
répondu johnklawlor 2018-02-21 19:58:23

si vous utilisez Docker composer, démarrer le conteneur en mode prévileged:

wordpress:
    image: wordpress:4.5.3
    restart: always
    ports:
      - 8084:80
    privileged: true
-2
répondu Renato Alencar 2016-07-07 20:01:53