在没有"--net host"的SSH连接的服务器上可靠地在Docker容器中运行X应用程序

Rub*_*ben 19 x11 ssh x11-forwarding docker

如果没有Docker容器,可以直接使用SSH X11转发(ssh -X)在远程服务器上运行X11程序.当应用程序在服务器上的Docker容器内运行时,我试图让同样的东西工作.当使用-X选项SSH连接到服务器时,会设置X11隧道,并且环境变量"$ DISPLAY"会自动设置为"localhost:10.0"或类似内容.如果我只是尝试在Docker中运行X应用程序,我会收到此错误:

Error: GDK_BACKEND does not match available displays
Run Code Online (Sandbox Code Playgroud)

我的第一个想法是实际将$ DISPLAY传递给容器,其中包含"-e"选项,如下所示:

docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image
Run Code Online (Sandbox Code Playgroud)

这有帮助,但它没有解决问题.错误消息更改为:

Unable to init server: Broadway display type not supported: localhost:10.0
Error: cannot open display: localhost:10.0
Run Code Online (Sandbox Code Playgroud)

在搜索网络后,我发现我可以做一些xauth魔术来修复身份验证.我添加了以下内容:

SOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \ 
  -e XAUTHORITY=$XAUTH name_of_docker_image
Run Code Online (Sandbox Code Playgroud)

但是,这仅在将" --net host " 添加到docker命令时才有效:

docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \ 
  -e XAUTHORITY=$XAUTH --net host name_of_docker_image
Run Code Online (Sandbox Code Playgroud)

这是不可取的,因为它使整个主机网络对容器可见.

为了让它完全在没有"--net host"的docker中的远程服务器上运行,现在缺少什么?

Rub*_*ben 29

我想到了.当您使用SSH连接到计算机并使用X11转发时,/ tmp/.X11-unix不用于X通信,并且不需要与$ XSOCK相关的部分.

任何X应用程序都使用$ DISPLAY中的主机名,通常是"localhost"并使用TCP连接.然后将其隧道传送回SSH客户端.当Docker使用"--net host"时,Docker容器的"localhost"与Docker主机相同,因此它可以正常工作.

如果未指定"--net host",则Docker将使用默认的网桥模式.这意味着"localhost"意味着容器内部的其他内容与主机相同,容器内的X应用程序将无法通过引用"localhost"来查看X服务器.因此,为了解决这个问题,必须将"localhost"替换为主机的实际IP地址.这通常是"172.17.0.1"或类似的.检查"docker0"接口的"ip addr".

这可以通过sed替换来完成:

DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1\1/'`
Run Code Online (Sandbox Code Playgroud)

此外,SSH服务器通常未配置为接受与此X11隧道的远程连接.然后必须通过编辑/ etc/ssh/sshd_config(至少在Debian中)并设置来更改它:

X11UseLocalhost no
Run Code Online (Sandbox Code Playgroud)

然后重新启动SSH服务器,并使用"ssh -X"重新登录服务器.

这几乎是这样,但还有一个复杂因素.如果Docker主机上正在运行任何防火墙,则必须打开与X11隧道关联的TCP端口.端口号是:和之间的数字.在$ DISPLAY中添加到6000.

要获取TCP端口号,您可以运行:

X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*/\1/'`
TCPPORT=`expr 6000 + $X11PORT`
Run Code Online (Sandbox Code Playgroud)

然后(如果使用ufw作为防火墙),为172.17.0.0子网中的Docker容器打开此端口:

ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp
Run Code Online (Sandbox Code Playgroud)

所有命令可以放在一个脚本中:

XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | sudo xauth -f $XAUTH nmerge -
sudo chmod 777 $XAUTH
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*/\1/'`
TCPPORT=`expr 6000 + $X11PORT`
sudo ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp 
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1\1/'`
sudo docker run -ti --rm -e DISPLAY=$DISPLAY -v $XAUTH:$XAUTH \
   -e XAUTHORITY=$XAUTH name_of_docker_image
Run Code Online (Sandbox Code Playgroud)

假设你不是root用户,因此需要使用sudo.

而不是sudo chmod 777 $XAUTH,你可以运行:

sudo chown my_docker_container_user $XAUTH
sudo chmod 600 $XAUTH
Run Code Online (Sandbox Code Playgroud)

防止服务器上的其他用户如果知道您为/tmp/.docker.auth文件创建了什么,也能够访问X服务器.

我希望这可以使它适用于大多数情况.

  • 代替使用“ xauth nlist”的神秘行,还可以使用更易于理解的命令:xauth -f /tmp/.docker.xauth add 172.17.0.1:$X11PORT。$ MAGIC_COOKIE,在其中可以找到$ MAGIC_COOKIE:xauth list $ DISPLAY | awk'{print $ 3}' (2认同)