Berechtigung beim Zugriff auf das Hostverzeichnis in Docker verweigert

282

Kurz gesagt: Ich versuche, ein Host-Verzeichnis in Docker bereitzustellen, kann dann aber nicht aus dem Container heraus darauf zugreifen, selbst wenn die Zugriffsberechtigungen gut aussehen.

Die Details:

ich mache

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

und dann

ls -al

Es gibt mir:

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

und viel mehr Zeilen wie diese (ich denke, das ist der relevante Teil).

Wenn ich mache

cd /Downloads
ls

Das Ergebnis ist

ls: cannot open directory .: Permission denied

Der Host ist Fedora 20 mit Docker 1.0.0 und go1.2.2.

Was läuft falsch?

user3753011
quelle

Antworten:

269

Die vollständige Geschichte finden Sie in diesem Blog-Beitrag von Project Atomic über Volumes und SELinux .

Speziell:

Dies wurde kürzlich einfacher, da Docker endlich einen Patch zusammengeführt hat, der in Docker-1.7 angezeigt wird (wir haben den Patch in Docker-1.6 unter RHEL, CentOS und Fedora veröffentlicht).

Dieser Patch unterstützt "z" und "Z" als Optionen für die Volume-Mounts (-v).

Beispielsweise:

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

Wird automatisch das chcon -Rt svirt_sandbox_file_t /var/db in der Manpage beschriebene tun .

Noch besser, Sie können Z verwenden.

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

Dadurch wird der Inhalt im Container mit dem genauen MCS-Label gekennzeichnet, mit dem der Container ausgeführt wird. Grundsätzlich wird er dort ausgeführt, chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/dbwo er s0:c1,c2für jeden Container unterschiedlich ist.

gregswift
quelle
18
Das funktioniert wie ein Zauber. Andere Lösungen sind meistens Problemumgehungen.
Tuxdna
4
vgl. der Abschnitt mit den
Bandetiketten
Oh Mann, es funktioniert wirklich. Ich habe das endlich gefunden. Danke vielmals! Gibt es offizielle Unterlagen dazu?
Kirby
1
Upstream hat es als letzten Absatz in diesem Abschnitt docs.docker.com/engine/reference/commandline/run/…
gregswift
1
Es ist möglich, die Berechtigungen unter SELinux zu korrigieren, während das Volume gleichzeitig schreibgeschützt bereitgestellt wird, indem beide Optionen gleichzeitig durch ein Komma getrennt verwendet werden : -v $(pwd):/app:ro,Z. Dies sollte als die richtige Antwort markiert werden.
Danirod
263

Es ist ein SELinux- Problem.

Sie können vorübergehend ausstellen

su -c "setenforce 0"

auf dem Host, um durch Ausführen auf eine SELinux-Regel zuzugreifen oder diese hinzuzufügen

chcon -Rt svirt_sandbox_file_t /path/to/volume
user3761313
quelle
3
Ist / path / to / volume der Pfad des Hosts? Wenn ja, scheint diese Lösung nicht mit Datencontainern zu funktionieren?
Roy Truelove
6
Vergessen Sie nicht, su -c "setenforce 1" zu machen ... sonst funktioniert es nur, weil SELinux noch deaktiviert ist
vcarel
Das hat mein Problem gelöst. Danke, ich hoffe, sie haben eine Lösung dafür.
Hokutosei
19
Das Hinzufügen der Selinux-Regel ist der beste Weg, da es in den meisten Fällen keine gute Idee ist, Container mit privilegiertem Modus auszuführen.
Zoro_77
7
Wie Zoro_77 sagte, füge eine Regel hinzu und stopdisablingselinux.com ;)
GabLeRoux
71

WARNUNG: Diese Lösung birgt Sicherheitsrisiken.

Versuchen Sie, den Container als privilegiert auszuführen:

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

Eine andere Option (die ich nicht ausprobiert habe) wäre, einen privilegierten Container zu erstellen und dann nicht privilegierte Container darin zu erstellen.

John Phillips
quelle
1
@JBernardo Welche der beiden Optionen hat das Problem gelöst?
user100464
@ user100464--privileged=true
JBernardo
1
Hilf in meinem Fall nicht. Debian Whezzy mit Backport-Kernel 3.16, aber nicht aktivierter SELinux-Konfiguration. :(
Aholbreich
Wenn Sie Docker-Composer verwenden, fügen Sie 'privileged: true' hinzu
Lionel Morrison
35
Mach das nicht. --privilegedist ein Sicherheitsrisiko
Navin
38

In der Regel treten Berechtigungsprobleme bei der Bereitstellung eines Host-Volumes auf, weil die UID / GID im Container gemäß den UID / GID-Berechtigungen der Datei auf dem Host keinen Zugriff auf die Datei hat. Dieser spezielle Fall ist jedoch anders.

Der Punkt am Ende der Berechtigungszeichenfolge zeigt an drwxr-xr-x., dass SELinux konfiguriert ist. Wenn Sie einen Host-Mount mit SELinux verwenden, müssen Sie eine zusätzliche Option an das Ende der Volume-Definition übergeben:

  • Die zOption gibt an, dass der Bind-Mount-Inhalt von mehreren Containern gemeinsam genutzt wird.
  • Die ZOption gibt an, dass der Bind-Mount-Inhalt privat und nicht freigegeben ist.

Ihr Volume Mount-Befehl würde dann folgendermaßen aussehen:

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

Weitere Informationen zu Host-Mounts mit SELinux finden Sie unter: https://docs.docker.com/storage/#configure-the-selinux-label


Für andere Benutzer, bei denen dieses Problem bei Containern auftritt, die als ein anderer Benutzer ausgeführt werden, müssen Sie sicherstellen, dass die UID / GID des Benutzers im Container über Berechtigungen für die Datei auf dem Host verfügt. Auf Produktionsservern erfolgt dies häufig, indem die UID / GID im Image-Erstellungsprozess so gesteuert wird, dass sie mit einer UID / GID auf dem Host übereinstimmt, der Zugriff auf die Dateien hat (oder, noch besser, keine Host-Mounts in der Produktion verwenden).

Ein benanntes Volume wird häufig Host-Mounts vorgezogen, da es das Volume-Verzeichnis aus dem Image-Verzeichnis initialisiert, einschließlich etwaiger Dateieigentümer und Berechtigungen. Dies geschieht, wenn das Volume leer ist und der Container mit dem benannten Volume erstellt wird.

MacOS-Benutzer haben jetzt OSXFS, das UID / GIDs automatisch zwischen dem Mac-Host und den Containern verarbeitet. Ein Ort, an dem es nicht hilft, sind Dateien aus der eingebetteten VM, die in den Container eingebunden werden, wie /var/lib/docker.sock.

Für Entwicklungsumgebungen, in denen sich die Host-UID / GID pro Entwickler ändern kann, besteht meine bevorzugte Lösung darin, den Container mit einem als Root ausgeführten Einstiegspunkt zu starten, die UID / GID des Benutzers im Container so zu korrigieren, dass sie mit der UID / GID des Host-Volumes übereinstimmt, und Verwenden Sie gosudiese Option, um vom Root zum Containerbenutzer zu wechseln und die Anwendung im Container auszuführen. Das wichtige Skript dafür befindet sich fix-permsin meinen Basis-Image-Skripten, die unter https://github.com/sudo-bmitch/docker-base zu finden sind

Das wichtige Bit aus dem fix-permsSkript ist:

# 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

Dadurch werden die UID des Benutzers im Container und die UID der Datei usermodabgerufen. Wenn sie nicht übereinstimmen, wird die UID aufgerufen. Zuletzt wird eine rekursive Suche durchgeführt, um alle Dateien zu reparieren, die die UIDs nicht geändert haben. Ich mag dies besser als das Ausführen eines Containers mit einem -u $(id -u):$(id -g)Flag, da für den obigen Einstiegspunktcode nicht jeder Entwickler ein Skript ausführen muss, um den Container zu starten, und für alle Dateien außerhalb des Volumes, deren Eigentümer der Benutzer ist, werden die Berechtigungen korrigiert.


Sie können Docker auch ein Hostverzeichnis aus einem Image initialisieren lassen, indem Sie ein benanntes Volume verwenden, das einen Bind-Mount ausführt. Dieses Verzeichnis muss im Voraus vorhanden sein, und Sie müssen einen absoluten Pfad zum Host-Verzeichnis angeben, im Gegensatz zu Host-Volumes in einer Compose-Datei, bei denen es sich um relative Pfade handeln kann. Das Verzeichnis muss auch leer sein, damit Docker es initialisieren kann. Drei verschiedene Optionen zum Definieren eines benannten Volumes für einen Bind-Mount sehen folgendermaßen aus:

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

Wenn Sie versuchen, Benutzernamensräume zu verwenden, werden Sie feststellen, dass Host-Volumes Berechtigungsprobleme haben, da die UIDs / GIDs der Container verschoben werden. In diesem Szenario ist es wahrscheinlich am einfachsten, Host-Volumes zu vermeiden und nur benannte Volumes zu verwenden.

BMitch
quelle
32

Von access.redhat.com:Sharing_Data_Across_Containers :

Die Einstellungen für das Host-Volume sind nicht portierbar, da sie hostabhängig sind und möglicherweise auf keinem anderen Computer funktionieren. Aus diesem Grund gibt es kein Dockerfile-Äquivalent zum Mounten von Hostverzeichnissen in den Container. Beachten Sie außerdem, dass das Hostsystem keine Kenntnisse über die SELinux-Richtlinie für Container hat. Wenn die SELinux-Richtlinie erzwungen wird, kann das bereitgestellte Hostverzeichnis daher unabhängig von der rw-Einstellung nicht in den Container geschrieben werden. Derzeit können Sie dies umgehen, indem Sie dem Host-Verzeichnis den richtigen SELinux-Richtlinientyp zuweisen ":

chcon -Rt svirt_sandbox_file_t host_dir

Dabei ist host_dir ein Pfad zu dem Verzeichnis auf dem Hostsystem, das an den Container angehängt ist.

Es scheint nur eine Problemumgehung zu sein, aber ich habe es versucht und es funktioniert.

Thomas8
quelle
14

Ich habe überprüft, dass chcon -Rt svirt_sandbox_file_t /path/to/volumedies funktioniert und Sie nicht als privilegierter Container ausgeführt werden müssen.

Dies ist auf:

  • Docker Version 0.11.1-dev, Build 02d20af / 0.11.1
  • CentOS 7 als Host und Container mit aktiviertem SELinux.
Jeff McCormick
quelle
2
Unter github.com/docker/docker/pull/5910 finden Sie offizielle Unterstützung für die Neukennzeichnung in Docker.
cpuguy83
13

Versuchen Sie es 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

Schauen Sie sich das Dokument https://docs.docker.com/engine/reference/commandline/volume_create/ an.

Cupen
quelle
3
Versuchte viele Antworten zu diesem Thema auf SO, aber tatsächlich half diese. Vielen Dank!
Paul
Es hat den Fehler der Erlaubnis behoben. Aber jetzt, wenn ich versuche, physischen Standort zu montieren, wird Voulme montiert ???? @ cupen
kunal verma
1
@kunalverma Ja. Wenn es Ihnen nicht gefällt, finden Sie hier die einfachere Antwort. stackoverflow.com/a/31334443/4909388
cupen
4

Ich hatte ein ähnliches Problem, mein Problem wurde durch eine Nichtübereinstimmung zwischen der UID des Hosts und der UID des Containernutzers verursacht. Das Update bestand darin, die UID des Benutzers als Argument an den Docker-Build zu übergeben und den Benutzer des Containers mit derselben UID zu erstellen.

In der DockerFile:

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

Im Build-Schritt:

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

Danach gab mir das Ausführen des Containers und der Befehle gemäß OP das erwartete Ergebnis.

RoboCop87
quelle
1
Was ist, wenn Sie die UID erst zur Laufzeit kennen? (Ich erstelle ein Image für Kollegen, um einige Tools zu packen, die in ihr Dateisystem zurückschreiben, aber unterschiedliche UIDs haben.) Ich denke, ich könnte es root und nur Adduser laufen lassen?
Inger
Darauf habe ich leider keine gute Antwort. Wenn jemand eine Lösung hat, würde mich das auch interessieren. Ich vermute, dass die Docker-Einstiegspunktfunktion eine Lösung bietet.
RoboCop87
0

Ich habe dieses Problem mithilfe eines Datencontainers behoben. Dies hat auch den Vorteil, dass die Daten von der Anwendungsschicht isoliert werden. Sie könnten es so laufen lassen:

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

Dieses Tutorial bietet eine gute Erklärung zur Verwendung von Datencontainern.

tmsss
quelle
-1

In meiner Situation war das Problem anders. Ich weiß nicht warum, aber selbst wenn das Verzeichnis auf dem Host darauf chmod 777ausgeführt wurde, war es im Docker als sichtbar 755.

Das Laufen im Container hat das sudo chmod 777 my_volume_dirProblem behoben.

CodeSandwich
quelle
5
chmod 777repariert kaum etwas.
Erki Aring
Es tut mir leid, aber Sie haben den Punkt verpasst. Der Punkt ist, dass die Berechtigungen für Innencontainer gesenkt wurden und nicht von außen behoben werden konnten.
CodeSandwich
-2

sudo -s habe den Trick für mich auf MAC gemacht

Nachiket Joshi
quelle
1
Wenn Sie abstimmen, hinterlassen Sie einen Kommentar und erklären Sie, warum. Ich bin auf genau das gleiche Problem gestoßen und konnte dieses Problem durch sudo -s lösen.
Nachiket Joshi
Nicht jedes Docker-Image verfügt über Sudo, und dies ist nicht in jedem Szenario möglich.
SOFe
2
Installieren Sie sudo nicht auf Containern. Ein Angreifer kann Sudo in einem Container verwenden.
Arnold Balliu