Ausführen der App in Docker als Nicht-Root-Benutzer

77

Nach den gestrigen Nachrichten von Shocker scheint es, dass Apps in einem Docker-Container nicht als Root ausgeführt werden sollten. Ich habe versucht, meine zu aktualisieren Dockerfile, um einen App-Benutzer zu erstellen, aber das Ändern der Berechtigungen für App-Dateien (solange sie noch root sind) scheint nicht zu funktionieren. Ich vermute, das liegt daran, dass dem Root-Benutzer möglicherweise keine LXC-Berechtigung erteilt wird.

Hier ist meine Docker-Datei:

# Node.js app Docker file

FROM dockerfile/nodejs
MAINTAINER Thom Nichols "[email protected]"

RUN useradd -ms /bin/bash node

ADD . /data
# This next line doesn't seem to have any effect:
RUN chown -R node /data 

ENV HOME /home/node
USER node

RUN cd /data && npm install

EXPOSE 8888

WORKDIR /data

CMD ["npm", "start"]

Ziemlich einfach, aber wenn ich ls -lalles noch im Besitz von root bin :

[ node@ed7ae33e76e1:/data {docker-nonroot-user} ]$ ls -l /data
total 64K
-rw-r--r--  1 root root  383 Jun 18 20:32 Dockerfile
-rw-r--r--  1 root root  862 Jun 18 16:23 Gruntfile.js
-rw-r--r--  1 root root 1.2K Jun 18 15:48 README.md
drwxr-xr-x  4 root root 4.0K May 30 14:24 assets/
-rw-r--r--  1 root root  416 Jun  3 14:22 bower.json
-rw-r--r--  1 root root  930 May 30 01:50 config.js
drwxr-xr-x  4 root root 4.0K Jun 18 16:08 lib/
drwxr-xr-x 42 root root 4.0K Jun 18 16:04 node_modules/
-rw-r--r--  1 root root 2.0K Jun 18 16:04 package.json
-rw-r--r--  1 root root  118 May 30 18:35 server.js
drwxr-xr-x  3 root root 4.0K May 30 02:17 static/
drwxr-xr-x  3 root root 4.0K Jun 18 20:13 test/
drwxr-xr-x  3 root root 4.0K Jun  3 17:38 views/

Meine aktualisierte Docker-Datei funktioniert hervorragend , da @ creak klargestellt hat, wie Volumes funktionieren. Sobald die anfänglichen Dateien chownbearbeitet wurden, npm installwird sie als Nicht-Root-Benutzer ausgeführt. Und dank eines postinstallHooks wird npm ausgeführt, bower install && grunt assetsdas sich um die verbleibenden Installationsschritte kümmert und die Notwendigkeit von npm install -gNode-Cli-Tools wie Bower, Grunzen oder Coffeescript vermeidet.

thom_nic
quelle
Nur neugierig, sind Sie sicher, dass ein nodeBenutzer erstellt wird?
Am
@am ja der Benutzer ist definitiv erstellt. Wenn ich renne docker run -it myimage /bin/bashund renne, whoamibin ich angemeldet, nodewas ich erwarten würde.
thom_nic

Antworten:

16

Dieser ist etwas knifflig, er liegt tatsächlich an dem Bild, von dem Sie ausgehen.

Wenn Sie sich die Quelle ansehen , stellen Sie fest, dass /data/es sich um ein Volume handelt. Alles, was Sie in tun, Dockerfilewird zur Laufzeit von dem Volume, das dann gemountet wird, verworfen und überschrieben.

Sie können zur Laufzeit chown, indem Sie Ihre CMD auf so etwas wie ändern CMD chown -R node /data && npm start.

knarren
quelle
Interessant! Ich glaube, ich habe das Volumenkonzept (ich werde anfangen zu lesen) oder die Unterscheidung zwischen Laufzeit und Erstellungszeit nicht vollständig verstanden.
thom_nic
3
Leider CMD chown...funktioniert das nicht, da dies ausgeführt wird, wenn der nodeBenutzer, der obv hat, keine Berechtigung zum Ändern der Berechtigungen von Dateien besitzt, deren Eigentümer er ist root. Ich frage mich, ob es in Ordnung ist, wenn der Quellcode zur Laufzeit nicht beschreibbar ist, solange die App Zugriff auf einen beschreibbaren Speicherort für Protokolle und zur Laufzeit kompilierte Dateien hat. Ist es möglich, zur Laufzeit ein Volume zu erstellen, das nicht Root gehört?
thom_nic
Nicht, dass ich davon Wüste. Eine Alternative wäre, den USER-Knoten nicht zu verwenden, root zu behalten und npm mit zu starten su node -c "npm start". Abhängig von Ihren Anforderungen sollte es jedoch in Ordnung sein, schreibgeschützt zu arbeiten, solange Sie Zugriff auf ein beschreibbares Verzeichnis haben.
Knarren
1
Ich werde dies als die richtige Antwort akzeptieren, weil Sie genau wussten, dass das Problem /dataein Band ist. Wenn ich dieses Verzeichnis in etwas anderes ändere /opt/foobaroder /home/node/app, chownfunktioniert es als Teil des Builds einwandfrei. Außerdem habe ich bestätigt, dass chown-ing ein Volume in einem laufenden Container ebenfalls zu funktionieren scheint. Ich denke, die einfachste Lösung besteht darin, kein Volume für die App-Quellen zu verwenden. Ich habe meine resultierende Docker-Datei hier aktualisiert: gist.github.com/thom-nic/5e561e800cbc0f71555c
thom_nic
26

Überprüfen Sie diesen Beitrag: http://www.yegor256.com/2014/08/29/docker-non-root.html In rultor.com führen wir alle Builds in ihren eigenen Docker-Containern aus. Und jedes Mal, bevor wir die Skripte im Container ausführen, wechseln wir zu einem Benutzer ohne Rootberechtigung. Das ist wie:

adduser --disabled-password --gecos '' r
adduser r sudo
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
su -m r -c /home/r/script.sh

r ist der Benutzer, den wir verwenden.

yegor256
quelle
1
Möglicherweise müssen Sie das Home-Verzeichnis des Benutzers explizit erstellen mit:adduser -m --disabled-password --gecos '' r
Bonh
Red Hat-basierte Systeme wie Fedora und CentOS müssen dies anders machen.
Gbraad
18

Update 28.09.2015

Ich habe bemerkt, dass dieser Beitrag ein bisschen Aufmerksamkeit erhält. Ein Ratschlag für alle, die möglicherweise daran interessiert sind, so etwas zu tun. Ich würde versuchen, Python oder eine andere Sprache als Wrapper für Ihre Skriptausführungen zu verwenden. Beim Ausführen nativer Bash-Skripte hatte ich Probleme, als ich versuchte, verschiedene Argumente an meine Container weiterzuleiten. Insbesondere gab es Probleme mit der Interpretation / Flucht von "und" Zeichen durch die Shell.


Ich musste den Benutzer aus einem etwas anderen Grund wechseln.

Ich habe ein Docker-Image erstellt, das eine voll funktionsfähige Installation von ImageMagick und Ffmpeg enthält, mit dem Wunsch, dass ich Transformationen für Bilder / Videos in meinem Host-Betriebssystem durchführen kann. Mein Problem war, dass dies Befehlszeilentools sind, daher ist es etwas schwieriger, sie über Docker auszuführen und die Ergebnisse dann wieder in das Host-Betriebssystem zu übertragen. Ich habe es geschafft, dies durch das Mounten eines Docker-Volumes zu ermöglichen. Dies schien in Ordnung zu sein, außer dass die Bild- / Videoausgabe im Besitz von root war (dh dem Benutzer, unter dem der Docker-Container ausgeführt wurde) und nicht dem Benutzer, der den Befehl ausgeführt hat.

Ich habe mir den Ansatz angesehen, den @ François Zaninotto in seiner Antwort erwähnt hat (das vollständige Make-Skript finden Sie hier ). Es war wirklich cool, aber ich bevorzugte die Option, ein Bash-Shell-Skript zu erstellen, das ich dann auf meinem Pfad registrieren würde. Ich habe einige der Konzepte aus dem Makefile-Ansatz übernommen (insbesondere die Benutzer- / Gruppenerstellung) und dann das Shell-Skript erstellt.

Hier ist ein Beispiel für mein Dockermagick- Shell-Skript:

#!/bin/bash

### VARIABLES

DOCKER_IMAGE='acleancoder/imagemagick-full:latest'
CONTAINER_USERNAME='dummy'
CONTAINER_GROUPNAME='dummy'
HOMEDIR='/home/'$CONTAINER_USERNAME
GROUP_ID=$(id -g)
USER_ID=$(id -u)

### FUNCTIONS

create_user_cmd()
{
  echo \
    groupadd -f -g $GROUP_ID $CONTAINER_GROUPNAME '&&' \
    useradd -u $USER_ID -g $CONTAINER_GROUPNAME $CONTAINER_USERNAME '&&' \
    mkdir --parent $HOMEDIR '&&' \
    chown -R $CONTAINER_USERNAME:$CONTAINER_GROUPNAME $HOMEDIR
}

execute_as_cmd()
{
  echo \
    sudo -u $CONTAINER_USERNAME HOME=$HOMEDIR
}

full_container_cmd()
{
  echo "'$(create_user_cmd) && $(execute_as_cmd) $@'"
}

### MAIN

eval docker run \
    --rm=true \
    -a stdout \
    -v $(pwd):$HOMEDIR \
    -w $HOMEDIR \
    $DOCKER_IMAGE \
    /bin/bash -ci $(full_container_cmd $@)

Dieses Skript ist an das Bild 'acleancoder / imagemagick-full' gebunden. Dies kann jedoch durch Bearbeiten der Variablen oben im Skript geändert werden.

Was es im Grunde tut, ist:

  • Erstellen Sie eine Benutzer-ID und eine Gruppe im Container , die dem Benutzer entsprechen, der das Skript vom Host-Betriebssystem aus ausführt .
  • Hängt das aktuelle Arbeitsverzeichnis des Host-Betriebssystems (unter Verwendung von Docker-Volumes) in das Ausgangsverzeichnis für den Benutzer ein, den wir im ausführenden Docker-Container erstellen .
  • Legt das tmp-Verzeichnis als Arbeitsverzeichnis für den Container fest .
  • Übergibt alle Argumente, die an das Skript übergeben werden, das dann vom ' / bin / bash ' des ausführenden Docker-Containers ausgeführt wird .

Jetzt kann ich die ImageMagick / Ffmpeg- Befehle für Dateien auf meinem Host-Betriebssystem ausführen. Angenommen , ich möchte ein Bild MyImage.jpeg in eine PNG- Datei konvertieren , könnte ich jetzt Folgendes tun:

$ cd ~/MyImages
$ ls
  MyImage.jpeg
$ dockermagick convert MyImage.jpeg Foo.png
$ ls
  Foo.png MyImage.jpeg

Ich habe auch an das 'stdout' angehängt, damit ich den ImageMagick-Identifizierungsbefehl ausführen kann, um Informationen zu einem Bild auf meinem Host abzurufen, z.

$ dockermagick identify MyImage.jpeg
  MyImage.jpeg JPEG 640x426 640x426+0+0 8-bit DirectClass 78.6KB 0.000u 0:00.000

Es besteht die offensichtliche Gefahr, das aktuelle Verzeichnis bereitzustellen und die Weitergabe beliebiger Befehlsdefinitionen zur Ausführung zuzulassen. Es gibt aber auch viele Möglichkeiten, das Skript sicherer zu machen. Ich führe dies in meiner eigenen nicht produktiven persönlichen Umgebung aus, daher sind diese für mich nicht von größter Bedeutung. Ich würde Ihnen jedoch wärmstens empfehlen, die Gefahren zu berücksichtigen, wenn Sie dieses Skript erweitern. Erwähnenswert ist auch, dass dieses Skript keinen OS X-Host berücksichtigt. Die make-Datei , aus der ich Ideen / Konzepte stehle, berücksichtigt dies, sodass Sie dieses Skript dazu erweitern können.

Eine weitere Einschränkung ist, dass ich nur auf Dateien verweisen kann, die sich derzeit in dem Pfad befinden, für den ich das Skript ausführe. Dies liegt an der Art und Weise, wie ich die Volumes einbinde, sodass Folgendes nicht funktionieren würde:

$ cd ~/MyImages
$ ls
  MyImage.jpeg
$ dockermagick convert ~/DifferentDirectory/AnotherImage.jpeg Foo.png
$ ls
  MyImage.jpeg

Gehen Sie am besten in das Verzeichnis, in dem sich das Bild befindet, und führen Sie es direkt aus. Natürlich bin ich mir sicher, dass es auch Möglichkeiten gibt, diese Einschränkung zu umgehen, aber für mich und meine aktuellen Bedürfnisse wird dies ausreichen.

ctrlplusb
quelle
2
Dies ist ein sehr übersichtliches Konzept, bei dem Docker verwendet wird, um Befehlszeilentools zu isolieren / zu portieren!
thom_nic
Das ist ausgezeichnet! Verwenden Sie es jetzt, obwohl in Python angepasst und in meinen eigenen dockerWrapper integriert.
Leichtigkeitsrennen im Orbit
(Das einzige Problem ist, dass es bestimmte Einschränkungen für die Docker-Images festlegt, die Sie verwenden können, nämlich dass sie alle diese Befehle
unverändert unterstützen
Ich bin froh, dass Sie sich für Python entschieden haben. Ich habe das Gleiche getan und war mit meinen Skripten viel zufriedener. Ich bin mir nicht sicher, ob ich Ihnen in Bezug auf die von Ihnen angesprochenen Einschränkungen genau folge, aber wenn ich das richtig verstehe, kann ich davon ausgehen, dass Sie die erforderlichen Docker-Images mit den entsprechenden Befehlszeilen-Tools einrichten müssen, die Sie benötigen.
Querkommunikation
Wenn Sie "sudo Befehl nicht gefunden Fehler" erhalten haben. Verwendung echo "su $CONTAINER_USERNAME -c "in der Funktion execute_as_cmd ().
Arulraj.net
5

Hinweis : Ich antworte hier, da diese Frage angesichts des generischen Titels in Google angezeigt wird, wenn Sie nach einer Lösung für "Ausführen einer App in Docker als Nicht-Root-Benutzer" suchen. Hoffe es hilft denen, die hier gestrandet sind.

Mit Alpine Linux können Sie einen Systembenutzer wie folgt erstellen:

RUN adduser -D -H -S -s /bin/false -u 1000 myuser

Alles in der Dockerfilenach dieser Zeile wird mit ausgeführt myuser.

myuser Benutzer hat:

  • Kein Passwort vergeben
  • kein home dir
  • keine Login-Shell
  • Kein Root- Zugriff.

Dies ist aus adduser --help:

-h DIR      Home directory
-g GECOS    GECOS field
-s SHELL    Login shell
-G GRP      Add user to existing group
-S          Create a system user
-D          Don't assign a password
-H          Don't create home directory
-u UID      User id
-k SKEL     Skeleton directory (/etc/skel)
Rotareti
quelle
0

Hinweis : Diese Antwort wird gegeben, weil viele Leute, die nach einer Verwendung ohne Rootberechtigung suchen, hier landen werden. Beachten Sie, dass hier nicht das Problem behoben wird, das das Problem verursacht hat, sondern der Titel und die Erläuterung einer Antwort von @ yegor256, bei der ein Benutzer ohne Rootberechtigung im Container verwendet wird. Diese Antwort erklärt, wie dies für den Anwendungsfall Nicht-Debian / Nicht-Ubuntu erreicht werden kann. Damit wird das Problem mit Volumes nicht behoben.

Auf Red Hat-basierten Systemen wie Fedora und CentOS kann dies folgendermaßen erfolgen:

RUN adduser user && \
    echo "user ALL=(root) NOPASSWD:ALL" | tee -a /etc/sudoers.d/user && \
    chmod 0440 /etc/sudoers.d/user

In Ihrer Docker-Datei können Sie als dieser Benutzer Befehle ausführen, indem Sie Folgendes tun:

RUN su - user -c "echo Hello $HOME"

Der Befehl kann wie folgt ausgeführt werden:

CMD ["su","-","user","-c","/bin/bash"]

Ein Beispiel hierfür finden Sie hier: https://github.com/gbraad/docker-dev/commit/644c51002f4b8e6fe5bb745638542a4c3d908b16

gbraad
quelle
Dies behebt nicht das Problem des OP, bei dem ein Volume bearbeitet wurde, das nicht für das endgültige Image gilt. Das Senden die Ausgabe von Echo zu einem Rohr auf einem Datei - T - Shirt erscheint seltsam, warum nicht nur append mit ein >>. Wenn Sie einem Benutzer vollen Sudo-Zugriff auf root gewähren, werden alle Vorteile der Ausführung des Containers als Nicht-Root-Benutzer zunichte gemacht.
BMitch
Dies hängt mit einer Antwort zusammen, die von @ yegor256 gegeben wurde. Ich konnte es im Kommentarbereich nicht klarstellen ... Ich werde dies in die Antwort auf Adresse aufnehmen
gbraad