如果没有Docker容器,可以直接使用SSH X11转发在远程服务器上运行X11程序(ssh -X).我曾尝试过在服务器上的Docker容器中运行应用程序时也能实现同样的效果.当使用-X选项对服务器执行SSH时,X11隧道被设置,环境变量“$DISPLAY”被自动设置为“localhost:10.0”或类似的值。如果我只是尝试在Docker中运行X应用程序,我会收到以下错误:
Error: GDK_BACKEND does not match available displays
我的第一个想法是使用“-e”选项将$DISPLAY传递到容器中,如下所示:
docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image
这会有所帮助,但不能解决问题。错误消息将更改为:
Unable to init server: Broadway display type not supported: localhost:10.0
Error: cannot open display: localhost:10.0
在网上搜索之后,我发现我可以做一些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
但是,只有在docker命令中添加“--net host”时,这才有效:
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH --net host name_of_docker_image
这是不希望的,因为它使得整个主机网络对于容器可见。
为了让它完全运行在Docker中的远程服务器上而不使用“--net host”,现在缺少了什么?
4条答案
按热度按时间mitkmikd1#
我想明白了,当你用SSH连接电脑,使用X11转发的时候,X通信不使用**/tmp/. X11-unix**,$XSOCK相关的部分是不需要的。
任何X应用程序都使用$DISPLAY中的主机名,通常是“localhost”,并使用TCP连接。然后通过隧道返回到SSH客户端。当使用“--net host”作为Docker时,“localhost”对于Docker容器和Docker主机是相同的,因此它可以正常工作。
当不指定“--net host”时,Docker使用默认的网桥网络模式。这意味着“localhost”在容器内部表示除主机之外的其他东西,容器内部的X应用程序将无法通过引用“localhost”来看到X服务器。因此,为了解决这个问题,必须将“localhost”替换为主机的实际IP地址。这通常是“172.17.0.1“或类似地址。检查“docker 0”接口的“ip addr”。
这可以通过sed替换来完成:
另外,SSH服务器通常不接受远程连接到X11隧道。这必须通过编辑**/etc/ssh/sshd_config**(至少在Debian中)和设置来改变:
然后重新启动SSH服务器,使用“ssh-X”重新登录服务器。
这就差不多了,但还有一个复杂问题。如果Docker主机上运行任何防火墙,则必须打开与X11隧道关联的TCP端口。端口号是$DISPLAY中的**:和.**之间的数字加上6000。
要获取TCP端口号,可以运行:
然后(如果使用 ufw 作为防火墙),为www.example.com子网中的Docker容器打开此端口172.17.0.0:
所有命令都可以放在一个脚本中:
假设您不是root用户,因此需要使用sudo。
除了
sudo chmod 777 $XAUTH
,您还可以运行:以防止服务器上的其他用户也能够访问X服务器(如果他们知道您创建/tmp/.docker.auth文件的目的)。
我希望这能使它在大多数情况下正常工作。
6ss1mwsb2#
如果您设置
X11UseLocalhost = no
,您甚至允许外部流量到达X11套接字。也就是说,定向到机器外部IP的流量可以到达SSHD X11转发。仍然有两种安全机制 * 可能 * 适用(防火墙,X11 auth)。不过,如果你在处理一个特定于用户甚至应用程序的问题,我宁愿不去处理 * 系统全局设置 *,就像这个例子一样。这里有一个替代方法,可以从容器中获取X11图形,并通过X11将其从服务器转发到客户机,而无需更改sshd配置中的
X11UseLocalhost
。使用默认的
X11UseLocalhost yes
,sshd只监听根网络名称空间上的127.0.0.1
。我们需要从Docker网络名称空间内部获取到根网络ns中的环回接口的X11流量。veth对连接到docker0
网桥,因此两端都可以与www.example.com通信,而无需任何路由。根网络ns中的三个接口172.17.0.1 without any routing. The three interfaces in the root net ns (docker0
,lo
andens33
) can communicate via routing.我们希望实现以下目标:
我们可以让X11应用程序直接与
172.17.0.1
对话以"避开" Docker net ns。这可以通过适当地设置DISPLAY
来实现:export DISPLAY=172.17.0.1:10
:现在,我们在主机上添加一个iptables规则,以便从www.example.com路由到根网络ns中的www.example.com: 172.17.0.1 to 127.0.0.1 in the root net ns:
注意,我们使用的是端口
6010
,这是SSHD执行X11转发的默认端口:它使用的是显示编号10,该编号已添加到端口"base" 6000。在建立SSH连接后,您可以通过检查SSH启动的shell中的DISPLAY
环境变量来检查要使用的显示编号。也许您可以通过只路由来自此容器的流量来改进转发规则(veth end)。而且,老实说,我不太确定为什么需要
route_localnet
。看起来127/8
是一个奇怪的数据包源/目的地,因此默认情况下禁用路由。您可能还可以将流量从Docker Net ns内部的环回接口重路由到veth对,并且从那里到根网络NS中的环回接口。使用上面给出的命令,我们最终得到:
当您使用X11转发建立连接时,SSHD会建立剩余的连接。请注意,您必须在尝试启动容器内的X11应用程序 * 之前 * 建立连接,因为该应用程序会立即尝试连接到X11服务器。
少了一块:我们现在尝试在容器内以
172.17.0.1:10
的身份访问X11服务器。但是容器没有任何X11身份验证,或者如果你绑定挂载主目录(容器外的主目录通常是<hostname>:10
),则没有正确的X11身份验证。按照Ruben的建议添加一个新条目,使其在docker容器内可见:其中
<cookie>
是由SSH X11转发设置的cookie,例如经由xauth list
。您可能还需要在防火墙中允许流量进入
172.17.0.1:6010
。您还可以从Docker容器网络名称空间内的主机启动应用程序:
如果没有
su
,您将以root用户身份运行。当然,您也可以使用另一个容器并共享网络名称空间:上面显示的X11转发机制适用于整个网络名称空间(实际上,适用于连接到
docker0
桥的所有东西),因此,它适用于容器网络名称空间内的任何应用程序。qoefvg9y3#
在我的例子中,我位于“remote”并连接到“docker_host”上的“docker_container”:
远程--〉停靠站主机--〉停靠站容器
为了使使用VScode调试脚本更容易,我将SSHD安装到“docker_container”中,报告端口22,该端口Map到“docker_host”上的另一个端口(例如1234)。
所以我可以通过ssh直接连接到运行的容器(从“远程”):
ssh -Y -p 1234 appuser@docker_host.local
(其中
appuser
是“docker_container”内的用户名。我现在正在我的本地子网上工作,所以我可以通过.localMap引用我的服务器。对于外部IP,只要确保您的路由器Map到这个端口到这台机器。)这将通过ssh创建一个从我的“remote”到“docker_container”的直接连接。
远程--〉(ssh)--〉停靠器容器
在“docker_container”内部,我用
sudo apt-get install openssh-server
安装了sshd
(您可以将此添加到您的Dockerfile中,以便在构建时安装)。要允许X11转发工作,请按如下方式编辑
/etc/ssh/sshd_config
文件:然后在容器中重新启动ssh,你应该在容器中执行shell,从“docker_host”开始,而不是在你通过ssh连接到“docker_container”的时候:(x 1米5英寸1 x)
重新启动sshd:
sudo service ssh restart
当您通过ssh连接到“docker_container”时,请检查
$DISPLAY
环境变量。通过ssh(如cv2.imshow())从“docker_container”中执行您最喜欢的X11图形程序来进行测试
zrfyljdw4#
我使用了一种自动化的方法,可以完全从Docker容器中执行。
所有需要做的就是将DISPLAY变量传递给容器,并挂载
.Xauthority
。此外,它只使用DISPLAY变量的端口,因此它也适用于DISPLAY=localhost:XY.Z
。创建包含以下内容的文件
source-me.sh
:创建以下Dockerfile进行测试:
构建和运行:
在容器内运行:
要以非交互方式运行,必须确保
source-me.sh
的来源为: