it-swarm.com.de

Was ist der (beste) Weg, um Berechtigungen für freigegebene Docker-Volumes zu verwalten?

Ich habe eine Weile mit Docker herumgespielt und finde immer wieder das gleiche Problem, wenn es um persistente Daten geht.

Ich erstelle meine Dockerfile und mache ein Volume oder verwende --volumes-from, um einen Host-Ordner in meinem Container einzuhängen.

Welche Berechtigungen sollte ich auf das freigegebene Volume auf dem Host anwenden?

Ich kann mir zwei Möglichkeiten vorstellen:

  • Bisher habe ich allen Schreib-/Lesezugriff erteilt, damit ich aus dem Docker-Container in den Ordner schreiben kann.

  • Ordnen Sie die Benutzer vom Host dem Container zu, sodass ich detailliertere Berechtigungen zuweisen kann. Nicht sicher, dass dies möglich ist und nicht viel darüber gefunden hat. Bisher kann ich nur den Container als Benutzer ausführen: docker run -i -t -user="myuser" postgres, aber dieser Benutzer hat eine andere UID als mein Host myuser, daher funktionieren Berechtigungen nicht. Ich bin auch nicht sicher, ob die Zuordnung der Benutzer Sicherheitsrisiken birgt.

Gibt es andere Alternativen?

Wie gehen Sie mit dieser Ausgabe um?

295
Xabs

UPDATE 2016-03-02: Ab Docker 1.9.0 hat Docker Volumes benannt die Data-Only-Container ersetzen . Die nachstehende Antwort sowie mein verknüpfter Blogbeitrag haben immer noch einen Wert im Sinne von wie man Daten im Docker betrachtet. Sie sollten jedoch benannte Datenträger verwenden, um das unten beschriebene Muster anstelle von Datencontainern zu implementieren.


Ich glaube, der kanonische Weg, dieses Problem zu lösen, ist die Verwendung von Nur-Daten-Containern . Bei diesem Ansatz erfolgt der gesamte Zugriff auf die Volumendaten über Container, die -volumes-from des Datencontainers verwenden, sodass die Host-Uid/gid keine Rolle spielt.

Ein in der Dokumentation angegebener Anwendungsfall ist beispielsweise das Sichern eines Datenvolumens. Dazu wird ein anderer Container für die Sicherung über tar verwendet, und er verwendet ebenfalls -volumes-from, um den Datenträger bereitzustellen. Ich denke, der Schlüsselpunkt von grok ist: Statt über den Zugriff auf die Daten auf dem Host mit den richtigen Berechtigungen nachzudenken, sollten Sie über einen anderen Container nachdenken, wie Sie das tun, was Sie benötigen - Sicherungen, Browsen usw. - . Die Container selbst müssen konsistente UIDs/Gids verwenden, müssen jedoch nichts auf dem Host zuordnen und bleiben somit portabel.

Dies ist auch für mich relativ neu, aber wenn Sie einen bestimmten Anwendungsfall haben, können Sie das gerne kommentieren und ich werde versuchen, die Antwort zu erweitern.

UPDATE: Für den angegebenen Anwendungsfall in den Kommentaren haben Sie möglicherweise ein Bild some/graphite zum Ausführen von Graphite und ein Bild some/graphitedata als Datencontainer. Das Ignorieren von Ports und ähnlichem ist die Dockerfile von image some/graphitedata wie folgt:

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

Erstellen und erstellen Sie den Datencontainer:

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

Die some/graphite-Docker-Datei sollte auch die gleiche Uid/Gids erhalten, daher könnte es etwa so aussehen:

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

Und es würde wie folgt laufen:

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

Ok, das gibt uns nun unseren Graphitcontainer und den zugehörigen Data-Only-Container mit dem richtigen Benutzer/der richtigen Gruppe (Hinweis: Sie können den some/graphite-Container auch für den Datencontainer erneut verwenden, wobei das Entypoing/cmd beim Ausführen außer Kraft gesetzt wird, es aber vorhanden ist als separate Bilder ist IMO klarer).

Nehmen wir an, Sie möchten etwas im Datenordner bearbeiten. Anstatt das Volume an den Host zu binden und dort zu bearbeiten, erstellen Sie einen neuen Container, um diesen Job auszuführen. Nennen wir es some/graphitetools. Lasst auch den entsprechenden Benutzer/die entsprechende Gruppe erstellen, genau wie das Bild 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"]

Sie können dies DRY machen, indem Sie some/graphite oder some/graphitedata in der Dockerfile erben, oder anstatt ein neues Bild zu erstellen, verwenden Sie einfach eines der vorhandenen Bilder (überschreiben Sie ggf. Entrypoint/cmd).

Jetzt rennst du einfach:

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

und dann vi /data/graphite/whatever.txt. Dies funktioniert perfekt, da alle Container denselben Graphitbenutzer mit der entsprechenden UID/GID haben.

Da Sie niemals /data/graphite vom Host aus mounten, ist es Ihnen egal, wie die Host-Uid/gid der Uid/gid zugeordnet wird, die in den graphite- und graphitetools-Containern definiert ist. Diese Container können jetzt auf jedem Host bereitgestellt werden und funktionieren weiterhin einwandfrei.

Das Besondere daran ist, dass graphitetools alle möglichen nützlichen Dienstprogramme und Skripts enthalten kann, die Sie jetzt auch auf tragbare Weise bereitstellen können.

UPDATE 2: Nachdem ich diese Antwort geschrieben hatte, entschied ich mich, einen ausführlicheren Blogbeitrag über diesen Ansatz zu schreiben. Ich hoffe, es hilft.

UPDATE 3: Ich habe diese Antwort korrigiert und weitere Details hinzugefügt. Zuvor enthielt es einige falsche Annahmen über den Besitz und die Perms. Der Besitz wird normalerweise zum Zeitpunkt der Datenträgererstellung zugewiesen, d. H. Im Datencontainer, da der Datenträger gerade erstellt wird. Siehe dieses Blog . Dies ist jedoch keine Anforderung - Sie können den Datencontainer einfach als "Referenz/Handle" verwenden und die Besitzrechte/Perms in einem anderen Container über chown in einem Entrypoint festlegen, der mit gosu endet, um den Befehl als korrekten Benutzer auszuführen. Wenn sich jemand für diesen Ansatz interessiert, kommentieren Sie ihn bitte und ich kann mit diesem Ansatz Links zu einem Beispiel bereitstellen.

157
Raman

Eine sehr elegante Lösung ist auf dem offiziellen redis image und allgemein in allen offiziellen Bildern zu sehen.

Schritt für Schritt beschrieben:

  • Erstellen Sie vor allen anderen Aktionen einen Benutzer/eine Gruppe

Wie man bei Dockerfile-Kommentaren sieht:

fügen Sie zuerst unseren Benutzer und die Gruppe hinzu, um sicherzustellen, dass ihre IDs konsistent zugewiesen werden, unabhängig davon, welche Abhängigkeiten hinzugefügt werden

  • Installieren Sie gosu mit Dockerfile

gosu ist eine Alternative zu suSudo für ein einfaches Abspringen vom Root-Benutzer. (Redis wird immer mit redis Benutzer ausgeführt)

  • Konfigurieren Sie /data volume und legen Sie es als workdir fest

Durch Konfigurieren des/data-Datenträgers mit dem Befehl VOLUME /data verfügen wir jetzt über einen separaten Datenträger, der entweder ein Docker-Datenträger sein oder an ein Hostverzeichnis gebunden sein kann.

Wenn Sie es als workdir (WORKDIR /data) konfigurieren, wird es zum Standardverzeichnis, von dem Befehle ausgeführt werden.

  • Fügen Sie die Docker-Entypoint-Datei hinzu und legen Sie sie mit dem Standard-CMD-Redis-Server als ENTRYPOINT fest

Das bedeutet, dass alle Containerausführungen durch das Docker-Entypoint-Skript ausgeführt werden. Standardmäßig ist der auszuführende Befehl Redis-Server.

docker-entrypoint ist ein Skript, das eine einfache Funktion ausführt: Ändern Sie den Besitz des aktuellen Verzeichnisses (/ data) und wechseln Sie von root zu redis Benutzer, um redis-server auszuführen. (Wenn der ausgeführte Befehl kein Redis-Server ist, wird der Befehl direkt ausgeführt.)

_/Dies hat folgende Wirkung

Wenn das Verzeichnis/data an den Host gebunden ist, bereitet der Docker-Eingangspunkt die Benutzerberechtigungen vor, bevor der redis-server unter redis user ausgeführt wird.

Dies gibt Ihnen die Gewissheit, dass der Container unter einer beliebigen Volumenkonfiguration ausgeführt werden muss.

Wenn Sie das Volume für verschiedene Bilder freigeben müssen, müssen Sie natürlich sicherstellen, dass sie dieselbe Benutzer-ID/Gruppen-ID verwenden. Andernfalls werden die Benutzerberechtigungen des letzten Containers durch den neuesten Container entfernt.

42
Dimitris

Dies ist wohl nicht der beste Weg für die meisten Umstände, aber es wurde noch nicht erwähnt, so dass es vielleicht jemandem helfen wird.

  1. Binden Sie das Mount-Host-Volume

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. Ändern Sie das Startskript Ihres Containers, um die GID des Volumes zu ermitteln, an dem Sie interessiert sind

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

  3. Stellen Sie sicher, dass Ihr Benutzer zu einer Gruppe mit dieser GID gehört (möglicherweise müssen Sie eine neue Gruppe erstellen). In diesem Beispiel werde ich so tun, als würde meine Software als Benutzer nobody ausgeführt, wenn sie sich im Container befindet. Daher möchte ich sicherstellen, dass nobody zu einer Gruppe mit einer Gruppen-ID von 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

Das gefällt mir, weil ich Gruppenberechtigungen auf meinen Host-Volumes leicht ändern kann und weiß, dass diese aktualisierten Berechtigungen im Docker-Container gelten. Dies geschieht ohne Erlaubnis oder Eigentumsänderungen an meinen Host-Ordnern/-Dateien, was mich glücklich macht.

Ich mag das nicht, weil es keine Gefahr darstellt, sich einer beliebigen Gruppe innerhalb des Containers hinzuzufügen, die zufällig eine von Ihnen gewünschte GID verwendet. Es kann nicht mit einer USER -Klausel in einer Docker-Datei verwendet werden (es sei denn, dieser Benutzer hat Root-Rechte, nehme ich an). Außerdem schreit es Hackjob ;-)

Wenn Sie Hardcore sein möchten, können Sie dies natürlich auf viele Arten erweitern - z. Suche nach allen Gruppen in beliebigen Unterdateien, mehreren Volumes usw.

28
Hamy

Ok, das wird jetzt bei Docker-Ausgabe # 7198 verfolgt

Im Moment beschäftige ich mich mit Ihrer zweiten Option:

Ordnen Sie die Benutzer vom Host dem Container zu

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 Ich bin momentan eher geneigt zu HamyAntwort

15
Leo Gallucci

Versuchen Sie, einen Befehl zu Dockerfile hinzuzufügen

RUN usermod -u 1000 www-data

credits gehen an https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

10
FDisk

Genau wie Sie, ich habe nach einem Weg gesucht, Benutzer/Gruppen von Host zu Docker-Containern abzubilden, und dies ist der kürzeste Weg, den ich bisher gefunden habe:

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

Dies ist ein Auszug aus meinem docker-compose.yml.

Die Idee ist, (im Nur-Lese-Modus) Benutzer-/Gruppenlisten aus dem Host in den Container zu laden. Nach dem Starten des Containers hat derselbe Uid-> Benutzername (sowie Gruppen) Übereinstimmungen mit dem Host. Jetzt können Sie Benutzer-/Gruppeneinstellungen für Ihren Dienst innerhalb des Containers so konfigurieren, als würde er auf Ihrem Host-System funktionieren. 

Wenn Sie sich entscheiden, Ihren Container auf einen anderen Host zu verschieben, müssen Sie lediglich den Benutzernamen in der Dienstkonfigurationsdatei in den auf diesem Host vorhandenen Namen ändern.

7
alex_edev

Hier ist ein Ansatz, bei dem immer noch ein Nur-Daten-Container verwendet wird, der jedoch nicht mit dem Anwendungscontainer synchronisiert werden muss (in Bezug auf dieselbe UID/GID).

Vermutlich möchten Sie eine App im Container als $ USER ohne Rootberechtigung ohne Login-Shell ausführen. 

In der Dockerfile:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

Dann in entrypoint.sh:

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

Verwenden Sie die Optionen --uidmap und --private-uids, um einen Docker-Container zu sichern und den Root für Docker-Container zu ändern

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

Aus Sicherheitsgründen können Sie auch mehrere Funktionen (--cap-drop) im Docker-Container entfernen

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

UPDATE Unterstützung sollte in docker > 1.7.0 kommen

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

4
umount

Basisbild

Verwenden Sie dieses Bild: https://hub.docker.com/r/reduardo7/docker-Host-user

oder

Wichtig: zerstört die Container-Portabilität zwischen Hosts.

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) Build mit docker

4) Laufen Sie!

sh run.sh
3
Eduardo Cuomo

Mein Ansatz ist es, die aktuelle UID/GID zu ermitteln und dann einen solchen Benutzer/eine Gruppe innerhalb des Containers zu erstellen und das Skript unter ihm auszuführen. Daher werden alle Dateien, die er erstellt, dem Benutzer entsprechen, der das Skript ausführt:

# 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

In meinem speziellen Fall habe ich versucht, mein Knotenpaket mit dem Node-Docker-Image zu erstellen, damit ich npm nicht auf dem Implementierungsserver installieren muss. Es hat gut funktioniert, bis ich außerhalb des Containers und auf dem Host-Computer versucht habe, eine Datei in das Verzeichnis node_modules zu verschieben, das das Node-Docker-Image erstellt hatte, und dem ich Berechtigungen verweigert hatte, weil es im Besitz von root war. Ich erkannte, dass ich dies umgehen konnte, indem ich das Verzeichnis aus dem Container auf den Host-Computer kopierte. Über Docker-Dokumente ...

Auf den lokalen Rechner kopierte Dateien werden mit der UID: GID des .__ erstellt. Benutzer, der den Befehl docker cp aufgerufen hat.

Dies ist der Bash-Code, den ich verwendet habe, um den Besitz des von und innerhalb des Docker-Containers erstellten Verzeichnisses zu ändern.

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

Bei Bedarf können Sie das Verzeichnis mit einem zweiten Docker-Container entfernen.

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

Um den Ordner zwischen Docker-Host und Docker-Container freizugeben, versuchen Sie den folgenden Befehl

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

Das Flag -v hängt das aktuelle Arbeitsverzeichnis in den Container ein. Wenn das Host-Verzeichnis eines gebundenen Volumes nicht vorhanden ist, erstellt Docker dieses Verzeichnis automatisch für Sie auf dem Host.

Es gibt jedoch zwei Probleme, die wir hier haben:

  1. Sie können nicht auf das gemountete Volume schreiben, wenn Sie Benutzer ohne Rootberechtigung sind, da die gemeinsam genutzte Datei einem anderen Benutzer in Host gehört.
  2. Sie sollten den Prozess nicht in Ihren Containern als root ausführen, aber selbst wenn Sie als hartcodierter Benutzer ausgeführt werden, entspricht er nicht dem Benutzer auf Ihrem Laptop/Jenkins.

Lösung:

Container: Einen Benutzer erstellen, sagen Sie 'Testbenutzer'. Die Benutzer-ID beginnt standardmäßig mit 1000.

Host: Erstellen Sie eine Gruppe, sagen Sie 'testgroup' mit der Gruppennummer 1000 und ordnen Sie das Verzeichnis der neuen Gruppe (testgroup) zu

0