[X11] Puoi eseguire le app GUI in un contenitore docker?


Answers

Xauthority diventa un problema con i nuovi sistemi. Posso scartare qualsiasi protezione con xhost + prima di eseguire i miei contenitori docker, oppure posso passare un file Xauthority ben preparato. I file Xauthority tipici sono specifici per hostname. Con la finestra mobile, ogni contenitore può avere un nome host diverso (impostato con docker run -h), ma nemmeno impostare il nome host del contenitore identico al sistema host non è stato di aiuto nel mio caso. xeyes (mi piace questo esempio) semplicemente ignorerebbe il magic cookie e non passerebbe alcuna credenziale al server. Quindi riceviamo un messaggio di errore 'Nessun protocollo specificato Impossibile aprire la visualizzazione'

Il file Xauthority può essere scritto in un modo tale che l'hostname non abbia importanza. Abbiamo bisogno di impostare la famiglia di autenticazione su "FamilyWild". Non sono sicuro, se xauth ha una riga di comando adeguata per questo, ecco un esempio che combina xauth e sed per farlo. Abbiamo bisogno di cambiare i primi 16 bit dell'output 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
Question

Come puoi eseguire le app GUI in un contenitore docker ?

Ci sono delle immagini che configurano vncserver o qualcosa che puoi, ad esempio, aggiungere una sandbox speedbump in più per dire Firefox?




Condivisione visualizzazione host: 0, come indicato in altre risposte, presenta due inconvenienti:

  • Rompe l'isolamento del contenitore a causa di alcune perdite di sicurezza X. Ad esempio, keylogging con xev o xinput è possibile.
  • Le applicazioni possono presentare errori di rendering e errori di accesso alla RAM dovuti alla mancanza di memoria condivisa per l'estensione X MIT-SHM.

Per aggirare le perdite di sicurezza di X e per evitare il problema del MIT-SHM, ho pubblicato x11docker su github . L'idea principale è quella di eseguire un secondo server X con i propri cookie di autenticazione e con MIT-SHM disabilitato. i contenitori docker ottengono l'accesso al nuovo server X e sono separati dal display dell'host: 0. Non ci sono dipendenze X all'interno dell'immagine dato che X / Xephyr è fornito dall'host.

Sotto uno script di esempio per eseguire un'immagine di finestra mobile in Xephyr. Si aspetta alcuni argomenti, prima un gestore di finestre host da eseguire in Xephyr, in secondo luogo un'immagine docker, opzionalmente un terzo comando immagine da eseguire. Per eseguire un ambiente desktop nella finestra mobile, utilizzare ":" anziché un gestore di finestre host. Sui sistemi senza password di root, modificare la variabile $Getroot

Xephyr viene avviato usando xinit. Un xinitrc personalizzato viene creato per creare un cookie, per impostare il layout di tastiera, per eseguire il gestore di finestre e per eseguire xterm con xtermrc per richiedere la password per eseguire la finestra mobile.

La chiusura della finestra di Xephyr termina le applicazioni del contenitore docker. La chiusura delle applicazioni dockered chiude la finestra di Xephyr.

annotazioni:

  • Il problema MIT-SHM può anche essere evitato con l'opzione --ipc=host , ma questo --ipc=host isolamento del contenitore e viene scoraggiato.
  • Il cookie deve essere modificato in "familiy wild" come descritto da @ Jürgen Weigert se viene utilizzato il socket X condiviso. Con X su TCP, questo non è necessario.
  • soluzioni simili sono possibili con Xpra, Xorg e Xvnc
  • Per X su tcp, scopri il daemon docker ip (principalmente 172.17.42.1), condividi DISPLAY = 172.17.42.1: 1, crea cookie per esso, non condividi NewXsocket e non utilizzare l'opzione X / Xephyr -nolisten tcp. Usando X su tcp, MIT-SHM è disabilitato automaticamente.

Esempi:

  • x11docker_example "openbox --sm-disable" x11docker/lxde pcmanfm
  • x11docker_example : x11docker/lxde

script x11docker_example:

#! /bin/bash
# x11docker_example : Example script to run docker GUI applications in Xephyr.
#                     Look at x11docker on github: https://github.com/mviereck/x11docker
#
# Usage:
#   x11docker_example WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run a desktop environment in docker, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"

# command to get root permissions to run docker
Getroot="su -c"
# Getroot="sudo su -c"      # Use this on systems without a root password like Ubuntu or Sparky

# define new display and its X socket.
Newdisplay=:1
# To make sure $Newdisplay is not already in use, check /tmp/.Xn-lock
[ -e "/tmp/.X1-lock" ] && echo "Error: Display :1 is already in use" >&2 && exit 1
NewXsocket=/tmp/.X11-unix/X1


# cache folder and files
Cachefolder=/tmp/x11docker_example
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xtermrc=$Cachefolder/xtermrc
Xinitrc=$Cachefolder/xinitrc
Dockerpidfile=$Cachefolder/docker.pid
Dockerlogfile=$Cachefolder/docker.log


# 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
Dockercommand="docker run --rm -e DISPLAY=$Newdisplay -e XAUTHORITY=/Xcookie -v $Xclientcookie:/Xcookie:ro -v $NewXsocket:$NewXsocket:ro $Dockerimage"  


# command to run X/Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given
# $Newdisplay                    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
# -nolisten tcp                  disable tcp connections for security reasons
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr $Newdisplay -auth $Xservercookie -nolisten tcp -extension MIT-SHM -retro"


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

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

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

  echo "# create new XAUTHORITY cookie file" 
  echo ":> $Xclientcookie"
  echo "xauth generate $Newdisplay . untrusted"
  echo "cp $Xclientcookie $Xservercookie"
  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 '"$Newdisplay | sed -e 's/^..../ffff/')" 
  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'

  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 "# prompt for password"
  echo "xterm -title x11docker_example -e '/bin/bash $Xtermrc'"

  echo "# wait for docker to finish"
  echo 'Dockerpid=$(cat '$Dockerpidfile') && {'
  echo '  while [ -n "$(pgrep docker | grep $Dockerpid)" ] ; do'
  echo '    sleep 1'
  echo '  done }'

  [ "$Windowmanager" = ":" ] || echo 'kill $Windowmanagerpid'
  echo 'kill $Tailpid'
} > $Xinitrc


# create xtermrc for a password prompt
{ echo "#! /bin/bash"
  echo "echo 'x11docker_example will start docker on display $Newdisplay with command:'"
  echo "echo $Getroot '$Dockercommand'"
  echo "echo 'Please type in your password to run docker:'"
  echo "$Getroot 'nohup $Dockercommand 2>&1 1>$Dockerlogfile & echo "'$!'" > $Dockerpidfile'"
} > $Xtermrc


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



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

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

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

CONS:
- utilizzando x sull'host (non inteso per la sicurezza sandboxing)

nel caso in cui il collegamento fallisca un giorno ho messo 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 di marcia:

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://.com/a/28985715/2835523




La soluzione fornita su fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker sembra essere un modo semplice per avviare le applicazioni GUI dall'interno dei contenitori (ho provato per Firefox su Ubuntu 14.04) ma ho trovato 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 trovato che (basato su 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 per il corretto funzionamento di firefox:

    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/




C'è un'altra soluzione di lord.garbage per eseguire le applicazioni GUI in un contenitore senza utilizzare l'inoltro VNC, SSH e X11. È menzionato anche here .




OSX

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

Ho lavorato con questi ingredienti aggiuntivi:

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

Apprezzerei i commenti degli utenti per migliorare questa risposta per OSX, non sono sicuro che il socket forwarding per X sia sicuro, ma il mio scopo è quello di eseguire localmente il container docker.

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 utilizzo 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 ottenere xeyes e matplotlib lavorando con questo approccio.

Windows 7+

È un po 'più facile su Windows 7+ con MobaXterm:

  1. Installa MobaXterm per Windows
  2. Avvia MobaXterm
  3. Configura il server X: Impostazioni -> X11 (scheda) -> imposta l'accesso remoto X11 a pieno
  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




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