前言
在之前的 Homelab 搭建手记(2)更换无线网卡与 XRDP WiFi 扫描授权问题 中,记录过 XRDP 环境下的 WiFi 扫描授权问题。当时发现本地自动登录的 Xfce 和远程 Xfce 同时运行时容易出现 D-Bus、polkit 等额外冲突,所以给出的阶段性建议是关闭本地自动登录,只保留单一的 XRDP 图形会话。
但这个方案本质上是在回避冲突。实际使用中,偶尔还是需要让物理显示器上的本地桌面保持登录,同时从其他设备通过 RDP 进入同一个用户的独立桌面。经过一段时间的日志排查和反复测试,最终确认问题不在端口、密码认证或 Xorg 显示编号,而在第二个 Xfce 会话复用了已有图形会话的环境。
本文记录如何在 Debian 13 + Xfce + XRDP 环境中,让同一个 Linux 用户同时保持本地与远程两个桌面,并进一步补齐 Fcitx5 中文输入和 GNOME Keyring,使 VS Code、GitHub Copilot 等依赖系统密钥环的应用也能在远程桌面中正常使用。相关配置已经整合进公开的 homelab-setup 项目,可以通过脚本自动完成。
一、问题现象与目标
1.1 同用户登录时黑屏退出
最初的故障现象比较固定:
- 用户没有在本地登录时,
RDP可以正常进入Xfce; - 用户已经在本地登录后,再使用同一用户登录
RDP,输入密码后出现黑屏; - 黑屏持续一段时间后,远程连接自动断开;
- 注销其中一个桌面后,另一个桌面又能正常登录。
排查日志后可以看到,远程的 Xorg :10 已经成功启动,分辨率也已经完成设置,随后运行到了 startwm.sh 和 xfce4-session。但第二个 xfce4-session 很快退出,xrdp-sesman 随之关闭远程 Xorg。
这说明认证和 xorgxrdp 基本正常,真正失败的位置在远程 Xfce 会话初始化阶段。
1.2 实测环境
本文配置基于以下环境完成验证:
text12345Debian 13.6 Xfce 4.20 xfce4-session 4.20.2 xrdp 0.10.1 xorgxrdp 0.10.2
可以通过以下命令确认系统和软件包版本:
bash12cat /etc/debian_version dpkg -l | grep -E 'xrdp|xorgxrdp|xfce4-session'
不同 xrdp 版本支持的 session policy 可能存在差异,特别是网上常见的 Policy=UBC 多来自旧版本教程,不应在没有确认版本的情况下直接照搬。
1.3 目标不是共享同一个桌面
本文希望实现的结构是:
text123同一 Unix 用户 ├── 本地 Xfce:Xorg :0 └── RDP Xfce:Xorg :10
两个桌面共享同一个 UID、文件权限和 $HOME,但各自拥有独立的:
- X11 Display;
- systemd-logind session;
- D-Bus Session Bus;
- Xfce Session Manager;
- Keyring 控制目录。
这不是让 RDP 接管物理显示器上的现有桌面。本地打开的窗口不会出现在远程桌面中,反之亦然。如果需要看到完全相同的桌面窗口,应使用桌面共享或 VNC 类方案。
二、为什么常见配置没有解决问题
2.1 ~/.xsession 只决定启动哪个桌面
很多教程会创建 ~/.xsession:
bash12printf '%s\n' 'startxfce4' > ~/.xsession chmod +x ~/.xsession
默认启动链路大致如下:
text123456789xrdp ↓ /etc/xrdp/startwm.sh ↓ /etc/X11/Xsession ↓ ~/.xsession ↓ startxfce4
这个配置回答的是“XRDP 应该启动哪个桌面环境”,并没有解决“同一个用户如何同时运行两个 Xfce session”。当远程 Xorg 已经成功创建而 xfce4-session 随即退出时,继续调整 ~/.xsession、端口或 MaxSessions 并不能触及根因。
2.2 冲突来自图形会话环境
同一个用户已经拥有本地 Xfce 后,第二个桌面可能接触到已有会话的环境,重点包括:
text1234DBUS_SESSION_BUS_ADDRESS SESSION_MANAGER D-Bus Session Bus Xfce Session Manager
仅清除变量后重新进入 /etc/X11/Xsession,在实测环境中仍然不能形成稳定隔离。最终采用的方案是绕过默认 Xsession 启动链路,为远程 Xfce 显式创建独立的 D-Bus Session Bus:
sh123unset DBUS_SESSION_BUS_ADDRESS unset SESSION_MANAGER exec dbus-run-session -- /usr/local/bin/xrdp-xfce-session
其中 dbus-run-session 会为后续程序启动一个新的 Session Bus,并在桌面会话退出后结束该总线。
三、配置独立的 XRDP Xfce 会话
3.1 安装依赖
安装 XRDP、Xfce、D-Bus、输入法和密钥环组件:
bash123456sudo apt update sudo apt install \ xrdp xorgxrdp xfce4 xfce4-goodies dbus-x11 \ gnome-keyring libsecret-1-0 libpam-gnome-keyring libsecret-tools \ fcitx5 fcitx5-chinese-addons fcitx5-frontend-all \ fcitx5-config-qt im-config
本文使用 xorgxrdp。每次新建远程会话时会启动独立的 Xorg Server,而不是复用本地显示器上的 :0。
3.2 配置 sesman.ini
修改系统配置前先备份:
bash1sudo cp /etc/xrdp/sesman.ini /etc/xrdp/sesman.ini.bak
检查 [Sessions] 段:
ini1234567[Sessions] X11DisplayOffset=10 MaxSessions=50 KillDisconnected=false DisconnectedTimeLimit=0 IdleTimeLimit=0 Policy=Default
这些配置分别用于:
- 从
:10开始分配远程 Display,避开本地的:0; - 允许多个
XRDPsession 存在; - 客户端断开后保留远程 session;
- 不因断开或空闲超时自动结束 session;
- 使用
xrdp 0.10实测可用的默认策略。
典型的显示编号为:
text123本地 Xorg: :0 第一个 RDP: :10 后续 RDP: :11、:12……
独立 Display 只能避免 X Server 层冲突,还需要继续隔离 D-Bus 和 Xfce 会话。
3.3 配置 startwm.sh
备份 /etc/xrdp/startwm.sh:
bash1sudo cp /etc/xrdp/startwm.sh /etc/xrdp/startwm.sh.bak
将文件调整为:
sh1234567891011121314#!/bin/sh if test -r /etc/profile; then . /etc/profile fi if test -r ~/.profile; then . ~/.profile fi unset DBUS_SESSION_BUS_ADDRESS unset SESSION_MANAGER exec dbus-run-session -- /usr/local/bin/xrdp-xfce-session
设置权限:
bash1sudo chmod 755 /etc/xrdp/startwm.sh
这里不再执行默认的 /etc/X11/Xsession,而是先读取系统和用户环境,再清除可能继承自其他图形会话的变量,最后在新的 D-Bus Session Bus 中启动专用包装脚本。
3.4 创建 Xfce 与 Keyring 包装脚本
在普通本地 Xfce 中,可以通过桌面自启动运行 gnome-keyring-daemon。这也是 Homelab 搭建手记(4)开发工具配置 中解决 VS Code 密钥管理问题时采用的方案。
但本文为 RDP 创建了独立的 D-Bus。原本运行在 /run/user/UID/bus 上的 Secret Service 不会自动出现在新的私有 bus 中,因此还需要在远程 bus 内启动一个独立的 gnome-keyring-daemon。
创建 /usr/local/bin/xrdp-xfce-session:
bash123456789101112131415161718192021222324#!/bin/bash set -euo pipefail runtime_dir="${XDG_RUNTIME_DIR:?XDG_RUNTIME_DIR is required for an XRDP session}" display_id="${DISPLAY#:}" display_id="${display_id//[^A-Za-z0-9_.-]/_}" if [ ! -d "$runtime_dir" ] || [ ! -w "$runtime_dir" ]; then echo "XRDP runtime directory is not writable: $runtime_dir" >&2 exit 1 fi umask 077 keyring_dir="$(mktemp -d "$runtime_dir/keyring-rdp-${display_id:-unknown}-XXXXXX")" export GNOME_KEYRING_CONTROL="$keyring_dir" gnome-keyring-daemon \ --start \ --components=secrets \ --control-directory="$keyring_dir" \ >/dev/null exec startxfce4
设置权限:
bash1sudo chmod 755 /usr/local/bin/xrdp-xfce-session
包装脚本根据当前 Display 创建权限为 700 的临时 Keyring 控制目录。每次启动使用唯一目录,避免重新连接或多个远程会话相互删除仍在使用的控制目录。
最终启动链路为:
text1234567891011xrdp-sesman ↓ /etc/xrdp/startwm.sh ├── 读取 /etc/profile 与 ~/.profile ├── 清除已有图形会话环境 └── dbus-run-session ├── 创建 RDP 私有 Session Bus └── xrdp-xfce-session ├── 创建独立 Keyring 控制目录 ├── 启动 Secret Service └── 启动 Xfce
3.5 配置 Fcitx5
由于新的 startwm.sh 绕过了 Debian /etc/X11/Xsession,不能再假设 ~/.xprofile 会被自动读取。本文明确读取 ~/.profile,所以将输入法环境变量写入该文件:
sh12345# Fcitx5 input method export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx export XMODIFIERS=@im=fcitx export GLFW_IM_MODULE=ibus
这里只负责设置环境,不要直接在 .profile 中启动 fcitx5。创建 ~/.config/autostart/fcitx5.desktop,让 daemon 在当前 Xfce 会话中启动:
ini12345678[Desktop Entry] Type=Application Name=Fcitx 5 Comment=Start Fcitx 5 Input Method Exec=fcitx5 -d Terminal=false Hidden=false X-GNOME-Autostart-enabled=true
职责划分为:
text12345~/.profile └── 设置输入法环境变量 Xfce Autostart └── 在当前图形会话中启动 fcitx5 daemon
im-config -m 中出现 Active configuration: missing (normally missing) 不一定表示配置错误。在自动模式下,只要当前选择和自动选择均为 fcitx5,就无需为了消除 missing 强制生成 ~/.xinputrc。
3.6 检查 PAM Keyring 配置
检查 /etc/pam.d/xrdp-sesman:
bash1grep -n 'pam_gnome_keyring' /etc/pam.d/xrdp-sesman
Debian 软件包通常已经提供类似配置:
text12-auth optional pam_gnome_keyring.so -session optional pam_gnome_keyring.so auto_start
如果已经存在,不要重复添加。PAM 认证发生在 startwm.sh 创建私有 D-Bus 之前,因此 PAM 启动的 Keyring 仍不能代替包装脚本中服务于远程私有 bus 的 Secret Service。
四、使用公开脚本自动配置
4.1 执行 XRDP 模块
手工配置有助于理解启动链路,实际部署时可以直接使用公开的 homelab-setup。克隆项目后执行:
bash123git clone https://github.com/DoraTiger/homelab-setup.git cd homelab-setup bash init.sh --silent 12-xrdp.sh
对应实现位于:
modules/12-xrdp.sh:安装依赖并编排配置流程;lib/xrdp-config.sh:生成和收敛配置的纯函数;tests/integration/test-xrdp-config.sh:验证幂等性、D-Bus 隔离、Keyring 和 Fcitx5 环境。
4.2 脚本的收敛策略
脚本没有简单覆盖所有系统文件,而是按配置归属分别处理:
- 只修改
sesman.ini的目标键,保留其他 section 和用户配置; - 已经具备正确私有 D-Bus 启动链的
startwm.sh保留原内容,只校正权限; - 修改系统文件前集中备份,不在
/etc和用户目录中散落多个.bak; - Fcitx5 环境已经完整时保持用户文件不变;
- 只迁移内容与旧版脚本完全一致的
.xsession和已知旧polkit规则; - 用户自行维护的同名文件只提示,不自动删除;
- 配置完成后不会主动重启正在运行的
XRDP服务,避免中断现有会话。
这部分设计主要解决“脚本能重复运行”和“不能覆盖用户已有配置”两个问题。相关集成测试会在临时目录中连续执行两次配置收敛,确保结果保持一致。
4.3 重新建立远程会话
配置完成后,应在方便时正常注销当前远程 Xfce,然后重启服务:
bash1sudo systemctl restart xrdp xrdp-sesman
只关闭 RDP 客户端窗口通常是断开连接,不代表远程 session 已经注销。为了确保新的 startwm.sh、D-Bus 和 Keyring 启动链完整生效,首次验证时应从 Xfce 菜单正常注销后重新登录。
五、验证会话隔离与桌面功能
5.1 验证 logind session
保持本地桌面登录,再使用同一个用户建立 RDP 连接。查看整机 session:
bash1loginctl list-sessions
然后分别检查本地和远程 session:
bash12345loginctl show-session LOCAL_SESSION_ID \ -p Name -p Type -p Class -p Remote -p Seat -p Display -p State loginctl show-session RDP_SESSION_ID \ -p Name -p Type -p Class -p Remote -p Seat -p Display -p State
预期核心结果为:
text12本地:Display=:0 Remote=no State=active RDP: Display=:10 Remote=yes State=active
loginctl list-sessions 查询的是整台机器的 systemd-logind 数据库,所以在本地和远程终端看到相同列表属于正常现象。
5.2 验证 DISPLAY 与 D-Bus
分别在两个桌面中执行:
bash123echo "DISPLAY=$DISPLAY" echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" echo "XDG_SESSION_ID=$XDG_SESSION_ID"
典型结果如下:
| 项目 | 本地 Xfce | RDP Xfce |
|---|---|---|
| 用户与 UID | 相同 | 相同 |
| DISPLAY | :0.0 |
:10.0 |
| Session ID | 本地 session | RDP session |
| Remote | no |
yes |
| D-Bus | /run/user/UID/bus |
/tmp/dbus-* |
$HOME |
共享 | 共享 |
重点不是 session 编号必须与示例一致,而是两个桌面的 Display、Session ID 和 D-Bus 地址不同,并且都处于 active 状态。
5.3 验证中文输入
在远程桌面执行:
bash123456echo "$GTK_IM_MODULE" echo "$QT_IM_MODULE" echo "$XMODIFIERS" echo "$GLFW_IM_MODULE" pgrep -a fcitx5 im-config -m
然后分别在本地和远程打开一个 GTK 编辑器,使用 Ctrl+Space 切换并输入中文。只有环境变量正确但没有 fcitx5 进程时,应检查 ~/.config/autostart/fcitx5.desktop,而不是继续修改 Locale。
5.4 验证 Secret Service
确认远程桌面仍使用私有 D-Bus:
bash12echo "$DBUS_SESSION_BUS_ADDRESS" busctl --user list | grep -E 'secret|keyring'
正常情况下,org.freedesktop.secrets 应显示实际 owner 和 PID,而不只是 (activatable)。
使用 secret-tool 完成读写测试:
bash12345secret-tool store \ --label='XRDP Persistence Test' \ test persistence secret-tool lookup test persistence
正常注销并重新建立远程会话后,再次执行 lookup。如果仍能读取保存的内容,说明新的 Keyring daemon 已经从 ~/.local/share/keyrings/ 加载持久化数据。测试完成后清理:
bash1secret-tool clear test persistence
最后启动 VS Code,确认不再提示 OS Keyring 不可用,GitHub Copilot OAuth 可以调用当前远程会话中的浏览器,并且重新登录 RDP 后凭据仍然存在。
六、问题排查
6.1 按启动层级检查日志
如果输入密码后仍然黑屏退出,不要同时修改多个参数。先确认软件版本、sesman.ini 和实际启动脚本:
bash1234cat /etc/debian_version dpkg -l | grep -E 'xrdp|xorgxrdp|xfce4-session' cat /etc/xrdp/startwm.sh grep -v '^[[:space:]]*[#;]' /etc/xrdp/sesman.ini
然后查看 XRDP 日志:
bash12sudo tail -200 /var/log/xrdp.log sudo tail -200 /var/log/xrdp-sesman.log
如果认证成功、远程 Xorg 已经启动并运行到 xfce4-session,继续排查桌面与 D-Bus:
bash1234567sudo journalctl -b --no-pager \ | grep -Ei 'xrdp|xfce|dbus|session|pam_systemd' \ | tail -250 grep -RiE 'error|failed|already|session|dbus|display' \ ~/.xsession-errors* ~/.local/share/xrdp/ 2>/dev/null \ | tail -200
最后检查进程和会话:
bash12ps -u "$USER" -f | grep -E 'xfce|dbus|keyring|fcitx|xrdp|Xorg' loginctl list-sessions
6.2 几个容易误判的方向
只反复修改 ~/.xsession:它只负责选择桌面,不能隔离第二个 Xfce session。
把显卡警告直接当成根因:日志中可能出现 unsupported render node、amdgpu_device_initialize failed 等警告。如果远程 Xorg 已经建立并运行到 xfce4-session,这些警告通常不是本次退出的直接原因。
让 RDP 强制复用 /run/user/UID/bus:这样可能暂时让应用找到本地 Keyring,却会破坏远程桌面的 D-Bus 隔离。实测中会进一步影响浏览器和 OAuth 调用。
为了 im-config 的 missing 强制生成配置:应先确认自动选择是否已经是 fcitx5,不要只根据一个状态词判断输入法故障。
直接清理整个 Keyring 目录:多个会话可能仍在使用各自的控制目录。当前脚本使用 mktemp 创建唯一目录,不会为了新会话删除旧会话的目录。
七、适用边界
7.1 两个桌面仍然共享 $HOME
隔离 X11、D-Bus 和 Keyring 控制目录,并不等于把整个用户运行环境完全隔离。两个桌面仍然共享:
- 用户文件和配置;
- 浏览器 profile;
~/.local/share/keyrings/中的持久化数据;- 使用文件锁或单实例机制的应用状态。
因此 Firefox、Chromium 等应用不适合在两个桌面中同时使用同一个 profile,某些托盘程序和单实例 GUI 软件也可能发生冲突。对于终端、IDE、文件管理和一般服务器运维,这种模式基本够用。
7.2 会话保留与资源占用
本文配置会保留断开的远程 session,并关闭空闲超时。这样可以在网络中断后重新连接原桌面,但也意味着长期不注销的 session 会持续占用内存和进程资源。完成工作后应从 Xfce 菜单正常注销,不要只关闭 RDP 客户端。
如果服务器需要严格的资源回收策略,应根据实际使用方式调整 KillDisconnected、DisconnectedTimeLimit 和 IdleTimeLimit,而不是直接沿用本文的长期保留配置。
八、总结
同一个用户无法同时运行本地和远程 Xfce,表面上表现为 RDP 黑屏退出,实际根因并不在密码、端口或 Display 编号,而是第二个桌面没有形成完整的会话隔离。
最终方案的核心是:
text123456789本地 Xfce :0 └── systemd User Bus + 本地 Keyring RDP Xfce :10 └── dbus-run-session ├── 私有 D-Bus Session Bus ├── RDP 专用 Secret Service ├── Fcitx5 └── Xfce
在此基础上,本地与远程桌面可以同时保持 active,远程桌面中的中文输入、VS Code Keyring 和 Copilot 登录也能够正常工作。与第(2)篇中避免并发桌面的阶段性方案相比,这次不再回避问题,而是对 X11 Display、logind session、D-Bus 和 Keyring 分别进行了隔离。
相关实现已经进入公开的 homelab-setup,手工配置用于解释工作原理,日常部署则由脚本负责幂等收敛、备份和验证。后续如果 xrdp 或桌面环境版本发生变化,仍应以完整启动链和实际日志为依据重新验证,而不是只依赖某一条历史配置。

