¿Se pueden ejecutar aplicaciones GUI en un contenedor Docker?

409

¿Cómo puede ejecutar aplicaciones GUI en un contenedor Docker ?

¿Hay alguna imagen que se configure vncservero algo así para que pueda, por ejemplo, agregar un entorno limitado adicional de speedbump, por ejemplo, Firefox?

Será
fuente
Esta pregunta parece pertenecer solo a Linux (según la edad y el contenido de las respuestas) y no a Windows. Si es así, ¿podemos editar el título para aclarar esto? Gracias
UuDdLrLrSs
Consulte la Guía del usuario de HPC Visualization Containers para obtener algunas ideas.
kenorb

Respuestas:

238

Simplemente puede instalar un servidor vncs junto con Firefox :)

Empujé una imagen, vnc / firefox, aquí: docker pull creack/firefox-vnc

La imagen se ha hecho con este 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'

Esto creará un contenedor Docker que ejecute VNC con la contraseña 1234:

Para Docker versión 18 o posterior:

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

Para Docker versión 1.3 o posterior:

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

Para Docker antes de la versión 1.3:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create
crujir
fuente
2
¿Cómo usaría un cliente VNC para ver esto de forma remota? Escribir en el puerto IP + no parece estar funcionando.
user94154
17
Primero, debe verificar el puerto asignado (haciendo docker inspect <container id>o simplemente docker ps, luego se conecta a la IP de su host con el puerto que acaba de encontrar.
creack
99
la imagen creackfirefox-vnc falla con un error: Ingrese la contraseña de VNC: stty: entrada estándar: ioctl inapropiado para dispositivos fgets: No existe dicho archivo o directorio stty: entrada estándar: ioctl inapropiado para dispositivo x11vnc -usepw: no se pudo encontrar una contraseña para usar.
alfonsodev
66
Use docker well> Ejecutar aplicaciones GUI con Docker fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker
Dennis C
77
No hay nombre de usuario, la contraseña se indica claramente en la respuesta y cualquier cliente vnc lo hará. En mi caso, me gusta el osx nativo. (desde el buscador, presione el comando + K y conéctese a vnc: // <docker ip>: <puerto expuesto del contenedor>)
creack
195

Xauthority se convierte en un problema con los sistemas más nuevos. Puedo descartar cualquier protección con xhost + antes de ejecutar mis contenedores acoplables, o puedo pasar un archivo Xauthority bien preparado. Los archivos típicos de Xauthority son específicos del nombre de host. Con docker, cada contenedor puede tener un nombre de host diferente (configurado con docker run -h), pero incluso configurar el nombre de host del contenedor idéntico al sistema host no ayudó en mi caso. xeyes (me gusta este ejemplo) simplemente ignoraría la cookie mágica y no pasaría credenciales al servidor. Por lo tanto, recibimos un mensaje de error 'No se ha especificado el protocolo No se puede abrir la pantalla'

El archivo Xauthority se puede escribir de manera que el nombre de host no importe. Necesitamos establecer la familia de autenticación en 'FamilyWild'. No estoy seguro, si xauth tiene una línea de comando adecuada para esto, entonces aquí hay un ejemplo que combina xauth y sed para hacerlo. Necesitamos cambiar los primeros 16 bits de la salida nlist. El valor de FamilyWild es 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
Jürgen Weigert
fuente
8
Solo una nota, -v $XSOCK:$XSOCK -v $XAUTH:$XAUTHse puede acortar a-v $XSOCK -v $XAUTH
Piotr Aleksander Chmielowski el
2
@PiotrAleksanderChmielowski que no funcionó para mí, Docker versión 1.12.0, compilación 8eab29e
tbc0
14
@Dirk: es posible que desee reemplazar :0con $DISPLAY. Eso significa xauth nlist $DISPLAY | ...y docker run -ti -e DISPLAY=$DISPLAY .... Por lo general, la PANTALLA X es :0, pero no siempre (y especialmente no si se conecta a través de ssh -X).
johndodo
44
En Ubuntu 16.04 xauth crea el /tmp/.docker.xautharchivo con 600permisos. Esto da como resultado que xauth dentro del contenedor acoplable no pueda leer el archivo. Puede verificar ejecutando xauth listdentro del contenedor acoplable. He agregado chmod 755 $XAUTHdespués del xauth nlist :0 | ...comando para resolver esto.
Abai
2
@Abai ¿Por qué usar 755, si 444 o 644 es suficiente?
Daniel Alder
68

Acabo de encontrar esta entrada de blog y quiero compartirla aquí con ustedes porque creo que es la mejor manera de hacerlo y es muy fácil.

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

PROS:
+ no x cosas del servidor en el contenedor docker
+ no se necesita cliente / servidor vnc
+ no ssh con reenvío x
+ contenedores docker mucho más pequeños

CONTRAS:
- usar x en el host (no destinado para el sandboxing seguro)

en caso de que el enlace falle algún día, he puesto la parte más importante aquí:
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

construir la imagen:

docker build -t firefox .

y el comando de ejecución:

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

por supuesto, también puede hacer esto en el comando de ejecución con sh -c "echo script-here"

SUGERENCIA: para audio, eche un vistazo a: https://stackoverflow.com/a/28985715/2835523

A. Binzxxxxxx
fuente
¿Cómo puedo hacer esto en Windows 7? ¿Necesito instalar un servidor X?
Walkignison
3
Como la mayoría de las respuestas aquí, esto se aplica solo a Unix, creo, hasta que Windows sea compatible con el sistema de ventanas del servidor X.
A. Binzxxxxxx
¿Crees que podría funcionar si instalé el servidor X en Windows o incluso incluí un servidor X en mi contenedor Docker?
caminatas
1
Creo que también necesita instalar en Dockerfile apt-get -y install sudopara crear la /etc/sudoers.dcarpeta.
mulg0r
1
También puede ser necesario permitir conexiones a X desde cualquier host con$ xhost +
Bandoos
52

Con los volúmenes de datos de Docker es muy fácil exponer el zócalo de dominio Unix de xorg dentro del contenedor.

Por ejemplo, con un Dockerfile como este:

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

Podrías hacer lo siguiente:

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

Por supuesto, esto es esencialmente lo mismo que X-forwarding. Otorga al contenedor acceso completo al servidor x en el host, por lo que solo se recomienda si confía en lo que hay dentro.

Nota: Si le preocupa la seguridad, una mejor solución sería limitar la aplicación con un control de acceso obligatorio o basado en roles . Docker logra un aislamiento bastante bueno, pero fue diseñado con un propósito diferente en mente. Use AppArmor , SELinux o GrSecurity , que fueron diseñados para abordar su inquietud.

Aryeh Leib Taurog
fuente
55
También debe permitir el acceso al servidor X desde otros hosts utilizando una herramienta como xhost. Para abrirlo completamente, úselo xhost +en el host.
Tully
3
@Tully solo xhost +locales necesario. Sin embargo, sería mejor hacer que el ~/.Xauthorityarchivo esté disponible en el contenedor, para que pueda autenticarse.
Aryeh Leib Taurog
3
¿Has logrado que funcione en una Mac (usando boot2docker)?
Karl Forner el
44
Esto funcionaba bastante bien para mí en una computadora portátil Ubuntu 14.04 con docker 1.5 anteriormente; pero ahora me falla en Ubuntu 15.04, docker 1.6.2, con el error Can't open display: :0. ¿Algunas ideas?
cboettig
66
Solía xhost +si:localuser:$USERautorizar solo al usuario que iniciaba el contenedor.
Nick Breen
26

También puede usar el subusuario: https://github.com/timthelion/subuser

Esto le permite empaquetar muchas aplicaciones gui en docker. Firefox y emacs han sido probados hasta ahora. Sin embargo, con firefox, webGL no funciona. El cromo no funciona en absoluto.

EDITAR: ¡El sonido funciona!

EDIT2: En el tiempo desde que publiqué esto por primera vez, el subusuario ha progresado mucho. Ahora tengo un sitio web subuser.org , y un nuevo modelo de seguridad para conectarse a X11 a través del puente XPRA .

timthelion
fuente
3
Tenga en cuenta que el subusuario sigue siendo muy nuevo y relativamente no probado. Si tiene algún problema, envíe informes de errores.
timthelion
Evitaría X11 si hay alguna forma de hacerlo. Su aplicación asesina estaría ejecutando el proxy tor en la ventana acoplable, y ejecutando un navegador completo con complementos en una ventana acoplable secundaria, de modo que el firewall, etc., obligue a toda la red a salir a través de la ventana acoplable tor. Esto correría vueltas alrededor del paquete actual del navegador tor para la usabilidad web porque dejarías pasar contenido rico.
Será
1
¿Es el problema para usted con la seguridad X11? ¿O es que quieres que esto funcione con Windows? ¿O que quieres que esto funcione de forma remota? Todas las anteriores? Creo que hacer que esto funcione con vnc es bastante posible (aunque no lo convertiría en el método predeterminado porque agrega una dependencia de vnc). Hacer que el subusuario funcione de forma remota no es realmente posible / significativo. También hay esto: github.com/rogaha/docker-desktop, pero según los informes de errores, parece que xpra podría ser inutilizable en la vida real.
timthelion
24

OSX

Jürgen Weigert tiene la mejor respuesta que funcionó para mí en Ubuntu, sin embargo, en OSX, Docker se ejecuta dentro de VirtualBox y, por lo tanto, la solución no funciona sin más trabajo.

Lo tengo trabajando con estos ingredientes adicionales:

  1. Xquartz (OSX ya no se entrega con el servidor X11)
  2. reenvío de sockets con socat (brew install socat)
  3. script de bash para iniciar el contenedor

Agradecería los comentarios de los usuarios para mejorar esta respuesta para OSX, no estoy seguro si el reenvío de socket para X es seguro, pero mi uso previsto es ejecutar el contenedor de Docker solo localmente.

Además, el script es un poco frágil ya que no es fácil obtener la dirección IP de la máquina, ya que está en nuestra red inalámbrica local, por lo que siempre es una IP aleatoria.

El script BASH que uso para iniciar el contenedor:

#!/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

Puedo hacer que xeyes y matplotlib funcionen con este enfoque.

Windows 7+

Es un poco más fácil en Windows 7+ con MobaXterm:

  1. Instalar MobaXterm para Windows
  2. Inicie MobaXterm
  3. Configure X server: Settings -> X11 (tab) -> set X11 Remote Access to full
  4. Use este script BASH para iniciar el contenedor

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 corriendo en PC

Mella
fuente
No entendí lo que querías decir con el script bash: ¿cómo lo ejecuto en Windows?
deller
@deller Hago desarrollo de software en Windows usando GIT, así que tengo el shell GIT-bash disponible para mí.
Nick
Seguí los pasos. Sin embargo, entiendo error: XDG_RUNTIME_DIR not set in the environment.y Error: cannot open display: VAIO:0.0. ¿Encontró algo como esto?
user3275095
1
Recibo un error relacionado con el usuario que no se encuentra, es decir, "no hay entradas coincidentes en el archivo passwd" ¿Alguna pista?
Walkignison
19

Compartir la pantalla del host: 0, como se indica en algunas otras respuestas, tiene dos inconvenientes:

  • Rompe el aislamiento del contenedor debido a algunas fugas de seguridad X. Por ejemplo, el registro de teclas con xevo xinputes posible, y el control remoto de aplicaciones host con xdotool.
  • Las aplicaciones pueden tener fallas de renderizado y errores de acceso a RAM incorrectos debido a la falta de memoria compartida para la extensión X MIT-SHM. (También se puede arreglar con la opción de degradación del aislamiento --ipc=host).

Debajo de un script de ejemplo para ejecutar una imagen acoplable en Xephyr que soluciona estos problemas.

  • Evita fugas de seguridad de X a medida que las aplicaciones acoplables se ejecutan en un servidor X anidado.
  • MIT-SHM está deshabilitado para evitar fallas de acceso a RAM.
  • La seguridad del contenedor se mejora con --cap-drop ALL --security-opt no-new-privileges. Además, el usuario del contenedor no es root.
  • Se crea una cookie X para restringir el acceso a la pantalla Xephyr.

El script espera algunos argumentos, primero un administrador de ventanas de host para ejecutarse en Xephyr, segundo una imagen acoplable, opcionalmente tercero un comando de imagen que se ejecutará. Para ejecutar un entorno de escritorio en Docker, use ":" en lugar de un administrador de ventanas de host.

Al cerrar la ventana de Xephyr, se finalizan las aplicaciones del contenedor acoplable. La finalización de las aplicaciones acopladas cierra la ventana de Xephyr.

Ejemplos:

  • 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

Este script se mantiene en x11docker wiki . Un script más avanzado es x11docker que también admite funciones como aceleración de GPU, cámara web e impresora compartida, etc.

mviereck
fuente
18

Aquí hay una solución liviana que evita tener que instalar cualquier Xservidor, vncservidor o sshddemonio en el contenedor. Lo que gana en simplicidad lo pierde en seguridad y aislamiento.

Se supone que se conecta a la máquina host mediante sshla X11expedición.

En la sshdconfiguración del host, agregue la línea

X11UseLocalhost no

De manera que el puerto del servidor X transmitido en el host se abre en todas las interfaces (no sólo lo) y, en particular, sobre la interfaz virtual del estibador, docker0.

El contenedor, cuando se ejecuta, necesita acceso al .Xauthorityarchivo para poder conectarse al servidor. Para hacer eso, definimos un volumen de solo lectura que apunta al directorio de inicio en el host (¡tal vez no sea una buena idea!) Y también establecemos la XAUTHORITYvariable en consecuencia.

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

Eso no es suficiente, también tenemos que pasar la variable DISPLAY del host, pero sustituyendo el nombre del host por la ip:

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

Podemos definir un alias:

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

Y pruébalo así:

dockerX11run centos xeyes
danidiaz
fuente
2
(Esto es ideal para aplicaciones confiables. Sin embargo, para cualquier tipo de sandboxing, desea evitar el reenvío X.)
Será el
1
Si prefiere no montar el directorio principal entera en el contenedor sólo se puede montar el .Xauthorityarchivo en sí: -v $HOME/.Xauthority:/root/.Xauthority -e XAUTHORITY=/root/.Xauthority.
Robert Haines
2
En lugar de cambiar X11UseLocalhost, también puede usar la opción adicional --net=hostpara el docker runcomando (que se encuentra aquí ).
ingomueller.net
--net=hostes una mala idea que ahora si abres un puerto en el contenedor también estará abierto en el host ...
MrR
16

Si bien la respuesta de Jürgen Weigert cubre esencialmente esta solución, al principio no me quedó claro qué se estaba describiendo allí. Así que agregaré mi opinión al respecto, en caso de que alguien más necesite aclaraciones.

En primer lugar, la documentación pertinente es la página de manual de seguridad X .

Numerosas fuentes en línea sugieren simplemente montar el socket X11 unix y el ~/.Xauthorityarchivo en el contenedor. Estas soluciones a menudo funcionan por suerte, sin comprender realmente por qué, por ejemplo, el usuario del contenedor termina con el mismo UID que el usuario, por lo que no hay necesidad de autorización de clave mágica.

En primer lugar, el archivo Xauthority tiene el modo 0600, por lo que el usuario del contenedor no podrá leerlo a menos que tenga el mismo UID.

Incluso si copia el archivo en el contenedor y cambia la propiedad, todavía hay otro problema. Si ejecuta xauth listen el host y el contenedor, con el mismo Xauthorityarchivo, verá diferentes entradas en la lista. Esto se debe a que xauthfiltra las entradas según dónde se ejecute.

El cliente X en el contenedor (es decir, la aplicación GUI) se comportará igual que xauth. En otras palabras, no ve la cookie mágica para la sesión X que se ejecuta en el escritorio del usuario. En cambio, ve las entradas para todas las sesiones X "remotas" que ha abierto anteriormente (explicado a continuación).

Entonces, lo que debe hacer es agregar una nueva entrada con el nombre de host del contenedor y la misma clave hexadecimal que la cookie de host (es decir, la sesión X que se ejecuta en su escritorio), por ejemplo:

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

El problema es que la cookie debe agregarse xauth adddentro del contenedor:

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

De lo contrario, lo xauthetiqueta de una manera que solo se ve fuera del contenedor.

El formato para este comando es:

xauth add hostname/$DISPLAY protocol hexkey

Donde .representa el MIT-MAGIC-COOKIE-1protocolo.

Nota: No es necesario copiar o enlazar-montar .Xauthorityen el contenedor. Simplemente cree un archivo en blanco, como se muestra, y agregue la cookie.

La respuesta de Jürgen Weigert se soluciona utilizando el FamilyWildtipo de conexión para crear un nuevo archivo de autoridad en el host y copiarlo en el contenedor. Tenga en cuenta que primero extrae la clave hexadecimal para la sesión X actual del ~/.Xauthorityuso xauth nlist.

Entonces los pasos esenciales son:

  • Extraiga la clave hexadecimal de la cookie para la sesión X actual del usuario.
  • Cree un nuevo archivo Xauthority en el contenedor, con el nombre de host del contenedor y la clave hexadecimal compartida (o cree una cookie con el FamilyWildtipo de conexión).

Admito que no entiendo muy bien cómo FamilyWildfunciona, o cómo xautho los clientes X filtran las entradas del archivo Xauthority dependiendo de dónde se ejecutan. Información adicional sobre esto es bienvenida.

Si desea distribuir su aplicación Docker, necesitará un script de inicio para ejecutar el contenedor que obtenga la clave hexadecimal para la sesión X del usuario, y la importe al contenedor de una de las dos formas explicadas anteriormente.

También ayuda a comprender la mecánica del proceso de autorización:

  • Un cliente X (es decir, una aplicación GUI) que se ejecuta en el contenedor busca en el archivo Xauthority una entrada de cookie que coincida con el nombre de host del contenedor y el valor de $DISPLAY.
  • Si se encuentra una entrada coincidente, el cliente X la pasa con su solicitud de autorización al servidor X, a través del socket apropiado en el /tmp/.X11-unixdirectorio montado en el contenedor.

Nota: El socket X11 Unix todavía necesita ser montado en el contenedor, o el contenedor no tendrá ruta al servidor X. La mayoría de las distribuciones deshabilitan el acceso TCP al servidor X de forma predeterminada por razones de seguridad.

Para obtener información adicional y comprender mejor cómo funciona la relación cliente / servidor X, también es útil ver el caso de ejemplo de reenvío SSH X:

  • El servidor SSH que se ejecuta en una máquina remota emula su propio servidor X.
  • Establece el valor de $DISPLAYen la sesión SSH para apuntar a su propio servidor X.
  • Se utiliza xauthpara crear una nueva cookie para el host remoto y la agrega a los Xauthorityarchivos tanto para usuarios locales como remotos.
  • Cuando se inician las aplicaciones GUI, se comunican con el servidor X emulado de SSH.
  • El servidor SSH reenvía estos datos al cliente SSH en su escritorio local.
  • El cliente SSH local envía los datos a la sesión del servidor X que se ejecuta en su escritorio, como si el cliente SSH fuera realmente un cliente X (es decir, una aplicación GUI).
  • El servidor X utiliza los datos recibidos para representar la GUI en su escritorio.
  • Al comienzo de este intercambio, el cliente X remoto también envía una solicitud de autorización, utilizando la cookie que se acaba de crear. El servidor X local lo compara con su copia local.
orodbhen
fuente
12

Esto no es liviano, pero es una buena solución que le da paridad a las características de Docker con virtualización de escritorio completa. Tanto Xfce4 como IceWM para Ubuntu y CentOS funcionan, y la noVNCopción facilita el acceso a través de un navegador.

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

Se ejecuta noVNCtan bien como tigerVNCvncserver. Luego se llama startxdado Administrador de ventanas. Además, libnss_wrapper.sose utiliza para emular la administración de contraseñas para los usuarios.

con guiones
fuente
¿Alguien ha probado esto?
guilhermecgs 01 de
3
@guilhermecgs sí, y funciona bien. Desde entonces también probé xpraen Docker, que es X sin raíz. xpraEra la IMO más adecuada y es más eficiente que VNC.
guiones el
Para que quede claro ... ¿Puedo tener una experiencia de escritorio completa (GNOME, KDE) con esta imagen?
guilhermecgs 01 de
Solo probé el Xfce4 y IceWM (que está en ese repositorio). Por supuesto, la experiencia será limitada, por ejemplo, los dispositivos de montaje no se mostrarán en el escritorio (gvfs) a menos que pase --device /dev/...a la ventana acoplable y establezca los --capprivilegios necesarios . Eso anula el propósito de la contención, pero puede pasar a través de dispositivos. Creo que con algunos ajustes debería ser posible ejecutar GNOME / KDE bajo VNC. Ejecuté múltiples X en la ventana acoplable con tarjetas nvidia (sin VNC o Xpra), por lo que eso es factible.
guiones
No lo hemos intentado hasta ahora. El mayor desafío en esto sería traer un demonio D-Bus que funcione. La mayoría de los escritorios gnome o KDE los necesitarán. Que el proyecto ubuntu-desktop-lxde-vnc te ayude allí.
toschneck
11

La solución dada en http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ parece ser una forma fácil de iniciar aplicaciones GUI desde dentro de los contenedores (intenté con Firefox sobre ubuntu 14.04) pero descubrí que se requiere un pequeño cambio adicional a la solución publicada por el autor.

Específicamente, para ejecutar el contenedor, el autor ha mencionado:

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

Pero descubrí que (basado en un comentario particular en el mismo sitio) que dos opciones adicionales

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

y

    -net=host 

debe especificarse al ejecutar el contenedor para que firefox funcione correctamente:

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

He creado una imagen acoplable con la información en esa página y estos hallazgos adicionales: https://hub.docker.com/r/amanral/ubuntu-firefox/

Anshuman Manral
fuente
3
He descubierto que ni siquiera necesitas pasar el /tmp/.X11-unixzócalo. Simplemente funciona con montaje .Xauthorityy --net=host.
CMCDragonkai
2
Esta es en realidad la única solución que funciona en estos días. Usar /tmp/.X11-unixcomo volumen ya no funciona, ya que Docker rechaza silenciosamente los montajes de volumen de directorios fijos.
Christian Hujer
1
Creo que depende de qué distribución estés usando. Definitivamente, puede montar el zócalo X11 Unix en CentOS. También es importante entender lo que --network=hosthace. Le da a su contenedor acceso completo a la pila de red del host, lo que puede ser indeseable, dependiendo de lo que esté tratando de hacer. Si solo está jugando con la ejecución de GUI en contenedores en su escritorio, entonces no debería importar.
orodbhen
7

Hay otra solución de lord.garbage para ejecutar aplicaciones GUI en un contenedor sin usar el reenvío VNC, SSH y X11. Se menciona aquí también.

niutech
fuente
1
Esto es genial si la seguridad no es una preocupación. Si el objetivo de acoplar algo es aislarlo, lo mejor es evitar que X11 entre y salga del contenedor.
Se
7

Si desea ejecutar una aplicación GUI sin cabeza, lea aquí . Lo que debe hacer es crear un monitor virtual con xvfbu otro software similar. Esto es muy útil si desea ejecutar pruebas de selenio, por ejemplo, con navegadores.

Algo que no se menciona en ninguna parte es que algunos softwares en realidad usan sand-boxing con contenedores de Linux. Entonces, por ejemplo, Chrome nunca se ejecutará normalmente si no usa el indicador apropiado --privilegedal ejecutar el contenedor.

Pithikos
fuente
6

Llego tarde a la fiesta, pero para los usuarios de Mac que no quieren seguir el camino de XQuartz, aquí hay un ejemplo de trabajo que construye una imagen de Fedora, con un entorno de escritorio (xfce) usando Xvfby VNC. Es simple y funciona:

En una Mac, puede acceder a ella usando la aplicación Compartir pantalla (predeterminada), conectándose 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

Consulte el archivo Léame vinculado para ver los comandos de compilación y ejecución si lo desea / necesita.

Vince
fuente
5

Según la respuesta de Jürgen Weigert , tengo algunas mejoras:

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

La única diferencia es que crea un directorio $ XAUTH_DIR que se usa para colocar el archivo $ XAUTH y montar el directorio $ XAUTH_DIR en lugar del archivo $ XAUTH en el contenedor acoplable.

El beneficio de este método es que puede escribir un comando en /etc/rc.local que es crear una carpeta vacía llamada $ XAUTH_DIR en / tmp y cambiar su modo a 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

Cuando se reinicia el sistema, antes del inicio de sesión del usuario, Docker montará el directorio $ XAUTH_DIR automáticamente si la política de reinicio del contenedor es "siempre". Después del inicio de sesión del usuario, puede escribir un comando en ~ / .profile que es crear el archivo $ XAUTH, luego el contenedor usará automáticamente este archivo $ 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

Después de todo, el contenedor obtendrá automáticamente el archivo Xauthority cada vez que el sistema se reinicie y el usuario inicie sesión.

Allenyllee
fuente
4

Las otras soluciones deberían funcionar, pero aquí hay una solución para docker-compose.

Para corregir ese error, debe pasar $ DISPLAY y .X11-unix a Docker, así como otorgar al usuario que inició Docker acceso a xhost.

Dentro del docker-compose.ymlarchivo:

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

En terminal o script:

  • xhost +si:localuser:$USER
  • xhost +local:docker
  • export DISPLAY=$DISPLAY
  • docker-compose up
Vacío
fuente
3

Puede permitir que el usuario de Docker (aquí: root) acceda a la pantalla 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
wedesoft
fuente
2

OSX (10.13.6, sierra alta)

Similar a la respuesta de @Nick , pero su solución no funcionó para mí.

Primero instale socat haciendo brew install socate instale XQuartz ( https://www.xquartz.org/ )

Luego siguió estos pasos aquí ( http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ ) en la sección de comentarios:

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

También pude lanzar CLion desde mi contenedor de Debian Docker.

nommer
fuente
1

Docker con red BRIDGE. para 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

puedes usar más permisos privados

xhost +

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

Otra respuesta en caso de que ya hayas creado la imagen:

  1. invocar docker sin sudo ( Cómo solucionar el docker: se ha denegado el permiso )

  2. comparta el mismo USUARIO y home & passwd entre el host y el contenedor compartido (consejos: use la identificación de usuario en lugar del nombre de usuario)

  3. la carpeta de desarrollo para que las bibliotecas dependientes del controlador funcionen bien

  4. más X11 adelante.

    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

usted puede preguntar, ¿cuál es el punto de usar Docker si tantas cosas son iguales? bueno, una de las razones por las que puedo pensar es para superar el infierno de la dependencia del paquete ( https://en.wikipedia.org/wiki/Dependency_hell ).

Entonces, este tipo de uso es más adecuado para el desarrollador, creo.

TingQian LI
fuente
Este es el único que funcionaría para mí. Para mis propósitos, pude minimizarlo: 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

Me las arreglé para ejecutar una secuencia de vídeo desde una cámara USB utilizando opencven dockerlos siguientes pasos:

  1. Dejar que Docker acceda al servidor X

    xhost +local:docker
    
  2. Cree el socket X11 Unix y el archivo de autenticación X

    XSOCK=/tmp/.X11-unix
    XAUTH=/tmp/.docker.xauth
    
  3. Agregar permisos adecuados

    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
    
  4. Establezca la velocidad de representación de Qt en "nativa", para que no omita el motor de representación X11

    export QT_GRAPHICSSYSTEM=native
    
  5. Dígale a Qt que no use MIT-SHM (memoria compartida), de esa manera también debería ser más seguro en cuanto a seguridad

    export QT_X11_NO_MITSHM=1
    
  6. Actualice el comando de ejecución de Docker

    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: Cuando finalice el proyecto, devuelva los controles de acceso a su valor predeterminado: xhost -local:docker

Más detalles: Uso de GUI con Docker

Crédito: detección de objetos de procesamiento de video y en tiempo real utilizando Tensorflow, OpenCV y Docker

tsveti_iko
fuente
"Crear el socket X11 Unix y el archivo de autenticación X" ¿no crea ningún archivo, solo define variables?
MrR