Autorisation refusée sur l'accès au répertoire hôte dans Docker

283

En bref: j'essaie de monter un répertoire hôte dans Docker, mais je ne peux pas y accéder depuis le conteneur, même si les autorisations d'accès semblent bonnes.

Les détails:

je fais

sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash

puis

ls -al

Ça me donne:

total 8892
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 .
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 ..
-rwxr-xr-x.   1 root root       0 Jun 18 14:34 .dockerenv
-rwx------.   1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x.  18 1000 1000   12288 Jun 16 11:40 Downloads
drwxr-xr-x.   2 root root    4096 Jan 29 18:10 bin
drwxr-xr-x.   2 root root    4096 Apr 19  2012 boot
drwxr-xr-x.   4 root root     340 Jun 18 14:34 dev
drwxr-xr-x.  56 root root    4096 Jun 18 14:34 etc
drwxr-xr-x.   2 root root    4096 Apr 19  2012 home

et beaucoup plus de lignes comme ça (je pense que c'est la partie pertinente).

Si je fais

cd /Downloads
ls

le résultat est

ls: cannot open directory .: Permission denied

L'hôte est Fedora 20, avec Docker 1.0.0 et go1.2.2.

Qu'est-ce qui ne va pas?

user3753011
la source

Réponses:

269

Voir cet article de blog Project Atomic sur Volumes et SELinux pour l'histoire complète.

Plus précisément:

Cela est devenu plus facile récemment depuis que Docker a finalement fusionné un correctif qui apparaîtra dans docker-1.7 (Nous avons porté le correctif dans docker-1.6 sur RHEL, CentOS et Fedora).

Ce patch ajoute la prise en charge de "z" et "Z" comme options sur les montages de volume (-v).

Par exemple:

docker run -v /var/db:/var/db:z rhel7 /bin/sh

Effectue automatiquement les opérations chcon -Rt svirt_sandbox_file_t /var/db décrites dans la page de manuel.

Encore mieux, vous pouvez utiliser Z.

docker run -v /var/db:/var/db:Z rhel7 /bin/sh

Cela étiquetera le contenu à l'intérieur du conteneur avec l'étiquette MCS exacte avec laquelle le conteneur s'exécutera, en gros, il s'exécute chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/dblà où s0:c1,c2diffère pour chaque conteneur.

gregswift
la source
18
Cela fonctionne comme un charme. D'autres solutions sont principalement des solutions de contournement.
tuxdna
4
cf. la section des étiquettes de volume dans la documentation du docker
maxschlepzig
Oh, mec, ça marche vraiment. J'ai finalement trouvé ça. Merci beaucoup! Existe-t-il une documentation officielle à ce sujet?
Kirby
1
En amont l'a comme dernier paragraphe de cette section docs.docker.com/engine/reference/commandline/run/…
gregswift
1
Il est possible de fixer les autorisations sous SELinux lors du montage du volume en lecture seule en même temps en utilisant les deux options en même temps séparés par une virgule: -v $(pwd):/app:ro,Z. Cela devrait être marqué comme la bonne réponse.
danirod
264

Il s'agit d'un problème SELinux .

Vous pouvez émettre temporairement

su -c "setenforce 0"

sur l'hôte pour accéder ou ajouter une règle SELinux en exécutant

chcon -Rt svirt_sandbox_file_t /path/to/volume
user3761313
la source
3
est / chemin / vers / volume le chemin de l'hôte? Si oui, il ne semble pas que cette solution fonctionnerait avec des conteneurs de données?
Roy Truelove
6
n'oubliez pas de faire su -c "setenforce 1" ... sinon cela ne fonctionnera que parce que SELinux est toujours désactivé
vcarel
cela a résolu mon problème. merci, j'espère qu'ils auront un correctif pour cela.
Hokutosei
19
L'ajout de la règle selinux est le meilleur moyen, car il n'est pas judicieux dans la plupart des cas d'exécuter des conteneurs en mode privilégié.
Zoro_77
7
Comme l'a dit Zoro_77, ajoutez une règle et stopdisablingselinux.com ;)
GabLeRoux
71

AVERTISSEMENT: cette solution présente des risques de sécurité.

Essayez d'exécuter le conteneur avec privilèges:

sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash

Une autre option (que je n'ai pas essayée) serait de créer un conteneur privilégié puis de créer des conteneurs non privilégiés à l'intérieur de celui-ci.

John Phillips
la source
1
@JBernardo Laquelle des deux options a résolu le problème?
user100464
@ user100464--privileged=true
JBernardo
1
N'aide pas dans mon cas. Debian Whezzy avec noyau backporté 3.16 mais pas la configuration SELinux activée. :(
aholbreich
si vous utilisez docker-composer ajoutez 'privileged: true'
Lionel Morrison
35
Ne faites pas cela. --privilegedest un risque pour la sécurité
Navin
38

En règle générale, les problèmes d'autorisations avec un montage de volume hôte sont dus au fait que l'uid / gid à l'intérieur du conteneur n'a pas accès au fichier conformément aux autorisations uid / gid du fichier sur l'hôte. Cependant, ce cas spécifique est différent.

Le point à la fin de la chaîne d'autorisation drwxr-xr-x., indique que SELinux est configuré. Lorsque vous utilisez un montage hôte avec SELinux, vous devez passer une option supplémentaire à la fin de la définition du volume:

  • L' zoption indique que le contenu du montage de liaison est partagé entre plusieurs conteneurs.
  • L' Zoption indique que le contenu du montage de liaison est privé et non partagé.

Votre commande de montage de volume ressemblerait alors à:

sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash

En savoir plus sur les montages d'hôte avec SELinux sur: https://docs.docker.com/storage/#configure-the-selinux-label


Pour d'autres qui voient ce problème avec les conteneurs s'exécutant en tant qu'utilisateur différent, vous devez vous assurer que l'uid / gid de l'utilisateur à l'intérieur du conteneur dispose des autorisations sur le fichier sur l'hôte. Sur les serveurs de production, cela se fait souvent en contrôlant l'uid / gid dans le processus de création d'image pour faire correspondre un uid / gid sur l'hôte qui a accès aux fichiers (ou mieux encore, n'utilisez pas de montages d'hôte en production).

Un volume nommé est souvent préféré pour héberger des montages car il initialise le répertoire de volume à partir du répertoire d'image, y compris toute propriété de fichier et autorisations. Cela se produit lorsque le volume est vide et que le conteneur est créé avec le volume nommé.

Les utilisateurs de MacOS ont maintenant OSXFS qui gère automatiquement les uid / gid entre l'hôte Mac et les conteneurs. Les fichiers de l'intérieur de la machine virtuelle intégrée qui sont montés dans le conteneur, comme /var/lib/docker.sock, ne sont pas utiles.

Pour les environnements de développement où l'uid / gid de l'hôte peut changer par développeur, ma solution préférée est de démarrer le conteneur avec un point d'entrée s'exécutant en tant que root, de corriger l'uid / gid de l'utilisateur à l'intérieur du conteneur pour correspondre au uid / gid du volume de l'hôte, et puis utilisez gosupour passer de la racine à l'utilisateur du conteneur pour exécuter l'application à l'intérieur du conteneur. Le script important pour cela est fix-permsdans mes scripts d'image de base, qui peuvent être trouvés à: https://github.com/sudo-bmitch/docker-base

Le bit important du fix-permsscript est:

# update the uid
if [ -n "$opt_u" ]; then
  OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
  NEW_UID=$(stat -c "%u" "$1")
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

Cela obtient l'uid de l'utilisateur à l'intérieur du conteneur, et l'uid du fichier, et s'ils ne correspondent pas, appelle usermodpour ajuster l'uid. Enfin, il effectue une recherche récursive pour corriger tous les fichiers qui n'ont pas changé d'uid. J'aime mieux que d'exécuter un conteneur avec un -u $(id -u):$(id -g)indicateur, car le code de point d'entrée ci-dessus ne nécessite pas que chaque développeur exécute un script pour démarrer le conteneur, et tous les fichiers en dehors du volume appartenant à l'utilisateur verront leurs autorisations corrigées.


Vous pouvez également demander à docker d'initialiser un répertoire hôte à partir d'une image en utilisant un volume nommé qui effectue un montage de liaison. Ce répertoire doit exister à l'avance et vous devez fournir un chemin absolu vers le répertoire hôte, contrairement aux volumes hôtes dans un fichier de composition qui peuvent être des chemins relatifs. Le répertoire doit également être vide pour que docker l'initialise. Trois options différentes pour définir un volume nommé sur un montage de liaison ressemblent à:

  # create the volume in advance
  $ docker volume create --driver local \
      --opt type=none \
      --opt device=/home/user/test \
      --opt o=bind \
      test_vol

  # create on the fly with --mount
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

  # inside a docker-compose file
  ...
  volumes:
    bind-test:
      driver: local
      driver_opts:
        type: none
        o: bind
        device: /home/user/test
  ...

Enfin, si vous essayez d'utiliser des espaces de noms d'utilisateurs, vous constaterez que les volumes hôtes ont des problèmes d'autorisation car les uid / gid des conteneurs sont décalés. Dans ce scénario, il est probablement plus facile d'éviter les volumes hôtes et de n'utiliser que les volumes nommés.

BMitch
la source
32

Depuis access.redhat.com:Sharing_Data_Across_Containers :

Les paramètres de volume de l'hôte ne sont pas portables, car ils dépendent de l'hôte et peuvent ne pas fonctionner sur une autre machine. Pour cette raison, il n'existe aucun équivalent Dockerfile pour le montage de répertoires hôtes sur le conteneur. Sachez également que le système hôte n'a aucune connaissance de la politique SELinux du conteneur. Par conséquent, si la stratégie SELinux est appliquée, le répertoire hôte monté n'est pas accessible en écriture sur le conteneur, quel que soit le paramètre rw. Actuellement, vous pouvez contourner ce problème en affectant le type de stratégie SELinux approprié au répertoire hôte ":

chcon -Rt svirt_sandbox_file_t host_dir

Où rép_hôte est un chemin d'accès au répertoire du système hôte monté sur le conteneur.

Il semble que ce ne soit qu'une solution de contournement, mais j'ai essayé et cela fonctionne.

Thomas8
la source
14

J'ai vérifié que chcon -Rt svirt_sandbox_file_t /path/to/volumecela fonctionne et que vous n'avez pas à exécuter en tant que conteneur privilégié.

C'est sur:

  • Docker version 0.11.1-dev, build 02d20af / 0.11.1
  • CentOS 7 en tant qu'hôte et conteneur avec SELinux activé.
jeff mccormick
la source
2
Voir github.com/docker/docker/pull/5910 pour la prise en charge officielle du réétiquetage dans Docker.
cpuguy83
13

Essayez docker volume create.

mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash

Jetez un œil au document https://docs.docker.com/engine/reference/commandline/volume_create/

cupen
la source
3
J'ai essayé beaucoup de réponses sur ce problème sur SO, mais en fait celui-ci a aidé. Merci!
Paul
Il a résolu l'erreur d'autorisation. Mais maintenant, si j'essaie de monter l'emplacement physique, il monte voulme ???? @ cupen
kunal verma
1
@kunalverma Oui. Si vous ne l'aimez pas, voici la réponse la plus simple. stackoverflow.com/a/31334443/4909388
cupen
4

J'ai eu un problème similaire, le mien a été causé par un décalage entre l'UID de l'hôte et l'UID de l'utilisateur du conteneur. Le correctif consistait à passer l'UID de l'utilisateur en tant qu'argument à la génération Docker et à créer l'utilisateur du conteneur avec le même UID.

Dans le DockerFile:

ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER

Dans l'étape de construction:

docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID

Après cela, l'exécution du conteneur et des commandes selon l'OP m'a donné le résultat attendu.

RoboCop87
la source
1
Que faire si vous ne connaissez pas l'UID avant l'exécution? (Je construis une image pour mes collègues, pour empaqueter des outils qui réécrivent dans leur système de fichiers, mais ils ont des UID différents). Je suppose que je pourrais le garder root et seulement ajouter un utilisateur lors de l'exécution?
inger
Je n'ai malheureusement pas de bonne réponse à cela. Si quelqu'un d'autre a une solution, je serais également intéressé. Je soupçonne que la fonctionnalité de point d'entrée Docker pourrait fournir une solution.
RoboCop87
0

J'ai résolu ce problème en utilisant un conteneur de données, cela a également l'avantage d'isoler les données de la couche application. Vous pouvez l'exécuter comme ceci:

docker run --volumes-from=<container-data-name> ubuntu

Ce didacticiel fournit une bonne explication sur l'utilisation des conteneurs de données.

tmsss
la source
-1

Dans ma situation, le problème était différent. Je ne sais pas pourquoi, mais même si le répertoire sur l'hôte avait chmod 777fonctionné dessus, dans docker il était visible comme 755.

Courir à l'intérieur du conteneur l'a sudo chmod 777 my_volume_dircorrigé.

CodeSandwich
la source
5
chmod 777ne règle presque jamais rien.
Erki Aring
Je suis désolé, mais vous avez manqué le point. Le fait est que les privilèges à l'intérieur du conteneur ont été réduits et qu'il ne pouvait pas être corrigé de l'extérieur.
CodeSandwich
-2

sudo -s a fait l'affaire pour moi sur MAC

Nachiket Joshi
la source
1
Si vous votez, laissez un commentaire et expliquez pourquoi. J'ai rencontré exactement le même problème et j'ai pu le résoudre par sudo -s.
Nachiket Joshi
Toutes les images de docker n'ont pas sudo, et ce n'est pas possible dans tous les scénarios.
SOFe
2
N'installez pas sudo sur des conteneurs. Un attaquant peut utiliser sudo dans un conteneur.
Arnold Balliu