web-dev-qa-db-fra.com

Quel est le meilleur moyen de gérer les autorisations pour les volumes partagés Docker?

Je joue avec Docker depuis un moment et je continue à trouver le même problème lorsque je traite des données persistantes.

Je crée ma Dockerfile et expose un volume ou utilise --volumes-from pour monte un dossier Host dans mon conteneur .

Quelles autorisations 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 afin que je puisse écrire dans le dossier à partir du conteneur Docker.

  • Mappez les utilisateurs de l'hôte dans le conteneur afin que je puisse attribuer des autorisations plus granulaires. Je ne suis pas sûr que cela soit possible et je n’ai pas trouvé grand chose à ce sujet. Jusqu'ici, tout ce que je peux faire est d'exécuter le conteneur en tant qu'utilisateur: docker run -i -t -user="myuser" postgres, mais cet utilisateur a un UID différent de celui de mon hôte myuser; les autorisations ne fonctionnent donc pas. De plus, je ne sais pas si le mappage des utilisateurs posera des risques de sécurité.

Y a-t-il d'autres alternatives?

Comment abordez-vous ce problème?

295
Xabs

UPDATE 2016-03-02: à compter de Docker 1.9.0, Docker a des volumes nommés qui remplacent les conteneurs de données uniquement . La réponse ci-dessous, ainsi que mon article de blog lié, a toujours une valeur au sens de comment penser aux données à l'intérieur du menu fixe mais envisagez d'utiliser des volumes nommés pour implémenter le modèle décrit ci-dessous plutôt que des conteneurs de données.


Je pense que la solution à ce problème consiste à utiliser les conteneurs de données uniquement . Avec cette approche, tous les accès aux données de volume se font via des conteneurs qui utilisent -volumes-from le conteneur de données. Par conséquent, l'hôte uid/gid n'a pas d'importance.

Par exemple, un cas d'utilisation indiqué dans la documentation est la sauvegarde d'un volume de données. Pour ce faire, un autre conteneur est utilisé pour effectuer la sauvegarde via tar. Il utilise également -volumes-from afin de monter le volume. Donc, je pense que le point clé de Grok est le suivant: plutôt que de penser à la façon d'obtenir un accès aux données sur l'hôte avec les autorisations appropriées, pensez à faire tout ce dont vous avez besoin - sauvegardes, navigation, etc. - via un autre conteneur . Les conteneurs eux-mêmes doivent utiliser des uid/gids cohérents, mais ils n'ont pas besoin de mapper sur quoi que ce soit sur l'hôte, restant ainsi portables.

Ceci est relativement nouveau pour moi aussi, mais si vous avez un cas d'utilisation particulier, n'hésitez pas à commenter et je vais essayer de développer la réponse.

UPDATE: pour le cas d'utilisation donné dans les commentaires, vous pouvez avoir une image some/graphite pour exécuter le graphite et une image some/graphitedata en tant que conteneur de données. Donc, en ignorant les ports et autres, la Dockerfile de l'image some/graphitedata ressemble à 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"]

Construisez et créez le conteneur de données:

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

Le some/graphite Dockerfile devrait également avoir le même uid/gids, il pourrait donc ressembler à 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 ce serait exécuté comme suit:

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

Ok, maintenant cela nous donne notre conteneur graphite et le conteneur associé contenant uniquement les données avec le bon utilisateur/groupe (notez que vous pouvez également réutiliser le conteneur some/graphite pour le conteneur de données, en remplaçant ainsi le paramètre entrypoing/cmd lors de son exécution, mais en images séparées, IMO est plus claire).

Supposons maintenant que vous souhaitiez modifier quelque chose dans le dossier de données. Par conséquent, plutôt que de lier le montage du volume à l'hôte et de l'éditer ici, créez un nouveau conteneur pour effectuer ce travail. Permet de l'appeler 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 créer ceci DRY en héritant de some/graphite ou some/graphitedata dans le fichier de docker, ou au lieu de créer une nouvelle image, réutilisez simplement l’une des images existantes (en modifiant le point d’entrée/cmd si nécessaire).

Maintenant, vous lancez simplement:

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

et ensuite vi /data/graphite/whatever.txt. Cela fonctionne parfaitement parce que tous les conteneurs ont le même utilisateur graphite avec les identifiants uid/gid correspondants.

Etant donné que vous ne montez jamais /data/graphite à partir de l'hôte, la façon dont l'hôte uid/gid de l'hôte est mappé sur l'hôte/gid défini dans les conteneurs graphite et graphitetools. Ces conteneurs peuvent maintenant être déployés sur n’importe quel hôte et ils continueront à fonctionner parfaitement.

La chose intéressante à propos de cela est que graphitetools pourrait avoir toutes sortes d'utilitaires et de scripts utiles, que vous pouvez maintenant déployer également de manière portable.

UPDATE 2: Après avoir écrit cette réponse, j'ai décidé d'écrire un article de blog plus complet sur cette approche. J'espère que ça aide.

UPDATE 3: J'ai corrigé cette réponse et ajouté plus de détails. Il contenait auparavant des hypothèses incorrectes sur la propriété et les perms - la propriété est généralement attribuée au moment de la création du volume, c'est-à-dire dans le conteneur de données, car c'est à ce moment que le volume est créé. Voir ce blog . Ce n’est pas une exigence, vous pouvez simplement utiliser le conteneur de données comme "référence/descripteur" et définir les droits de propriété/autorisations dans un autre conteneur via chown dans un point d’entrée, qui se termine par gosu pour exécuter la commande en tant qu’utilisateur approprié. Si quelqu'un est intéressé par cette approche, veuillez commenter et je peux fournir des liens vers un exemple utilisant cette approche.

157
Raman

Une solution très élégante peut être vue sur l'image officielle redis et en général dans toutes les images officielles.

Description pas à pas:

  • Créer un utilisateur/groupe redis avant tout

Comme on le voit sur les commentaires de Dockerfile:

ajoutez d'abord notre utilisateur et notre groupe pour vous assurer que leurs identifiants sont attribués de manière cohérente, quelles que soient les dépendances ajoutées

  • Installer gosu avec Dockerfile

gosu est une alternative de suSudo pour faciliter la descente de l'utilisateur root. (Redis est toujours exécuté avec redis utilisateur)

  • Configurez /data volume et définissez-le en tant que workdir

En configurant le volume/data avec la commande VOLUME /data, nous avons maintenant un volume séparé qui peut être soit un volume fixe, soit un montage lié à un répertoire hôte.

La configuration en tant que rép_travail (WORKDIR /data) en fait le répertoire par défaut où les commandes sont exécutées.

  • Ajouter un fichier docker-entrypoint et le définir comme ENTRYPOINT avec le serveur redis CMD par défaut

Cela signifie que toutes les exécutions de conteneurs s'exécutent via le script docker-entrypoint et que, par défaut, la commande à exécuter est redis-server.

docker-entrypoint est un script qui exécute une fonction simple: changez la propriété du répertoire en cours (/ data) et passez 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.)

_/Ceci a l'effet suivant

Si le répertoire/data est monté en liaison sur l'hôte, le docker-entrypoint prépare les autorisations utilisateur avant d'exécuter redis-server sous redis user.

Cela vous donne la certitude qu'il n'y a aucune configuration pour exécuter le conteneur sous n'importe quelle configuration de volume.

Bien sûr, si vous devez partager le volume entre différentes images, vous devez vous assurer qu'elles utilisent le même ID utilisateur/ID de groupe, sinon le dernier conteneur détournera les autorisations utilisateur du précédent.

42
Dimitris

Ok, ceci est maintenant en cours de lecture { suivi au numéro 7198 du docker } _

Pour le moment, je traite ceci en utilisant votre deuxième option:

Mapper 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

UPDATE Je suis actuellement plus enclin à Hamyréponse

15
Leo Gallucci

Essayez d'ajouter une commande à Dockerfile

RUN usermod -u 1000 www-data

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

10
FDisk

Comme vous, je cherchais un moyen de mapper des utilisateurs/groupes d’hôte à des conteneurs de menu fixe 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 mon fichier docker-compose.yml.

L'idée est de monter (en mode lecture seule) des listes d'utilisateurs/groupes de l'hôte au conteneur afin qu'après le démarrage du conteneur, les mêmes correspondances uid-> nom d'utilisateur (ainsi que pour les groupes) avec l'hôte. Vous pouvez maintenant configurer les paramètres utilisateur/groupe de votre service dans le 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, il vous suffit de changer le nom d'utilisateur dans le fichier de configuration du service pour qu'il corresponde à ce que vous avez sur cet hôte.

7
alex_edev

Voici une approche qui utilise toujours un conteneur de données uniquement mais ne nécessite pas sa synchronisation avec le conteneur d'application (en termes de même identifiant/identifiant de périphérique).

Vraisemblablement, vous voulez exécuter une application dans le conteneur en tant que $ USER non root sans shell de connexion. 

Dans le fichier Docker:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

Ensuite, dans entrypoint.sh:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"
5
Ethan

Pour sécuriser et modifier le contenu du conteneur racine pour docker, un hôte docker doit utiliser les options --uidmap et --private-uids.

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

Vous pouvez également supprimer plusieurs fonctionnalités (--cap-drop) dans le conteneur de menu fixe pour des raisons de sécurité.

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

UPDATE support devrait entrer dans docker > 1.7.0

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

4
umount

Image de base

Utilisez cette image: https://hub.docker.com/r/reduardo7/docker-Host-user

ou

Important: ceci détruit la portabilité du conteneur sur 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
Eduardo Cuomo

Mon approche consiste à détecter les UID/GID actuels, puis à créer un tel utilisateur/groupe dans le conteneur et à exécuter le script sous lui, de sorte que tous les fichiers qu'il créera correspondront à l'utilisateur qui a exécuté 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"
2
muni764

Dans mon cas spécifique, j’essayais de construire mon package de noeud avec l’image de docker de noeud afin de ne pas avoir à installer npm sur le serveur de déploiement. Cela a bien fonctionné jusqu’à ce que, en dehors du conteneur et sur la machine hôte, j’ai essayé de déplacer un fichier dans le répertoire node_modules créé par l’image de docker de noeuds, auquel je n’ai pas été autorisé à accéder parce qu’il appartenait à root. J'ai réalisé que je pouvais contourner ce problème en copiant le répertoire du conteneur sur la machine hôte. Via docker docs ...

Les fichiers copiés sur la machine locale sont créés avec l'UID: GID du fichier utilisateur ayant appelé 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 de menu fixe.

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 de menu fixe.

docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
0
johnklawlor

Pour partager un dossier entre l'hôte docker et le conteneur docker, essayez la commande ci-dessous

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

L'indicateur -v monte le répertoire de travail actuel dans le conteneur. Lorsque le répertoire de l'hôte d'un volume monté sur une liaison 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 sur le volume monté si vous étiez utilisateur non root car le fichier partagé appartiendra à un autre utilisateur de 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 utilisez un utilisateur codé en dur, il ne correspondra pas à l'utilisateur de votre ordinateur portable/Jenkins,

Solution:

Conteneur: Créer un utilisateur en disant 'testuser', par défaut, l'ID de l'utilisateur commencera à 1 000,

Hôte: Créez un groupe avec le groupe "testgroup" avec l'identifiant de groupe 1000 et passez le répertoire au nouveau groupe (testgroup

0
Praveen Muthusamy