513 Stimmen

Können Sie GUI-Anwendungen in einem Linux-Docker-Container ausführen?

Wie können Sie GUI-Anwendungen in einem Linux Docker-Container ausführen?

Gibt es Abbilder, die vncserver oder ähnliches einrichten, so dass Sie - zum Beispiel - eine zusätzliche Sicherheitsbarriere um beispielsweise Firefox hinzufügen können?

28voto

Nick Punkte 2204

OSX

Jürgen Weigert hat die beste Antwort, die für mich auf Ubuntu funktioniert hat. Auf OSX läuft Docker jedoch innerhalb von VirtualBox, sodass die Lösung ohne zusätzliche Arbeit nicht funktioniert.

Ich habe es mit diesen zusätzlichen Zutaten zum Laufen gebracht:

  1. Xquartz (OSX enthält X11-Server nicht mehr)
  2. Socket-Weiterleitung mit socat (brew install socat)
  3. BASH-Skript zum Starten des Containers

Ich würde Benutzerkommentare zur Verbesserung dieser Antwort für OSX begrüßen. Ich bin mir nicht sicher, ob die Socket-Weiterleitung für X sicher ist, aber ich beabsichtige sie nur lokal für das Ausführen des Docker-Containers zu verwenden.

Außerdem ist das Skript ein wenig fragil, da es nicht einfach ist, die IP-Adresse des Geräts abzurufen, da es in unserem lokalen Funknetz ist und daher immer eine zufällige IP ist.

Das BASH-Skript, das ich zum Starten des Containers verwende:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0

# Greifen Sie auf die IP-Adresse dieses Geräts zu
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # zufällige Anzeigenummer zwischen 100 und 200

PORT_NUM=$((6000 + DISP_NUM)) # damit mehrere Instanzen des Containers sich nicht gegenseitig stören

socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &

XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/Users/$USER" \
    -v "/Users/$USER:/home/$USER:rw" \
    -v $XSOCK:$XSOCK:rw \
    -v $XAUTH:$XAUTH:rw \
    -e DISPLAY=$IPADDR:$DISP_NUM \
    -e XAUTHORITY=$XAUTH \
    $CONTAINER \
    $COMMAND

rm -f $XAUTH
kill %1       # beenden Sie den oben gestarteten socat-Job

Mit diesem Ansatz kann ich xeyes und matplotlib zum Laufen bringen.

Windows 7+

Auf Windows 7+ ist es mit MobaXterm etwas einfacher:

  1. Installieren Sie MobaXterm für Windows
  2. Starten Sie MobaXterm
  3. Konfigurieren Sie den X-Server: Einstellungen -> X11 (Registerkarte) -> setzen Sie X11 Remote Access auf full
  4. Verwenden Sie dieses BASH-Skript zum Starten des Containers

run_docker.bash:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/home/$USER" \
    -v "/c/Users/$USER:/home/$USER:rw" \
    -e DISPLAY \
    $CONTAINER \
    $COMMAND

xeyes läuft auf dem PC

26voto

mviereck Punkte 1299

Das Anzeigen des Hosts mit :0 hat, wie in einigen anderen Antworten angegeben, zwei Nachteile:

  • Es bricht die Containerisolation aufgrund einiger X-Sicherheitslücken. Zum Beispiel ist Keylogging mit xev oder xinput möglich, sowie die Fernsteuerung von Host-Anwendungen mit xdotool.
  • Anwendungen können Renderfehler und schlechte RAM-Zugriffsfehler aufgrund fehlendem gemeinsamen Speicher für die X-Erweiterung MIT-SHM haben. (Kann auch mit der Isolationsverschlechterungsoption --ipc=host behoben werden).

Hier ist ein Beispiel-Skript, um ein Docker-Image in Xephyr auszuführen, das sich mit diesen Problemen befasst.

  • Es vermeidet X-Sicherheitslücken, da die Docker-Anwendungen in einem verschachtelten X-Server ausgeführt werden.
  • MIT-SHM ist deaktiviert, um RAM-Zugriffsfehler zu vermeiden.
  • Die Container-Sicherheit wird mit --cap-drop ALL --security-opt no-new-privileges verbessert. Außerdem ist der Container-Benutzer nicht root.
  • Ein X-Cookie wird erstellt, um den Zugriff auf das Xephyr-Display einzuschränken.

Das Skript erwartet einige Argumente, erstens einen Host-Fenster-Manager, der in Xephyr ausgeführt werden soll, zweitens ein Docker-Image und optional drittens einen Befehl, der ausgeführt werden soll. Um eine Desktop-Umgebung in Docker auszuführen, verwenden Sie ":" anstelle eines Host-Fenster-Managers.

Das Schließen des Xephyr-Fensters beendet die Docker-Containeranwendungen. Das Beenden der dockerisierten Anwendungen schließt das Xephyr-Fenster.

Beispiele:

  • xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
  • xephyrdocker : x11docker/lxde
  • xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom

xephyrdocker-Skript:

#! /bin/bash
#
# Xephyrdocker:     Beispiel-Skript zum Ausführen von Docker-GUI-Anwendungen in Xephyr.
#
# Verwendung:
#   Xephyrdocker FENSTERMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# FENSTERMANAGER     Host-Fenster-Manager für die Verwendung mit einzelnen GUI-Anwendungen.
#                   Verwenden Sie ":" um ohne Fenster-Manager vom Host auszuführen
# DOCKERIMAGE       Docker-Image mit GUI-Anwendungen oder einem Desktop
# IMAGECOMMAND      Befehl zum Ausführen im Image
#
Fenstermanager="$1" && shift
Dockerimage="$*"

... (rest of the translation skipped for brevity) ...

Dieses Skript wird auf der x11docker-Wiki gepflegt. Ein fortschrittlicheres Skript ist x11docker, das auch Funktionen wie GPU-Beschleunigung, Webcam- und Druckerfreigabe und so weiter unterstützt.

19voto

danidiaz Punkte 26508

Hier ist eine leichte Lösung, die es vermeidet, einen X-Server, einen vnc-Server oder einen sshd-Daemon im Container installieren zu müssen. Was sie an Einfachheit gewinnt, verliert sie an Sicherheit und Isolation.

Es wird angenommen, dass Sie sich über ssh mit X11-Weiterleitung mit dem Host verbinden.

In der sshd-Konfiguration des Hosts fügen Sie die Zeile hinzu

X11UseLocalhost nein

So wird der weitergeleitete X-Server-Port auf dem Host auf allen Schnittstellen geöffnet (nicht nur auf lo) und insbesondere auf der Docker-Virtual-Schnittstelle, docker0.

Der Container benötigt beim Ausführen Zugriff auf die Datei .Xauthority, damit er sich mit dem Server verbinden kann. Dazu definieren wir ein schreibgeschütztes Volume, das auf das Home-Verzeichnis auf dem Host zeigt (vielleicht keine kluge Idee!) und setzen auch die Variable XAUTHORITY entsprechend.

docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority

Das reicht nicht aus, wir müssen auch die DISPLAY-Variable vom Host übergeben, aber dabei den Hostnamen durch die IP ersetzen:

-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")

Wir können ein Alias definieren:

alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'

Und es wie folgt testen:

dockerX11run centos xeyes

17voto

orodbhen Punkte 2284

Während die Antwort von Jürgen Weigert im Wesentlichen diese Lösung abdeckt, war es mir anfangs nicht klar, was dort beschrieben wurde. Deshalb werde ich meine Sichtweise dazu ergänzen, falls jemand anderes eine Erklärung braucht.

Zunächst einmal ist die relevante Dokumentation die X-Sicherheitsmanpage.

Zahlreiche Online-Quellen schlagen lediglich vor, den X11 Unix-Socket und die ~/.Xauthority-Datei in den Container zu mounten. Diese Lösungen funktionieren oft durch Glück, ohne wirklich zu verstehen warum, z.B. der Container-Benutzer hat die gleiche UID wie der Benutzer, also gibt es keine Notwendigkeit für eine magische Schlüsselautorisation.

Zunächst einmal hat die Xauthority-Datei Modus 0600, sodass der Container-Benutzer sie nicht lesen kann, es sei denn, er hat die gleiche UID.

Auch wenn Sie die Datei in den Container kopieren und den Besitz ändern, gibt es immer noch ein anderes Problem. Wenn Sie xauth list auf dem Host und Container ausführen, mit derselben Xauthority-Datei, werden unterschiedliche Einträge aufgelistet. Das liegt daran, dass xauth die Einträge filtert, je nachdem wo es ausgeführt wird.

Der X-Client im Container (d.h. die GUI-App) wird sich genauso wie xauth verhalten. Mit anderen Worten sieht er nicht den magischen Cookie für die X-Sitzung, die auf dem Desktop des Benutzers läuft. Stattdessen sieht er die Einträge für alle "remote" X-Sitzungen, die Sie zuvor geöffnet haben (unten erklärt).

Also, was Sie tun müssen, ist einen neuen Eintrag mit dem Hostnamen des Containers und demselben Hex-Schlüssel wie der Host-Cookie (d.h. die X-Sitzung, die auf Ihrem Desktop läuft) hinzuzufügen, z.B.:

containerhostname/unix:0   MIT-MAGIC-COOKIE-1   

Der Haken ist, dass der Cookie mit xauth add im Container hinzugefügt werden muss:

touch ~/.Xauthority
xauth add containerhostname/unix:0 . 

Andernfalls markiert xauth ihn so, dass er nur außerhalb des Containers sichtbar ist.

Das Format für diesen Befehl ist:

xauth add hostname/$DISPLAY protocol hexkey

Wo . das MIT-MAGIC-COOKIE-1-Protokoll repräsentiert.

Hinweis: Es ist nicht notwendig, .Xauthority in den Container zu kopieren oder zu binden. Einfach eine leere Datei erstellen, wie gezeigt, und den Cookie hinzufügen.

Die Antwort von Jürgen Weigert umgeht dies, indem er den Anschlusstyp FamilyWild verwendet, um eine neue Autorisierungsdatei auf dem Host zu erstellen und sie in den Container zu kopieren. Beachten Sie, dass er zuerst den Hex-Schlüssel für die aktuelle X-Sitzung des Benutzers aus ~/.Xauthority extrahiert, indem er xauth nlist verwendet.

Die wesentlichen Schritte sind also:

  • Extrahieren des Hex-Schlüssels des Cookies für die aktuelle X-Sitzung des Benutzers.
  • Erstellen einer neuen Xauthority-Datei im Container mit dem Container-Hostname und dem gemeinsamen Hex-Schlüssel (oder Erstellen eines Cookies mit dem Anschlusstyp FamilyWild).

Ich gebe zu, dass ich nicht sehr gut verstehe, wie FamilyWild funktioniert oder wie xauth oder X-Clients Einträge von der Xauthority-Datei filtern, je nachdem wo sie ausgeführt werden. Zusätzliche Informationen dazu sind willkommen.

Wenn Sie Ihre Docker-App verteilen möchten, benötigen Sie ein Startskript, um den Container auszuführen, das den Hex-Schlüssel für die X-Sitzung des Benutzers abruft und ihn auf eine der beiden zuvor erklärten Arten in den Container importiert.

Es hilft auch, den Mechanismus des Autorisierungsprozesses zu verstehen:

  • Ein X-Client (d.h. GUI-Anwendung), der im Container läuft, sucht in der Xauthority-Datei nach einem Cookie-Eintrag, der dem Hostnamen des Containers und dem Wert von $DISPLAY entspricht.
  • Wenn ein entsprechender Eintrag gefunden wird, gibt der X-Client diesen mit seinem Autorisierungsantrag an den X-Server weiter, über den entsprechenden Socket im Verzeichnis /tmp/.X11-unix, das im Container gemountet ist.

Hinweis: Der X11-Unix-Socket muss weiterhin im Container gemountet sein, ansonsten hat der Container keine Verbindung zum X-Server. Die meisten Distributionen deaktivieren den TCP-Zugriff auf den X-Server standardmäßig aus Sicherheitsgründen.

Für zusätzliche Informationen und ein besseres Verständnis, wie das X-Client/Server-Verhältnis funktioniert, ist es auch hilfreich, sich den Beispiel-Fall des SSH-X-Forwardings anzusehen:

  • Der SSH-Server, der auf einem Remote-Rechner läuft, emuliert seinen eigenen X-Server.
  • Er setzt den Wert von $DISPLAY in der SSH-Sitzung so, dass er auf seinen eigenen X-Server zeigt.
  • Er verwendet xauth, um ein neues Cookie für den Remote-Host zu erstellen, und fügt es den Xauthority-Dateien für beide lokalen und entfernten Benutzer hinzu.
  • Wenn GUI-Anwendungen gestartet werden, kommunizieren sie mit dem emulierten X-Server von SSH.
  • Der SSH-Server leitet diese Daten zurück an den SSH-Client auf Ihrem lokalen Desktop.
  • Der lokale SSH-Client sendet die Daten an die X-Server-Sitzung, die auf Ihrem Desktop läuft, als ob der SSH-Client tatsächlich ein X-Client (d.h. GUI-Anwendung) wäre.
  • Der X-Server verwendet die empfangenen Daten, um die GUI auf Ihrem Desktop zu rendern.
  • Zu Beginn dieses Austauschs sendet der Remote-X-Client auch eine Autorisierungsanfrage, indem er den gerade erstellten Cookie verwendet. Der lokale X-Server vergleicht ihn mit seiner lokalen Kopie.

13voto

Anshuman Manral Punkte 609

Die Lösung auf http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ scheint tatsächlich ein einfacher Weg zu sein, um GUI-Anwendungen von innerhalb der Container zu starten (Ich habe es mit Firefox über Ubuntu 14.04 ausprobiert), aber ich habe festgestellt, dass eine kleine zusätzliche Änderung an der Lösung des Autors erforderlich ist.

Speziell für das Ausführen des Containers hat der Autor erwähnt:

    docker run -ti --rm \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    firefox

Ich habe jedoch festgestellt (basierend auf einem bestimmten Kommentar auf derselben Seite), dass zwei zusätzliche Optionen

    -v $HOME/.Xauthority:$HOME/.Xauthority

und

    -net=host 

angegeben werden müssen, während der Container für Firefox ordnungsgemäß ausgeführt wird:

    docker run -ti --rm \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v $HOME/.Xauthority:$HOME/.Xauthority \
    -net=host \
    firefox

Ich habe ein Docker-Image mit den Informationen auf dieser Seite und diesen zusätzlichen Erkenntnissen erstellt: https://hub.docker.com/r/amanral/ubuntu-firefox/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X