如果您设定
X11UseLocalhost = no
,那么您允许甚至是
外部流量到达X11 socket。也就是说,指向该主机的外部IP的流量可以到达SSHD X11转发。尽管还有两个安全机制可能适用(防火墙、X11认证),但如果您正在处理用户或甚至是应用程序特定问题的情况下,我更喜欢保留
系统全局设置而不进行调整。
以下是一种替代方法,可以在不更改sshd配置中的
X11UseLocalhost
的情况下从容器获取X11图形,并通过X11转发将其从服务器传输到客户端。
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------- veth123@if5 --|-- eth0@if6 |
| (bridge) (veth pair) | (veth pair) |
| | |
| 127.0.0.1 +-------------------------+
routing +- lo
| (loopback)
|
| 192.168.1.2
+- ens33
(physical host interface)
使用默认的
X11UseLocalhost yes
,sshd仅在根网络命名空间上的
127.0.0.1
监听。我们需要将来自docker网络命名空间内的X11流量传递到根网络命名空间中的环回接口。veth对连接到
docker0
桥,因此双端都可以直接与172.17.0.1通信,无需进行路由。根网络命名空间中的三个接口(
docker0
、
lo
和
ens33
)可以通过路由相互通信。
我们想要实现以下目标:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo >--ssh x11 fwd-+
(loopback) |
v
192.168.1.2 |
<-- ssh -- ens33 ------<-----+
(physical host interface)
我们可以让X11应用程序直接与
172.17.0.1
通信,以“逃脱”Docker网络命名空间。这可以通过适当设置
DISPLAY
实现:
export DISPLAY=172.17.0.1:10
。
+ docker container net ns+
| |
172.17.0.1 | 172.17.0.2 |
docker0 --------- veth123@if5 --|-- eth0@if6 -----< xeyes |
(bridge) (veth pair) | (veth pair) |
| |
127.0.0.1 +-------------------------+
lo
(loopback)
192.168.1.2
ens33
(physical host interface)
现在,我们在主机上添加一个iptables规则,将来自根网络命名空间中的172.17.0.1路由到127.0.0.1:
iptables \
--table nat \
--insert PREROUTING \
--proto tcp \
--destination 172.17.0.1 \
--dport 6010 \
--jump DNAT \
--to-destination 127.0.0.1:6010
sysctl net.ipv4.conf.docker0.route_localnet=1
注意我们使用的端口是
6010
,这是SSHD执行X11转发的默认端口:它使用显示器编号10,该编号加到了“基础”端口6000上。在建立SSH连接后,您可以通过检查由SSH启动的shell中的
DISPLAY
环境变量来检查要使用的显示器编号。
也许您可以通过仅路由容器(veth端)中的流量来改进转发规则。另外,我不太确定为什么需要使用
route_localnet
,说实话。因为
127/8
是一个奇怪的数据包源/目的地,默认情况下被禁用路由。您可能还可以将docker net ns内的环回接口的流量重新路由到veth对,再从那里路由到root net ns中的环回接口。
使用上述命令,我们最终得到:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo
(loopback)
192.168.1.2
ens33
(physical host interface)
当您启用X11转发时,剩余的连接由SSHD建立。请注意,在容器内尝试启动X11应用程序之前,必须先建立连接,因为该应用程序会立即尝试访问X11服务器。
还有一件事情需要注意:身份验证。我们现在正在尝试作为容器内的172.17.0.1:10
访问X11服务器。然而,容器中没有任何X11身份验证,或者如果您绑定挂载主目录(在容器外通常是<hostname>:10
)则不正确。使用Ruben的建议添加一个在docker容器内可见的新条目:
xauth add 172.17.0.1:10 . <cookie>
其中<cookie>
是由SSH X11转发设置的cookie,例如通过xauth list
命令设置。
您可能还需要在防火墙中允许进入到172.17.0.1:6010
的流量。
您还可以从主机内部启动一个应用程序,该程序将运行在Docker容器网络名称空间中:
sudo nsenter --target=<pid of process in container> --net su - $USER <app>
如果没有使用su
命令,你将会以root用户身份运行。当然,你也可以使用另一个容器并共享网络名称空间:
sudo docker run --network=container:<other container name/id> ...
如上所示,X11转发机制适用于整个网络命名空间(实际上是适用于连接到docker0
桥接器的所有内容)。因此,它将适用于容器网络命名空间中的任何应用程序。
--net host
,但仍然出现以下错误:X11: Failed to open display localhost:11.0
。 - agirault