È possibile eseguire applicazioni GUI in un contenitore Docker?


409

Come è possibile eseguire applicazioni GUI in un contenitore Docker ?

Ci sono immagini configurate vncservero qualcosa in modo che tu possa, ad esempio, aggiungere un sandbox di speedbump extra in giro per dire Firefox?


Questa domanda sembra riguardare solo Linux (in base all'età e al contenuto delle risposte) e non a Windows. In tal caso, possiamo modificare il titolo per chiarire questo? Grazie
UuDdLrLrSs


Risposte:


238

Puoi semplicemente installare un vncserver insieme a Firefox :)

Ho spinto un'immagine, vnc / firefox, qui: docker pull creack/firefox-vnc

L'immagine è stata creata con questo Dockerfile:

# Firefox over VNC
#
# VERSION               0.1
# DOCKER-VERSION        0.2

FROM    ubuntu:12.04
# Make sure the package repository is up to date
RUN     echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN     apt-get update

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN     apt-get install -y x11vnc xvfb firefox
RUN     mkdir ~/.vnc
# Setup a password
RUN     x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN     bash -c 'echo "firefox" >> /.bashrc'

Ciò creerà un contenitore Docker che esegue VNC con la password 1234:

Per Docker versione 18 o successiva:

docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

Per Docker versione 1.3 o successiva:

docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

Per Docker precedente alla versione 1.3:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create

2
Come utilizzerei un client VNC per visualizzarlo da remoto? La digitazione nella porta IP + non sembra funzionare.
user94154

17
Innanzitutto, è necessario controllare la porta allocata (facendo docker inspect <container id>o semplicemente docker ps, quindi ci si collega
all'ip del

9
l'immagine creackfirefox-vnc non riesce con errore: immettere la password VNC: stty: input standard: ioctl inappropriato per i dispositivi del dispositivo: nessun file o directory stty: input standard: ioctl non appropriato per il dispositivo x11vnc -usepw: impossibile trovare una password da utilizzare.
alfonsodev,

6
Utilizza bene la finestra mobile> Esecuzione di app GUI con Docker fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker
Dennis C

7
Non esiste un nome utente, la password è chiaramente indicata nella risposta e qualsiasi client vnc lo farà. Nel mio caso, mi piace quello nativo di osx. (da finder, premi command + K e connettiti a vnc: // <docker ip>: <contenitore esposto porta>)
creack

195

Xauthority diventa un problema con i sistemi più recenti. Posso scartare qualsiasi protezione con xhost + prima di eseguire i miei contenitori docker oppure posso passare un file Xauthority ben preparato. I file tipici di Xauthority sono specifici dell'hostname. Con la finestra mobile, ogni contenitore può avere un nome host diverso (impostato con la finestra mobile run -h), ma anche l'impostazione del nome host del contenitore identico al sistema host non ha aiutato nel mio caso. xeyes (mi piace questo esempio) semplicemente ignorerebbe il cookie magico e non trasmetterebbe credenziali al server. Quindi viene visualizzato un messaggio di errore "Nessun protocollo specificato Impossibile aprire il display"

Il file Xauthority può essere scritto in modo tale che il nome host non abbia importanza. Dobbiamo impostare la famiglia di autenticazione su "FamilyWild". Non sono sicuro, se xauth ha una riga di comando adeguata per questo, quindi ecco un esempio che combina xauth e sed per farlo. Dobbiamo cambiare i primi 16 bit dell'output di nlist. Il valore di FamilyWild è 65535 o 0xffff.

docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes

8
Solo una nota, -v $XSOCK:$XSOCK -v $XAUTH:$XAUTHpuò essere abbreviata in-v $XSOCK -v $XAUTH
Piotr Aleksander Chmielowski,

2
@PiotrAleksanderChmielowski che non ha funzionato per me, versione Docker 1.12.0, build 8eab29e
tbc0

14
@Dirk: potresti voler sostituire :0con $DISPLAY. Ciò significa xauth nlist $DISPLAY | ...che docker run -ti -e DISPLAY=$DISPLAY .... Di solito l'X DISPLAY lo è :0, ma non sempre (e soprattutto non se ci si connette tramite ssh -X).
johndodo,

4
Su Ubuntu 16.04 xauth crea il /tmp/.docker.xauthfile con le 600autorizzazioni. Ciò comporta che xauth all'interno del contenitore finestra mobile non sia in grado di leggere il file. È possibile verificare eseguendo xauth listnel contenitore finestra mobile. Ho aggiunto chmod 755 $XAUTHdopo il xauth nlist :0 | ...comando per risolvere questo problema.
Abai,

2
@Abai Perché usare 755, se 444 o 644 sono sufficienti?
Daniel Alder,

68

Ho appena trovato questo post sul blog e voglio condividerlo qui con te perché penso che sia il modo migliore per farlo ed è così facile.

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/

PRO:
+ nessuna roba x server nel contenitore docker
+ nessun client / server vnc necessario
+ nessuna ssh con x forwarding
+ contenitori docker molto più piccoli

CONS:
- utilizzo di x sull'host (non destinato al sandbox sicuro)

nel caso in cui il collegamento fallisca un giorno, ho inserito la parte più importante qui:
dockerfile:

FROM ubuntu:14.04

RUN apt-get update && apt-get install -y firefox

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox

costruisci l'immagine:

docker build -t firefox .

e il comando run:

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

ovviamente puoi farlo anche nel comando run con sh -c "echo script-here"

SUGGERIMENTO: per l'audio dai un'occhiata a: https://stackoverflow.com/a/28985715/2835523


Come posso farlo su Windows 7? Devo installare un server X?
walkignison

3
Come la maggior parte delle risposte qui, questo vale solo per Unix, credo - fino a quando Windows non supporta il sistema X Window.
A. Binzxxxxxx,

Pensi che potrebbe funzionare se ho installato X server su Windows o se ho anche raggruppato un server X nel mio contenitore Docker?
walkignison

1
Penso che sia necessario installare anche in Dockerfile apt-get -y install sudoper creare una /etc/sudoers.dcartella.
mulg0r

1
potrebbe anche essere necessario consentire connessioni a X da qualsiasi host con$ xhost +
Bandoos,

52

Con i volumi di dati della finestra mobile è molto semplice esporre il socket del dominio unix di xorg all'interno del contenitore.

Ad esempio, con un file Docker come questo:

FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes

Puoi fare quanto segue:

$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes

Questo ovviamente è essenzialmente lo stesso di X-forwarding. Concede al contenitore pieno accesso a xserver sull'host, quindi è consigliato solo se ti fidi di cosa c'è dentro.

Nota: se sei preoccupato per la sicurezza, una soluzione migliore sarebbe quella di limitare l'app al controllo di accesso obbligatorio o basato sul ruolo . Docker raggiunge un buon isolamento, ma è stato progettato con uno scopo diverso in mente. Usa AppArmor , SELinux o GrSecurity , progettati per rispondere alle tue preoccupazioni.


5
È inoltre necessario consentire l'accesso a X Server da altri host utilizzando uno strumento come xhost. Per aprirlo completamente, utilizzare xhost +sull'host.
Tully,

3
xhost +localÈ necessario solo @Tully . Sarebbe comunque meglio rendere ~/.Xauthoritydisponibile il file nel contenitore, in modo che possa autenticarsi.
Aryeh Leib Taurog,

3
sei riuscito a farlo funzionare su un Mac (usando boot2docker)?
Karl Forner,

4
Funzionava piuttosto bene per me su un laptop Ubuntu 14.04 con docker 1.5 precedente; ma ora non funziona su Ubuntu 15.04, docker 1.6.2, con l'errore Can't open display: :0. Qualche idea?
cboettig,

6
Ho usato xhost +si:localuser:$USERper autorizzare solo l'utente di avviare il contenitore.
Nick Breen,

26

Puoi anche usare un subutente: https://github.com/timthelion/subuser

Ciò ti consente di impacchettare molte app gui nella finestra mobile. Firefox ed emacs sono stati testati finora. Con firefox, tuttavia, webGL non funziona. Chromium non funziona affatto.

EDIT: il suono funziona!

EDIT2: Nel momento in cui l'ho pubblicato per la prima volta, il subutente ha fatto notevoli progressi. Ora ho un sito Web su subuser.org e un nuovo modello di sicurezza per la connessione a X11 tramite bridge XPRA .


3
Si noti che il subutente è ancora molto nuovo e relativamente non testato. In caso di problemi, inviare segnalazioni di bug!
timthelion,

Eviterei X11 se c'è un modo che puoi. La tua app killer eseguirà il proxy tor nella finestra mobile e eseguirà un browser completo con plug-in in una finestra mobile figlio in modo tale che il firewall ecc. Costringa tutta la rete a uscire tramite la finestra mobile. Ciò comporterebbe giri attorno all'attuale pacchetto del browser per l'usabilità del web perché lasceresti passare contenuti ricchi.
Sarà il

1
Il problema è per te con la sicurezza X11? O vuoi che funzioni con Windows? O che vuoi che funzioni da remoto? Tutti i precedenti? Penso che rendere questo lavoro con VNC sia del tutto possibile (anche se non lo renderei il metodo predefinito perché aggiunge una dipendenza da VNC). Far funzionare il subutente in remoto non è davvero possibile / significativo. C'è anche questo: github.com/rogaha/docker-desktop ma dalle segnalazioni di bug sembra che xpra possa essere inutilizzabile nella vita reale.
timthelion,

24

OSX

Jürgen Weigert ha la migliore risposta che ha funzionato per me su Ubuntu, tuttavia su OSX, la finestra mobile funziona all'interno di VirtualBox e quindi la soluzione non funziona senza altro lavoro.

Ho funzionato con questi ingredienti aggiuntivi:

  1. Xquartz (OSX non viene più fornito con il server X11)
  2. inoltro socket con socat (brew install socat)
  3. bash script per avviare il contenitore

Apprezzerei i commenti degli utenti per migliorare questa risposta per OSX, non sono sicuro che l'inoltro dei socket per X sia sicuro, ma il mio uso previsto è di eseguire il contenitore docker solo localmente.

Inoltre, lo script è un po 'fragile in quanto non è facile ottenere l'indirizzo IP della macchina poiché è sul nostro wireless locale, quindi è sempre un IP casuale.

Lo script BASH che uso per avviare il contenitore:

#!/usr/bin/env bash

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

# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # random display number between 100 and 200

PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother

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       # kill the socat job launched above

Sono in grado di far funzionare xeyes e matplotlib con questo approccio.

Windows 7+

È più semplice su Windows 7+ con MobaXterm:

  1. Installa MobaXterm per Windows
  2. Avvia MobaXterm
  3. Configura X server: Impostazioni -> X11 (scheda) -> imposta X11 Remote Access su full
  4. Utilizzare questo script BASH per avviare il contenitore

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 in esecuzione su PC


non capivo cosa intendevi con lo script bash - come posso eseguirlo in Windows?
deller

@deller Faccio sviluppo software su Windows usando GIT, quindi ho la shell GIT-bash disponibile per me.
Nick,

Ho seguito i passaggi. Tuttavia, ottengo error: XDG_RUNTIME_DIR not set in the environment.e Error: cannot open display: VAIO:0.0. Hai incontrato qualcosa del genere?
user3275095

1
Viene visualizzato un errore relativo all'utente non trovato, ad esempio "nessuna voce corrispondente nel file passwd" Qualche vantaggio?
walkignison

19

Condivisione della visualizzazione dell'host: 0, come indicato in alcune altre risposte, presenta due svantaggi:

  • Rompe l'isolamento del contenitore a causa di alcune falle di sicurezza X. Ad esempio, il keylogging con xevo xinputè possibile e il controllo remoto delle applicazioni host con xdotool.
  • Le applicazioni possono presentare problemi di rendering e errori di accesso alla RAM a causa della mancanza di memoria condivisa per l'estensione X MIT-SHM. (Può anche essere risolto con l'opzione di degradazione dell'isolamento --ipc=host).

Di seguito uno script di esempio per eseguire un'immagine docker in Xephyr che risolve questo problema.

  • Evita le perdite di sicurezza di X mentre le applicazioni docker vengono eseguite in un server X nidificato.
  • MIT-SHM è disabilitato per evitare errori di accesso alla RAM.
  • La sicurezza del contenitore è migliorata con --cap-drop ALL --security-opt no-new-privileges. Anche l'utente del contenitore non è root.
  • Viene creato un cookie X per limitare l'accesso al display di Xephyr.

Lo script prevede alcuni argomenti, in primo luogo l'esecuzione di un gestore di finestre host in Xephyr, in secondo luogo un'immagine docker, in terzo luogo un comando di immagine da eseguire. Per eseguire un ambiente desktop nella finestra mobile, utilizzare ":" invece di un gestore finestre host.

La chiusura della finestra di Xephyr termina le applicazioni del contenitore finestra mobile. La chiusura delle applicazioni ancorate chiude la finestra di Xephyr.

Esempi:

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

script xephyrdocker:

#! /bin/bash
#
# Xephyrdocker:     Example script to run docker GUI applications in Xephyr.
#
# Usage:
#   Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run without window manager from host, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"

# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"

# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
  [ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber

# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd

# command to run docker
# --rm                               created container will be discarded.
# -e DISPLAY=$Newdisplay             set environment variable to new display
# -e XAUTHORITY=/Xcookie             set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro      provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro      Share new X socket of Xephyr
# --user $Useruid:$Usergid           Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro       /etc/passwd file with user entry
# --group-add audio                  Allow access to /dev/snd if shared with '--device /dev/snd' 
# --cap-drop ALL                     Security: disable needless capabilities
# --security-opt no-new-privileges   Security: forbid new privileges
Dockercommand="docker run --rm \
  -e DISPLAY=:$Newdisplaynumber \
  -e XAUTHORITY=/Xcookie \
  -v $Xclientcookie:/Xcookie:ro \
  -v $Newxsocket:$Newxsocket:rw \
  --user $Useruid:$Usergid \
  -v $Etcpasswd:/etc/passwd:ro \
  --group-add audio \
  --env HOME=/tmp \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  $(command -v docker-init >/dev/null && echo --init) \
  $Dockerimage"

echo "docker command: 
$Dockercommand
"

# command to run Xorg or Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber             first argument has to be new display
# -auth $Xservercookie           path to cookie file for X server. Must be different from cookie file of client, not sure why
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp                  disable tcp connections for security reasons
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
  -auth $Xservercookie \
  -extension MIT-SHM \
  -nolisten tcp \
  -screen 1000x750x24 \
  -retro"

echo "X server command:
$Xcommand
"

# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd

# create xinitrc
{ echo "#! /bin/bash"

  echo "# set environment variables to new display and new cookie"
  echo "export DISPLAY=:$Newdisplaynumber"
  echo "export XAUTHORITY=$Xclientcookie"

  echo "# same keyboard layout as on host"
  echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"

  echo "# create new XAUTHORITY cookie file" 
  echo ":> $Xclientcookie"
  echo "xauth add :$Newdisplaynumber . $(mcookie)"
  echo "# create prepared cookie with localhost identification disabled by ffff,"
  echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
  echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')" 
  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
  echo "cp $Xclientcookie $Xservercookie"
  echo "chmod 644 $Xclientcookie"

  echo "# run window manager in Xephyr"
  echo $Windowmanager' & Windowmanagerpid=$!'

  echo "# show docker log"
  echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'

  echo "# run docker"
  echo "$Dockercommand"
} > $Xinitrc

xinit  $Xinitrc -- $Xcommand
rm -Rf $Cachefolder

Questo script è gestito su x11docker wiki . Uno script più avanzato è x11docker che supporta anche funzionalità come l'accelerazione GPU, la condivisione di webcam e stampanti e così via.


18

Ecco una soluzione leggera che evita di dover installare Xserver, vncserver o sshddemone sul contenitore. Ciò che guadagna in semplicità perde in sicurezza e isolamento.

Si presuppone che ci si connetta al computer host tramite ssh con l' X11inoltro.

Nella sshdconfigurazione dell'host, aggiungere la linea

X11UseLocalhost no

In modo che la porta del server X inoltrata sull'host sia aperta su tutte le interfacce (non solo lo) e in particolare sull'interfaccia virtuale Docker,docker0 .

Il contenitore, quando eseguito, deve accedere al .Xauthorityfile in modo che possa connettersi al server. Per fare ciò, definiamo un volume di sola lettura che punta alla home directory sull'host (forse non è una saggia idea!) E impostiamo anche la XAUTHORITYvariabile di conseguenza.

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

Questo non è abbastanza, dobbiamo anche passare la variabile DISPLAY dall'host, ma sostituendo il nome host con l'ip:

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

Possiamo definire un alias:

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

E testalo in questo modo:

dockerX11run centos xeyes

2
(Questo è ottimo per le app fidate. Per qualsiasi tipo di sandboxing, tuttavia, vuoi evitare l'inoltro X.)
Will

1
Se si preferisce non montare l'intera home directory nel contenitore si può solo montare il .Xauthorityfile stesso: -v $HOME/.Xauthority:/root/.Xauthority -e XAUTHORITY=/root/.Xauthority.
Robert Haines,

2
Invece di cambiare X11UseLocalhost, puoi anche usare l'opzione aggiuntiva --net=hostper il docker runcomando (che trovi qui ).
ingomueller.net

--net=hostè una cattiva idea come ora se apri una porta nel container sarà aperta anche nell'host ...
MrR

16

Mentre la risposta di Jürgen Weigert copre essenzialmente questa soluzione, all'inizio non mi è stato chiaro cosa ci fosse stato descritto. Quindi aggiungerò la mia opinione su questo, nel caso in cui qualcun altro abbia bisogno di chiarimenti.

Prima di tutto, la documentazione pertinente è la manpage di sicurezza X. .

Numerose fonti online suggeriscono di montare solo la presa unix X11 e il ~/.Xauthority file nel contenitore. Queste soluzioni spesso funzionano per fortuna, senza capire davvero perché, ad esempio l'utente del contenitore finisce con lo stesso UID dell'utente, quindi non è necessaria l'autorizzazione della chiave magica.

Prima di tutto, il file Xauthority ha la modalità 0600, quindi l'utente del contenitore non sarà in grado di leggerlo a meno che non abbia lo stesso UID.

Anche se copi il file nel contenitore e cambi la proprietà, c'è ancora un altro problema. Se esegui xauth listl'host e il contenitore, con lo stesso Xauthorityfile, vedrai diverse voci elencate. Questo perché xauthfiltra le voci in base a dove viene eseguita.

Il client X nel contenitore (ad es. App GUI) si comporterà come xauth. In altre parole, non vede il cookie magico per la sessione X in esecuzione sul desktop dell'utente. Invece, vede le voci per tutte le sessioni X "remote" che hai aperto in precedenza (spiegato di seguito).

Quindi, ciò che devi fare è aggiungere una nuova voce con il nome host del contenitore e la stessa chiave esadecimale del cookie host (ovvero la sessione X in esecuzione sul desktop), ad esempio:

containerhostname/unix:0   MIT-MAGIC-COOKIE-1   <shared hex key>

Il trucco è che il cookie deve essere aggiunto xauth addall'interno del contenitore:

touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>

Altrimenti, xauthtaggalo in modo che sia visibile solo all'esterno del contenitore.

Il formato per questo comando è:

xauth add hostname/$DISPLAY protocol hexkey

Dove .rappresenta il MIT-MAGIC-COOKIE-1protocollo.

Nota: non è necessario copiare o eseguire il bind-mount .Xauthoritynel contenitore. Basta creare un file vuoto, come mostrato, e aggiungere il cookie.

La risposta di Jürgen Weigert aggira il problema utilizzando il FamilyWildtipo di connessione per creare un nuovo file di autorità sull'host e copiarlo nel contenitore. Si noti che per prima cosa estrae la chiave esadecimale per la sessione X corrente ~/.Xauthoritydall'uso xauth nlist.

Quindi i passaggi essenziali sono:

  • Estrarre la chiave esadecimale del cookie per l'attuale sessione X dell'utente.
  • Crea un nuovo file Xauthority nel contenitore, con il nome host del contenitore e la chiave esadecimale condivisa (o crea un cookie con il FamilyWildtipo di connessione).

Ammetto di non capire molto bene come FamilyWildfunziona, o come xautho i client X filtrano le voci dal file Xauthority a seconda di dove vengono eseguite. Ulteriori informazioni al riguardo sono benvenute.

Se desideri distribuire l'app Docker, avrai bisogno di uno script di avvio per l'esecuzione del contenitore che ottiene la chiave esadecimale per la sessione X dell'utente e la importa nel contenitore in uno dei due modi spiegati in precedenza.

Aiuta anche a comprendere i meccanismi del processo di autorizzazione:

  • Un client X (ovvero un'applicazione GUI) in esecuzione nel contenitore cerca nel file Xauthority una voce cookie che corrisponda al nome host del contenitore e al valore di $DISPLAY.
  • Se viene trovata una voce corrispondente, il client X la passa con la sua richiesta di autorizzazione al server X, attraverso il socket appropriato nella /tmp/.X11-unixdirectory montata nel contenitore.

Nota: il socket Unix X11 deve ancora essere montato nel contenitore, altrimenti il ​​contenitore non avrà alcun percorso verso il server X. La maggior parte delle distribuzioni disabilita l'accesso TCP al server X per impostazione predefinita per motivi di sicurezza.

Per ulteriori informazioni e per comprendere meglio come funziona la relazione client / server X, è utile esaminare il caso di esempio dell'inoltro SSH X:

  • Il server SSH in esecuzione su una macchina remota emula il proprio server X.
  • Imposta il valore di $DISPLAYnella sessione SSH in modo che punti al proprio server X.
  • Utilizza xauthper creare un nuovo cookie per l'host remoto e lo aggiunge ai Xauthorityfile sia per gli utenti locali che per quelli remoti.
  • Quando vengono avviate le app GUI, parlano con il server X emulato di SSH.
  • Il server SSH inoltra questi dati al client SSH sul desktop locale.
  • Il client SSH locale invia i dati alla sessione del server X in esecuzione sul desktop, come se il client SSH fosse in realtà un client X (ovvero app GUI).
  • Il server X utilizza i dati ricevuti per eseguire il rendering della GUI sul desktop.
  • All'inizio di questo scambio, il client X remoto invia anche una richiesta di autorizzazione, utilizzando il cookie appena creato. Il server X locale lo confronta con la sua copia locale.

12

Non è leggero ma è una buona soluzione che offre la parità delle funzionalità docker con la virtualizzazione desktop completa. Sia Xfce4 o IceWM per Ubuntu e CentOS funzionano e l' noVNCopzione consente un facile accesso tramite un browser.

https://github.com/ConSol/docker-headless-vnc-container

Funziona noVNCcosì come tigerVNCvncserver. Quindi richiede startxWindow Manager. Inoltre, libnss_wrapper.soviene utilizzato per emulare la gestione delle password per gli utenti.


qualcuno l'ha provato?
Guilhermecgs,

3
@guilhermecgs sì, e funziona benissimo. Da allora ho anche provato xprasu docker, che è X senza root. È xprastato l'IMO più adatto ed è più efficiente di VNC.
dashesy

Giusto per essere chiari ... Posso avere un'esperienza desktop completa (GNOME, KDE) con questa immagine?
Guilhermecgs,

Ho provato solo Xfce4 e IceWM (che è in quel repository). Naturalmente l'esperienza sarà limitata, ad esempio i dispositivi di montaggio non verranno visualizzati sul desktop (gvfs) a meno che non si passi --device /dev/...alla finestra mobile e si impostino i --capprivilegi necessari . Ciò vanifica lo scopo del contenimento, ma è possibile passare attraverso i dispositivi. Con alcune modifiche dovrebbe essere possibile, credo, far funzionare GNOME / KDE sotto VNC. Ho eseguito più X nella finestra mobile con schede nvidia (senza VNC o Xpra), quindi è certamente fattibile.
dashesy

Non ci abbiamo provato finora. La più grande sfida su questo sarebbe quella di far apparire un demone D-Bus funzionante. La maggior parte dei desktop gnome o KDE ne avrà bisogno. Possa il progetto ubuntu-desktop-lxde-vnc aiutarti qui.
toschneck,

11

La soluzione fornita su http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ sembra essere un modo semplice per avviare le applicazioni della GUI dall'interno dei contenitori (ho provato per Firefox su Ubuntu 14.04) ma ho scoperto che è necessaria una piccola modifica aggiuntiva alla soluzione pubblicata dall'autore.

In particolare, per l'esecuzione del contenitore, l'autore ha menzionato:

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

Ma ho scoperto che (sulla base di un particolare commento sullo stesso sito) che due opzioni aggiuntive

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

e

    -net=host 

è necessario specificare durante l'esecuzione del contenitore affinché Firefox funzioni correttamente:

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

Ho creato un'immagine docker con le informazioni su quella pagina e questi risultati aggiuntivi: https://hub.docker.com/r/amanral/ubuntu-firefox/


3
Ho scoperto che non hai nemmeno bisogno di passare la /tmp/.X11-unixpresa. Funziona solo con il montaggio .Xauthoritye --net=host.
CMCDragonkai,

2
Questa è in realtà l'unica soluzione che funziona in questi giorni. L'uso /tmp/.X11-unixcome volume non funziona più, poiché la finestra mobile rifiuta silenziosamente i montaggi del volume dalle directory appiccicose.
Christian Hujer,

1
Penso che dipenda dalla distro che stai usando. Puoi sicuramente collegare il socket Unix X11 su CentOS. È anche importante capire cosa --network=hostfa. Fornisce al contenitore pieno accesso allo stack di rete dell'host, che potrebbe essere indesiderabile, a seconda di ciò che si sta tentando di fare. Se stai solo armeggiando con l'esecuzione di GUI containerizzate sul desktop, non dovrebbe importare.
orodbhen

7

Esiste un'altra soluzione di lord.garbage per eseguire app GUI in un contenitore senza utilizzare l'inoltro VNC, SSH e X11. È menzionato anche qui .


1
Questo è fantastico se la sicurezza non è un problema. Se lo scopo di agganciare qualcosa è isolarlo, è meglio evitare X11 dentro-fuori dal contenitore.
Sarà il

7

Se vuoi eseguire un'applicazione GUI senza testa, leggi qui . Quello che devi fare è creare un monitor virtuale con xvfbo altri software simili. Questo è molto utile se si desidera eseguire i test Selenium, ad esempio con i browser.

Qualcosa che non è menzionato da nessuna parte è che alcuni software usano effettivamente sand-boxing con container Linux. Ad esempio, Chrome non funzionerà mai normalmente se non si utilizza il flag appropriato --privilegedquando si esegue il contenitore.


6

Sono in ritardo alla festa, ma per gli utenti Mac che non vogliono percorrere il percorso XQuartz, ecco un esempio funzionante che crea un'immagine Fedora, con un ambiente desktop (xfce) che utilizza Xvfbe VNC. È semplice e funziona:

Su un Mac, puoi semplicemente accedervi utilizzando l'applicazione Screen Sharing (impostazione predefinita), connettendoti a localhost:5901.

Dockerfile:

FROM fedora

USER root

# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd

# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false  \
 && dnf install -y --setopt=deltarpm=false \
                openssl.x86_64             \
                java-1.8.0-openjdk.x86_64  \
                xorg-x11-server-Xvfb       \
                x11vnc                     \
                firefox                    \
                @xfce-desktop-environment  \
 && dnf clean all

# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer

# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh

# Expose VNC, SSH
EXPOSE 5901 22

# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV  DISPLAY :1.0
RUN  mkdir ~/.x11vnc
RUN  x11vnc -storepasswd letmein ~/.x11vnc/passwd

WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]

start-vnc.sh

#!/bin/sh

Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &

bash
# while true; do sleep 1000; done

Controllare il file Leggimi collegato per i comandi di generazione ed esecuzione se si desidera / è necessario.


5

Sulla base della risposta di Jürgen Weigert , ho alcuni miglioramenti:

docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes

L'unica differenza è che crea una directory $ XAUTH_DIR che viene utilizzata per posizionare il file $ XAUTH e montare la directory $ XAUTH_DIR invece del file $ XAUTH nel contenitore della finestra mobile.

Il vantaggio di questo metodo è che puoi scrivere un comando in /etc/rc.local che è quello di creare una cartella vuota denominata $ XAUTH_DIR in / tmp e cambiare la sua modalità in 777.

tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local

Al riavvio del sistema, prima dell'accesso dell'utente, la finestra mobile monterà automaticamente la directory $ XAUTH_DIR se il criterio di riavvio del contenitore è "sempre". Dopo l'accesso dell'utente, è possibile scrivere un comando in ~ / .profile per creare il file $ XAUTH, quindi il contenitore utilizzerà automaticamente questo file $ XAUTH.

tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile

Dopotutto, il contenitore otterrà automaticamente il file Xauthority ogni volta che il sistema si riavvia e accede all'utente.


4

Le altre soluzioni dovrebbero funzionare, ma ecco una soluzione per docker-compose.

Per correggere l'errore, è necessario passare $ DISPLAY e .X11-unix alla finestra mobile, nonché concedere all'utente che ha avviato la finestra mobile l'accesso a xhost.

All'interno del docker-compose.ymlfile:

version: '2'
services:
    node:
        build: .
        container_name: node
        environment:
            - DISPLAY
        volumes:
            - /tmp/.X11-unix:/tmp/.X11-unix

Nel terminale o nello script:

  • xhost +si:localuser:$USER
  • xhost +local:docker
  • export DISPLAY=$DISPLAY
  • docker-compose up


3

Puoi consentire all'utente Docker (qui: root) di accedere al display X11:

XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root 
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image 
xhost -SI:localuser:root

2

OSX (10.13.6, alta sierra)

Simile alla risposta di @Nick , ma la sua soluzione non ha funzionato per me.

Prima installa socat facendo brew install socat, e installa XQuartz ( https://www.xquartz.org/ )

Quindi segui questi passaggi qui ( http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ ) nella sezione commenti:

1. in one mac terminal i started:

socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"

2. and in another mac terminal I ran:

docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox

Sono stato anche in grado di avviare CLion dal mio contenitore docker debian.


1

Docker con rete BRIDGE. per Ubuntu 16.04 con display manager lightdm:

cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf

[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp

puoi usare più permessi privati

xhost +

docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name

1

Ancora un'altra risposta nel caso in cui tu abbia già creato l'immagine:

  1. invoca finestra mobile senza sudo ( Come risolvere la finestra mobile: problema di autorizzazione negata )

  2. condividi lo stesso USER e home & passwd tra host e container share (suggerimenti: usa l'id utente invece del nome utente)

  3. la cartella dev per far funzionare bene le librerie dipendenti dal driver

  4. più X11 in avanti.

    docker run --name=CONTAINER_NAME --network=host --privileged \
      -v /dev:/dev \
      -v `echo ~`:/home/${USER} \
      -p 8080:80 \
      --user=`id -u ${USER}` \
      --env="DISPLAY" \
      --volume="/etc/group:/etc/group:ro" \
      --volume="/etc/passwd:/etc/passwd:ro" \
      --volume="/etc/shadow:/etc/shadow:ro" \
      --volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
      --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
      -it REPO:TAG /bin/bash

potresti chiedere, qual è il punto di usare la finestra mobile se così tante cose sono uguali? bene, uno dei motivi a cui riesco a pensare è quello di superare l'inferno della dipendenza dal pacchetto ( https://en.wikipedia.org/wiki/Dependency_hell ).

Quindi questo tipo di utilizzo è più adatto allo sviluppatore, credo.


Questo è l'unico che avrebbe funzionato per me. Per i miei scopi, sono stato in grado di minimizzarlo in questo modo: docker run --network = host --volume = echo ~: / home / $ {USER} --user = id -u ${USER}--env = "DISPLAY" --volume = "/ etc / passwd: / etc / passwd: ro "-it REPO: TAG / bin / bash
user1145922

1

Sono riuscito a gestire un flusso video da una videocamera USB utilizzando opencvnel dockerseguendo questi passaggi:

  1. Consenti alla finestra mobile di accedere al server X.

    xhost +local:docker
    
  2. Crea il socket Unix X11 e il file di autenticazione X.

    XSOCK=/tmp/.X11-unix
    XAUTH=/tmp/.docker.xauth
    
  3. Aggiungi le autorizzazioni appropriate

    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
    
  4. Impostare la velocità di rendering Qt su "nativo", in modo da non bypassare il motore di rendering X11

    export QT_GRAPHICSSYSTEM=native
    
  5. Di 'a Qt di non usare MIT-SHM (memoria condivisa) - in questo modo dovrebbe essere anche più sicuro dal punto di vista della sicurezza

    export QT_X11_NO_MITSHM=1
    
  6. Aggiorna il comando Esegui finestra mobile

    docker run -it \
               -e DISPLAY=$DISPLAY \
               -e XAUTHORITY=$XAUTH \
               -v $XSOCK:$XSOCK \
               -v $XAUTH:$XAUTH \
               --runtime=nvidia \
               --device=/dev/video0:/dev/video0 \
               nvcr.io/nvidia/pytorch:19.10-py3
    

Nota: al termine del progetto, riportare i controlli di accesso al loro valore predefinito - xhost -local:docker

Maggiori dettagli: utilizzo della GUI con Docker

Credit: in tempo reale e di elaborazione video rilevamento di oggetti utilizzando tensorflow, OpenCV e Docker


"Crea il socket Unix X11 e il file di autenticazione X" non crea alcun file, definisce semplicemente le variabili?
MrR,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.