Linux下的 XDMCP 服务的启动与使用
背景
最近收到一个新 BUG,用户使用 XManager 利用 XDMCP协议进入桌面环境后,直接退出XManager 程序时,会话管理器未自动退出,导致后续登录相关组件异常。
服务端:openEuler + KiranDE(X11, LightDM)
客户端:openEuler + KiranDE(X11, LightDM)
该问题已解决并提交PR,本文记录问题复现、定位、解决过程以及相关扩展知识。
基础知识
What is XDMCP?
XDMCP 即 The X Display Manager Control Protocol,X 显示管理器控制协议。它允许一台计算机(客户端,通常是 X Server)请求另一台计算机(远程服务器,运行 X Display Manager)提供一个图形化的登录会话。
简单来说,它的核心目的是能在一台机器的屏幕上登录并使用另一台机器的图形桌面环境。
如果我们想要实现多个用户同时使用图形桌面,便可以利用 XDMCP 服务来实现。从名字就可以看出来该协议肯定与显示管理器(DM, DisplayManager)有关。事实确实如此,XDMCP 服务是由显示管理器来提供的。我们想要使用该服务便需要从显示管理器的配置文件入手。
提醒:XDMCP 协议本身是不加密的,这意味着登录凭据和所有会话数据都将在网络上以明文传输。强烈不建议在不受信任的网络(如互联网)上使用 XDMCP。它主要适用于物理隔离或足够安全的局域网(LAN)。XDMCP 协议由于安全问题已经被其他新的协议(例如:SSH -X, VNC等)所替代,现在已经不推荐使用了。
使用 XDMCP 链接客户端与服务端
1. 服务端开启 XDMCP 服务
编辑 lightDM 配置文件 /etc/lightdm/lightdm.conf
(如果文件不存在则新建,或在 /etc/lightdm/lightdm.conf.d/ 目录下新建)
sudo vim /etc/lightdm/lightdm.conf
找到 [XDMCPServer]
部分,确保或修改为:
[XDMCPServer]
enabled=true
# port=177 # 通常不需要指定,默认就是 177/udp
# listen-address= # 留空表示监听所有地址,或指定服务器IP
修改配置后,需要重启 lightDM 服务使设置生效。注意:这会中断服务器本地的图形会话
sudo systemctl restart lightdm.service
XDMCP 使用 UDP 协议,默认端口是 177。因此需要确保服务端的防火墙允许来自客户端 IP 地址的 UDP 177 端口的入站连接。这里演示使用 iptables
配置,若为其他请自行搜索相关配置方法。
# 允许来自任何地址的 UDP 177 端口
sudo iptables -A INPUT -p udp --dport 177 -j ACCEPT
# 或更安全准确地只允许来自客户端 IP 连接
# sudo iptables -A INPUT -s 192.168.1.10 -p udp --dport 177 -j ACCEP
2. 客户端通过XDMCP协议连接到服务端
Xephyr 是一个特殊的 X server,它运行在当前 X 会话的一个窗口内。可以让我们在当前桌面的一个窗口里看到并使用远程的服务器桌面。
这里使用 Xephyr
来连接:
Xephyr :2 -query 192.168.1.12 -screen 1280x1024
- :2 指定一个新的 display number 给 Xephyr。
- -query 192.168.1.12 发起 XDMCP 查询。
- -screen 1280x1024 (可选的参数) 可以指定 Xephyr 窗口的分辨率。
这会打开一个新窗口,里面会显示服务端的登录界面,使用帐号密码即可登录。
问题排查及解决方案
通过复现及简单调试可收束问题为:XDMCP连接断开会话管理器不自动退出问题。因此可聚焦在显示管理器(lightDM)以及会话管理器(kiran-session-manager)上。同时测试其他会话管理器是否正常。观察到该现象在 meta-session (KiranDE旧版会话管理器,基于gtk3)并未出现。
测试 meta-session
关闭 Xephyr 窗口后,Display 也会关闭,libX11
会发出一个 _XIOError
,gtk3 中会对该错误进行处理。mate-session 随之关闭。
通过 gdb 跟踪调试 meta-session 在断开连接时的程序调用栈:
...
(gdb) catch syscall exit_group
(gdb) ...
Thread 1 "kiran-session-m" hit Catchpoint 1 (call to syscall exit_group), __GI__exit (status=status@entry=1) at ../sysdeps/unix/sysv/linux/_exit.c:30
30 INLINE_SYSCALL (exit_group, 1, status);
(gdb) bt
#0 __GI__exit (status=status@entry=1) at ../sysdeps/unix/sysv/linux/_exit.c:30
#1 0x00007fd32a5b09b8 in gdk_x_io_error (display=<optimized out>) at ../gdk/x11/gdkmain-x11.c:251
#2 0x00007fd3294827cf in _XIOError (dpy=dpy@entry=0x5557e4b66b50) at XlibInt.c:1548
#3 0x00007fd32947fea5 in _XEventsQueued (mode=2, dpy=0x5557e4b66b50) at xcb_io.c:435
#4 _XEventsQueued (dpy=dpy@entry=0x5557e4b66b50, mode=mode@entry=2) at xcb_io.c:414
#5 0x00007fd3294717c1 in XPending (dpy=0x5557e4b66b50) at Pending.c:55
#6 0x00007fd32a5aab6b in gdk_check_xpending (display=<optimized out>) at ../gdk/x11/gdkeventsource.c:269
#7 gdk_event_source_check (source=0x5557e4b94f90) at ../gdk/x11/gdkeventsource.c:306
#8 0x00007fd329df1f83 in g_main_context_check
(context=context@entry=0x5557e4b95080, max_priority=2147483647, fds=fds@entry=0x5557e4ecd4c0, n_fds=-217997240,
n_fds@entry=6) at ../glib/gmain.c:4035
#9 0x00007fd329df2694 in g_main_context_iterate
(context=0x5557e4b95080, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>)
at ../glib/gmain.c:4208
#10 0x00007fd329df2af3 in g_main_loop_run (loop=loop@entry=0x5557e4daec00) at ../glib/gmain.c:4411
#11 0x00007fd32a82b335 in gtk_main () at ../gtk/gtkmain.c:1329
#12 0x00005557e440b07a in main ()
(gdb) f 1
#1 0x00007fd32a5b09b8 in gdk_x_io_error (display=<optimized out>) at ../gdk/x11/gdkmain-x11.c:251
251 _exit (1);
(gdb) l
246 g_debug ("%s: Fatal IO error %d (%s) on X server %s.\n",
247 g_get_prgname (),
248 errno, g_strerror (errno),
249 display ? DisplayString (display) : gdk_get_display_arg_name ());
250
251 _exit (1);
252 }
(gdb)
测试 ukui-session-manager
在断开 XDMCP 连接时定位到方法:
void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags)
此方法是在 qxcbeventdispatcher
中调用的
- QXcbUnixEventDispatcher::processEvent(flags)
- QXcbConnect::processXcbEvents(flags)
解决方案
通过后续分析在 kiran-session-manager 中此前使用的是 QCoreApplication
,在该模块中缺少对 X11 事件的处理,替换 QCoreApplication 为 QApplication 即可。