如何在Linux Docker容器中运行GUI应用程序?

是否有任何图像设置vncserver或其他东西,以便您可以-例如-在Firefox周围添加额外的加速沙箱?


当前回答

Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'

Xauthority文件的编写方式可以使主机名无关紧要。 我们需要将认证家族设置为“FamilyWild”。我不确定xauth是否有合适的命令行,所以这里有一个结合xauth和sed的示例。我们需要改变nlist输出的前16位。FamilyWild的值为65535或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

其他回答

这不是轻量级的,但是一个很好的解决方案,使docker特性与完整的桌面虚拟化相同。Ubuntu和CentOS的Xfce4或IceWM都可以工作,noVNC选项可以通过浏览器轻松访问。

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

它运行noVNC以及tigerVNC的vncserver。然后它为给定的窗口管理器调用startx。此外,libss_wrapper . dll。用于模拟用户的密码管理。

共享主机显示:0,正如在其他一些回答中所述,有两个缺点:

由于某些X安全漏洞,它打破了容器隔离。例如,可以使用xev或xinput进行键盘记录,使用xdotool远程控制主机应用程序。 由于X扩展MIT-SHM缺少共享内存,应用程序可能会出现呈现故障和糟糕的RAM访问错误。(也可以通过隔离降级选项——ipc=host进行修复)。

下面是一个在Xephyr中运行docker映像的示例脚本,可以解决这个问题。

当docker应用程序运行在嵌套的X服务器上时,它避免了X安全泄漏。 MIT-SHM关闭,避免RAM访问失败。 容器安全性通过——cap-drop ALL——security-opt no-new-privileges得到改进。容器用户也不是root用户。 创建一个X cookie来限制对Xephyr显示的访问。

脚本需要一些参数,第一个是在Xephyr中运行的主机窗口管理器,第二个是docker映像,第三个是可选的 要执行的映像命令。 要在docker中运行桌面环境,请使用“:”而不是主机窗口管理器。

关闭Xephyr窗口将终止docker容器应用程序。终止停靠的应用程序关闭Xephyr窗口。

例子:

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

xephyrdocker script:

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

这个脚本在x11docker wiki上维护。 更高级的脚本是x11docker,它还支持GPU加速、网络摄像头和打印机共享等功能。

如果你想无头运行GUI应用程序,请阅读这里。您需要做的是使用xvfb或其他类似软件创建一个虚拟监视器。如果您想在浏览器上运行Selenium测试,这是非常有用的。

任何地方都没有提到的是,一些软件实际上本身就在Linux容器中使用沙盒。例如,如果你在运行容器时没有使用适当的特权标志,Chrome将永远不会正常运行。

Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'

Xauthority文件的编写方式可以使主机名无关紧要。 我们需要将认证家族设置为“FamilyWild”。我不确定xauth是否有合适的命令行,所以这里有一个结合xauth和sed的示例。我们需要改变nlist输出的前16位。FamilyWild的值为65535或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

这里有一个轻量级的解决方案,可以避免在容器上安装任何X服务器,vnc服务器或sshd守护进程。它在简单中得到的好处,却在安全和隔离中失去了。

它假设您使用带有X11转发的ssh连接到主机。

在主机的sshd配置中添加该行

X11UseLocalhost no

因此,主机上转发的X服务器端口在所有接口上都是开放的(不仅仅是lo),特别是在Docker虚拟接口docker0上。

容器在运行时需要访问. xauthority文件,以便连接到服务器。为此,我们定义了一个只读卷,指向主机上的主目录(可能不是一个明智的主意!),并相应地设置XAUTHORITY变量。

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

这还不够,我们还必须从主机传递DISPLAY变量,但是用ip代替主机名:

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

我们可以定义一个别名:

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

然后像这样测试:

dockerX11run centos xeyes