两台机器的 GNOME RDP 排查:xrdp 占端口 / 0x907 证书 / 白屏闪退 / 凭证丢失
八月 06, 2026 更新: 九月 11, 2026 [桌面与效率] #gnome #RDP #Linux #gdm #freerdp #debian两台机器的 GNOME RDP 排查记录
两台机器一台 Ubuntu 24.04、一台 Debian 13,GNOME 版本不同(46 vs 48),遇到的问题也不同。
机器清单(2026-08-11 补充):
| 代号 | IP | 主机名 | 系统 | GNOME | grd 版本 | 客户端 | 状态 |
|---|---|---|---|---|---|---|---|
| 机器 A | 192.168.5.244 | — | Ubuntu 24.04.4 | 46.0 | 46.3 | Windows mstsc | 正常(双守护进程) |
| 机器 B | 192.168.7.99 | xiao99 | Debian 13 (Trixie) | 48.7 | 48.1 | Linux Remmina | 正常(本次修复) |
| douhao(对照参考) | 192.168.5.68 | debian13 | Debian 13(QEMU/KVM 虚拟机,跑在机器 B 上) | 48 | 48.1 | — | 一直正常 |
术语澄清
GNOME Remote Desktop 有两个独立的守护进程,都叫 gnome-remote-desktop,但运行级别不同:
| 系统级(远程登录) | 用户级(桌面共享) | |
|---|---|---|
| service 名 | gnome-remote-desktop.service (system) | gnome-remote-desktop.service (user) |
| 管理命令 | sudo systemctl start/stop ... | systemctl --user start/stop ... |
| 配置命令 | sudo grdctl --system rdp ... | grdctl rdp ... |
| 功能 | 提供 GDM 登录界面 | 共享已有桌面会话 |
| 客户端看到 | 登录屏 → 新桌面 | 直接进入当前桌面 |
| 默认端口 | 3389 | 3390(自动避让) |
--headless 不是守护进程类型,只是用户级守护进程的一个启动参数(drop-in 文件中的 ExecStart=... --headless)。加上:不依赖物理显示器,创建虚拟屏。不加:直接使用/共享已有的 Wayland 桌面。
机器 B 最终用的是用户级、非 headless——它还是一个守护进程。
机器 A:Ubuntu 24.04.4 + GNOME 46.0
环境: Ubuntu 24.04.4, GNOME 46.0, gnome-remote-desktop 46.3。客户端:Windows 10/11 mstsc。
故障
- 设置里「远程登录」开关一开就自动弹回
- Windows mstsc 连上后报 0x907「非预期的服务器身份验证证书」
现象
- 系统里同时装了
xrdp(xrdp/xrdp-sesman/xorgxrdp/pipewire-module-xrdp) - mstsc 连接无论 IP 还是主机名都报 0x907,不弹任何确认框
- 但用 Remmina / xfreerdp
/cert:ignore能正常连接
问题 1:开关弹回 — xrdp 占端口
服务状态和日志:
systemctl --user status gnome-remote-desktop.service
grdctl status # 用户级守护进程(桌面共享)
grdctl --system status # 系统级守护进程(远程登录)
journalctl -u gnome-remote-desktop.service
关键日志(用户每次点开关,系统守护进程都尝试绑 3389):
RDP server started
Failed to bind port 3389 after 10 attempts ← 重试 10 次失败
RDP server stopped ← 服务停止 → 开关弹回
查谁占了 3389:
ss -tlnp | grep 3389
# 0.0.0.0:3389 ← xrdp (root)
journalctl -u xrdp # listening to port 3389 on 0.0.0.0
根因:xrdp 开机抢先绑定 0.0.0.0:3389。xrdp.service 虽 disabled,但 Requires=xrdp-sesman.service(enabled),开机被拉起,8 秒内抢走 3389。GNOME 远程桌面绑不上,系统级守护进程重试 10 次后放弃,UI 开关弹回。
附:设置页「某些设置已锁定」横幅是 polkit 动作
org.gnome.controlcenter.remote-session-helper要求管理员认证,点「解锁」输密码即可,不是故障。
问题 2:mstsc 0x907 证书错误
服务端实测(本机 xfreerdp 一切正常):
xfreerdp /v:127.0.0.1:3389 /u:peter /p:xxx /auth-only /cert:ignore
# 认证测试通过
# 抓取服务端证书指纹 — 与配置文件一致,证书与私钥匹配,签名有效,有效期正常
但 mstsc 每次在 TLS 协商阶段中止,服务端日志:
[freerdp_tls_handshake]: BIO_do_handshake failed
CONNECTION_STATE_NEGO - rdp_server_accept_nego() fail
对照实验(同一台 Windows、同一个 mstsc):
| 监听 3389 的进程 | 证书 | mstsc 结果 |
|---|---|---|
| xrdp(之前) | xrdp 自签名 | 连过,Windows 缓存了该证书 |
| grd 用户守护进程 | grd 自签名 | 0x907 硬失败,无确认框 |
| grd 系统守护进程 | grd 自签名 | 正常连接 |
已确认:0x907 是 Windows 检测到服务器证书与本地缓存不一致时的硬性终止(防中间人设计,不弹框)。之前 3389 上跑的是 xrdp 的证书,换成 grd 的证书后指纹不同 → 0x907。在**新端口(无缓存)**上连接一次并接受新证书后,3389 也能连了。
推测:mstsc 直连用户守护进程时 NLA 凭据校验路径与预期不一致。grd 46 源码中确实有针对 mstsc 的特殊处理(use_system_credentials = is_client_mstsc && !requested_rdstls)。
机器 A 解决方案
方案 A1:同时开启「远程登录」+「桌面共享」(推荐)
- 远程登录 → 系统守护进程监听 3389
- 桌面共享 → 用户守护进程监听 3390(自动避让)
- mstsc 连 3389 即可
方案 A2:Windows 侧清证书缓存
regedit → HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers
删除对应主机名的条目
或换一个从未连过的地址形式(IP ↔ 主机名)连一次,接受新证书后原地址也能连。
机器 A 总结
- 开关弹回根因:xrdp 抢占 3389,
systemctl stop/disable xrdp xrdp-sesman后重启 gnome-remote-desktop - mstsc 0x907 是 Windows 证书缓存不一致,清缓存或接受新证书解决
- grd 46 双守护进程下 mstsc 直连用户守护进程不可靠,连 3389 由系统守护进程接管最省心
机器 A 后续(2026-08-12):双守护进程最终形态与 setup.rdp.sh 固化
机器 A 最终配置即上文「方案 A1」的完整落地(日常使用、改密码全部由 script/setup.rdp.sh 一键固化):
| 端口 | 角色 | 说明 |
|---|---|---|
| 3389 | 系统级 gnome-remote-desktop.service(--system) | GDM 登录界面(远程登录),用系统用户密码 |
| 3390 | 用户级守护进程(桌面共享,SCREEN_SHARE 模式) | 自动避让端口,用 RDP 凭证 |
--handover:登录后接管连接,由 gsd-sharing 按需拉起(GNOME 46 无独立 handover unit,不影响)。
本次踩的新坑(按严重程度排序):
坑 1:系统级凭证写入后,运行中的 daemon 不重载凭证
grdctl --system rdp set-credentials 写完后,3389 认证依然 STATUS_LOGON_FAILURE——运行中的系统 daemon 不会自动重读凭证文件,必须重启才生效(输出里的 tcti:IO failure, using GKeyFile as fallback 正是凭证走 GKeyFile 后端的回退噪音,凭证仍写入成功;原文写「不重载 grd.conf」,文件位置见后文更正):
sudo systemctl restart gnome-remote-desktop.service
前提:重启时没有活跃的 RDP greeter 会话(见坑 5,否则会把物理会话一起踢掉)。
坑 2:SCREEN_SHARE 模式改密码不生效——凭证只读 keyring
上文 Step 1 从源码得出「SCREEN_SHARE 凭证只读 keyring」的结论,机器 A 这次是反着踩的:脚本只改了 credentials.ini,3390 依旧用旧密码。改密码必须写 keyring(物理会话活跃、keyring 已解锁时写入即时生效):
grdctl rdp set-credentials peter '<新密码>'
--headless 模式(凭证读 credentials.ini)可以绕开 keyring 死结,但代价是自建虚拟屏、看不到物理屏内容——共享物理屏场景反而要留在 SCREEN_SHARE 模式并同步 keyring。
坑 3:自检命令在 SSH 环境跑不起来
xfreerdp 是 X11 客户端,无 DISPLAY 直接报 failed to open display。自检用 xvfb-run 包一层:
xvfb-run -a xfreerdp /v:127.0.0.1:3389 /u:peter /p:xxx /auth-only /cert:ignore
坑 4:「解锁后几秒又锁住」实为自动熄屏黑屏(非复锁)
loginctl unlock-session 后轮询 20 秒,LockedHint=no 稳定——会话并未复锁,是显示器自动熄灭(黑屏),SSH 解锁不会唤醒物理屏幕,看起来像"又锁了"。防锁屏判断也要小心:按 headless 服务是否 active 判断,不能按「服务文件是否存在」——Ubuntu 24.04 自带该文件,即使实际跑的是 SCREEN_SHARE 也会被跳过:
gsettings set org.gnome.desktop.screensaver lock-enabled false
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
gsettings set org.gnome.desktop.session idle-delay 0
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing'
坑 5(最重):重启系统级 daemon 会连锁踢掉物理会话
现象:脚本执行 sudo systemctl restart gnome-remote-desktop.service 时,本机物理会话(seat0)直接退出,GDM 自动登录恢复新会话。
根因链(journal 实证):
其他机器连着 3389 登录界面 → GDM 有活跃 RDP greeter 会话(user=gdm, Remote=yes)
→ 重启系统 grd daemon → greeter 全被杀(GdmDisplay: Session never registered, failing)
→ GDM 显示管理连锁出错(on_display_added: assertion 'GDM_IS_REMOTE_DISPLAY (display)' failed)
→ GDM 重新拉起 greeter 到 seat → logind 结束当前物理会话(Session N logged out)
对策:默认不重启系统 daemon——只写配置,等下次 daemon 启动/开机生效;必须立即生效时,先确认所有机器都关掉 3389 登录窗口再重启。检测会话时注意:greeter 的用户名是 Debian-gdm(不是 gdm),按用户名匹配会漏;可靠做法是按 CLASS 列过滤 loginctl list-sessions --no-legend | awk '$6=="greeter"{print $1}'。用户级 daemon(3390)重启无此风险。loginctl terminate-session 手动终止 greeter 也会弄脏 GDM 状态(后续一次 on_display_added 断言失败就能踢会话),尽量让客户端自然断开。
2026-09-11 在 douhao(Debian 13 / GDM 48 / grd 48.1)复现确认:重启系统 daemon 后物理会话(seat0)确实被踢,GDM 在 seat0 拉起新 greeter(当时有一个残留的远程 greeter c2 被杀,触发连锁)。连带后果:
- 用户级 daemon 随会话结束一起停止(
gnome-remote-desktop.service/-handover.service都WantedBy=gnome-session.target),3390 不再监听;- gnome-keyring daemon 随会话退出,login keyring 重新变锁定(下次控制台登录时 PAM 会尝试用登录密码解锁);
- tmux server 不受影响(tmux 客户端死、server 存活,重新 attach 即可)。
恢复:到虚拟机控制台在 seat0 登录界面登录,用户级服务会自动拉起;被
disable的 headless 服务不会自己回来。所以对没有控制台访问的远程机器,重启系统 daemon 前要先准备好控制台(Proxmox / virt-manager)。
坑 6:共享模式默认 mirror-primary,extend 必须显式选择
extend 模式自建虚拟扩展屏,RDP 里看不到物理屏内容——用户连 3390 会误以为"变成 headless 了"。脚本曾做「无物理会话自动回退 extend」的探测,实测在物理会话重启的空档 loginctl 查不到 seat0 会话,把 mirror 悄悄切回 extend。最终:默认 mirror-primary,只有显式 --extend 才配置 extend。
setup.rdp.sh(2026-08-12 版用法)
bash setup.rdp.sh # 默认:保留现有模式/密码,不重启任何 daemon
bash setup.rdp.sh --set-password # 改密码(提示输入新密码)
bash setup.rdp.sh --set-password --restart # 改密码并重启 daemon 立即生效(先确认无人在 3389 登录界面)
bash setup.rdp.sh --headless # 强制用户级 headless 模式
bash setup.rdp.sh --screenshare # 强制 SCREEN_SHARE 模式(默认保留现有模式)
bash setup.rdp.sh --extend # 共享模式用虚拟扩展屏(默认 mirror-primary)
设计要点:
--restart显式开关:默认不重启任何 daemon(坑 5);重启前检测 gdm 会话并告警- SCREEN_SHARE 模式自动
grdctl rdp set-credentials同步 keyring(坑 2),写入即时生效 - 系统级凭证写入后仅
--restart时重启 daemon(坑 1) - 末尾对 3389/3390 双端口做
/auth-only自检(xvfb-run,坑 3) - 防锁屏按 headless 服务 is-active 判断(坑 4)
- 参数冲突检查:
--headless×--screenshare、--extend×--mirror、--headless×--extend/--mirror报错退出
机器 B:Debian 13 (Trixie) + GNOME 48
环境: Debian 13 (Trixie), GNOME Shell 48.7, gnome-remote-desktop 48.1。客户端:Linux Remmina。
故障
- 连接 3389 白屏 + 服务端 CPU 飙升
- 连接 3390 闪退(NTLM 认证失败)
- 密码设置后始终不生效
排查过程
先检查当前状态:
systemctl --user status gnome-remote-desktop.service
grdctl status
ss -tlnp | grep -E '3389|3390'
初始状态:用户守护进程以 --headless 模式运行在 3389,用户名和密码均为空。不存在 xrdp 冲突。
Step 1:设置凭证(第一个坑)
grdctl rdp set-credentials peter '<your-password>'
# 无输出,看似成功
grdctl status | grep -E "Username|Password"
# Username: (hidden)
# Password: (hidden) ← 显示正常
客户端连上后日志:
[ERROR][com.winpr.sspi.NTLM] - [ntlm_read_AuthenticateMessage]: Message Integrity Check (MIC) verification failed!
[ERROR][com.freerdp.core.transport] - [transport_accept_nla]: client authentication failure
无论怎么重设密码都不行。后来发现:
cat ~/.local/share/gnome-remote-desktop/credentials.ini
# [RDP]
# credentials={'username': <'peter'>, 'password': <'旧密码'>}
# ↑ 几个月前的旧密码,grdctl 根本没更新它!
根因:grdctl 优先写 GNOME Keyring,如果 keyring 不可用(SSH 环境无 gnome-keyring-daemon),静默回退到 credentials.ini,但回退路径不工作。直接编辑文件绕过:
vim ~/.local/share/gnome-remote-desktop/credentials.ini
# 改 password 字段为新密码
systemctl --user restart gnome-remote-desktop.service
结论修正(2026-08-11,源码核实):上面「直接编辑 credentials.ini 后重启成功」的原因不是 daemon 读了 credentials.ini——grd 48 源码(
grd-credentials-libsecret.c+grd-settings.c)证明 SCREEN_SHARE(用户级无参数)模式的凭证只从 keyring(libsecret)读,lookup 失败直接返回 NULL,不存在 GKeyFile 回退。当时真正生效的是 keyring 里的凭证:8月6日 通过 3389 GDM 登录界面登录时,gdm-password的 PAM 配置(pam_gnome_keyring.soauth + session auto_start)创建并解锁了login.keyring(09:57 创建,379 字节),之后 grdctl 把 RDP 凭证写进了这个已解锁的 keyring。所以「vim credentials.ini + 重启」能成功的真实链路是:重启让 daemon 重新从 keyring 读到凭证(恰好那时 keyring 可读),credentials.ini 本身没被读取。credentials.ini 只对--headless/--system模式(GKeyFile 后端)有效。
Step 2:开启系统守护进程(引入新问题)
按照机器 A 的经验,推荐双守护进程。启用系统守护进程:
sudo grdctl --system rdp enable
sudo grdctl --system rdp set-credentials peter '<your-password>'
# 证书路径问题:系统守护进程以 gnome-remote-desktop 用户运行,
# 而非 root。正确的证书位置:
sudo mkdir -p /var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/certificates
HOSTNAME=$(hostname)
sudo openssl req -x509 -newkey rsa:4096 -sha384 -days 730 -nodes \
-keyout /var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/certificates/rdp-tls.key \
-out /var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/certificates/rdp-tls.crt \
-subj "/CN=$HOSTNAME" \
-addext "subjectAltName=DNS:$HOSTNAME" \
-addext "keyUsage=digitalSignature,keyEncipherment" \
-addext "extendedKeyUsage=serverAuth" \
-addext "nsCertType=server"
sudo chown gnome-remote-desktop:gnome-remote-desktop \
/var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/certificates/rdp-tls.*
# grd.conf 配置文件在 /etc/gnome-remote-desktop/grd.conf
结果:
- 系统守护进程启动到 3389
- 用户守护进程自动退到 3390
- 连接 3389 → 白屏 + CPU 飙升
- 连接 3390 → 闪退(NTLM MIC 失败)
Step 3:白屏排查 — GDM Wayland 渲染异常
日志显示系统守护进程成功启动并创建了 GDM greeter 会话:
loginctl list-sessions
# session c3, c4, c5 ... Type=wayland Class=greeter Remote=yes RemoteHost=0.0.0.0
但客户端白屏。
根因:系统级和用户级使用了不同的 compositor。
两个端口背后的渲染路径完全不同:
| 3389(系统级) | 3390(用户级) | |
|---|---|---|
| 运行用户 | Debian-gdm | peter(实际用户) |
| compositor | GDM 自带的轻量 greeter compositor | GNOME Shell 完整 compositor |
| 渲染方式 | GDM 独立启动 Wayland 进程绘制登录界面 | PipeWire screencast portal 抓取已有画面 |
| 是否 GPU 加速 | 在 RDP 虚拟输出上初始化失败 | 已在物理显示器上正常渲染,只需编码传输 |
GDM greeter compositor 极其精简,只负责画一个登录框,对 RDP 虚拟输出的支持不完善。而 GNOME Shell 是完整 compositor,在物理显示器上已经借助 GPU 渲染好了,3390 只是通过 PipeWire 把画面编码成 RDP 流发出去——本质上是屏幕录制,不涉及在新输出上重新渲染。
登录后进入的还是 GNOME Shell。 GDM 只是门卫——认证通过后启动正常用户会话。问题纯粹出在门卫自己的渲染上。
能否换掉 GDM? 不行。gnome-remote-desktop 系统级守护进程通过 D-Bus 直接调 GDM 来创建 gdm-launch-environment 会话,这个接口是 GDM 专有的,LightDM 等没有这些 D-Bus API,grd 也不支持其他显示管理器。要解决系统级白屏,只有三条路:
-
强制 GDM 用 Xorg(白屏消失,但 Wayland 洁癖不答应):
sudo sed -i 's/^#\s*WaylandEnable=false/WaylandEnable=false/' /etc/gdm3/custom.conf sudo systemctl restart gdm3 -
等上游修:GDM greeter compositor 对 RDP 虚拟输出的支持是 grd/freerdp 的持续工作
-
绕过:不用系统级,只保留用户级独占 3389(当前方案)
Step 3b:白屏真根因(2026-08-11 结论修正,douhao 对照实测)
过时说明:上面「GDM Wayland greeter 渲染异常」的结论只描述了表象。后来用正常机器(douhao,同 Debian 13 + GNOME 48)逐项对照排查,发现白屏的真根因在登录界面渲染链路的前置环节——
--handoverdaemon 根本没被启动,greeter 会话里从未创建虚拟显示器。修正记录如下。
对照实验发现的关键差异:
| douhao(正常) | dev(白屏) | |
|---|---|---|
| greeter 会话(Debian-gdm)进程 | 有 gnome-remote-desktop-daemon --handover(gsd-sharing 的直接子进程) | 没有 --handover 进程 |
| greeter gnome-shell 日志 | Added virtual monitor Meta-0(创建了虚拟显示器) | 无此日志 |
nmcli general | connected:full | connected (local only): limited |
NM PrimaryConnection 属性 | 非空(ActiveConnection/2) | /(空) |
| gsd-sharing 的 SharingStatus | AVAILABLE(3) | OFFLINE(0) |
根因链(逐环实测确认):
手动添加的默认路由(ip route add ... proto static,无 metric)
→ NetworkManager 不认为是自己管理的路由(PrimaryConnection=/ 空,ActiveConnection.Default=false)
→ gsd-sharing 的 nm_client_get_primary_connection() 返回 NULL
→ sharing_status = GSD_SHARING_STATUS_OFFLINE
→ start_assigned_service() 第一行就 return(源码 gsd-sharing-manager.c)
→ --handover daemon 永不启动
→ greeter 会话没有虚拟显示器(缺 "Added virtual monitor" 日志)
→ 登录界面白屏 + CPU 飙升(mutter 在虚拟输出上空转渲染)
gsd-sharing 的启动逻辑(gnome-settings-daemon 48 源码 plugins/sharing/gsd-sharing-manager.c):
assigned_services[]里注册了org.gnome.RemoteDesktop→gnome-remote-desktop-handover,remote_session_classes = { "user", "greeter" }- system bus 上
org.gnome.RemoteDesktop名字出现(系统级 daemon 注册)→ 触发start_assigned_service() - 该函数第一行:
if (manager->sharing_status == GSD_SHARING_STATUS_OFFLINE) return;← 白屏就是被这一行拦住的 sharing_status由nm_client_get_primary_connection()决定:有 active connection 且 carrier type 非空 → AVAILABLE;否则 OFFLINE- 启动方式:
sd_pid_get_user_unit()成功(systemd 管理)→ 走org.freedesktop.systemd1StartUnit;失败 → 直接g_desktop_app_info_launchfork.desktop(douhao 就是这条路径)
修复(dev 上执行,网络是 bridge 连接 br0):
# 重新激活网络连接,让 NM 接管默认路由(NM 配置里已有 ipv4.gateway)
sudo nmcli con up br0
# 验证(三处全部转好才说明修复到位):
nmcli -t -f STATE,CONNECTIVITY general
# connected:full ← 原来 limited
dbus-send --system --print-reply --dest=org.freedesktop.NetworkManager \
/org/freedesktop/NetworkManager org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.NetworkManager string:PrimaryConnection
# variant object path "/org/freedesktop/NetworkManager/ActiveConnection/5" ← 原来 "/"
dbus-send --session --print-reply --dest=org.gnome.SettingsDaemon.Sharing \
/org/gnome/SettingsDaemon/Sharing org.freedesktop.DBus.Properties.Get \
string:org.gnome.SettingsDaemon.Sharing string:SharingStatus
# variant uint32 3 (AVAILABLE) ← 原来 0 (OFFLINE)
修复后 3389 直接显示 GDM 登录界面,登录 → handover → 桌面完整链路正常,不需要停系统级守护进程或强制 GDM Xorg。
为何之前误判:白屏期间 greeter 的 EGL/GPU 初始化日志全部正常(Obtained a high priority EGL context),且修好 /dev/dri 的 ACL 后仍白屏,所以最初归因到「greeter 渲染异常」。而 Failed to handle service change: Process org.freedesktop.systemd1 exited with status 1 这类报错是 rygel 等 configurable services 走 systemd 激活失败的噪音(org.freedesktop.systemd1.service 的 Exec=/bin/false 是系统设计如此),与白屏无关,别被误导。
排查口诀:3389 白屏 → ① nmcli general 看是不是 limited/local only → 是则 nmcli con up <连接> 重新激活;② 看 greeter 会话里有没有 gnome-remote-desktop-daemon --handover 进程和 Added virtual monitor 日志;③ 查 gsd-sharing 的 SharingStatus 是否为 0。
Step 3c:网络必须由 NetworkManager 管理(2026-08-11 补充)
结论:3389 远程登录(GDM greeter 路径)依赖 NetworkManager,用 systemd-networkd 管理网络不行。
这是 Step 3b 依赖链的另一种触发方式(同一根因,不同起因):
- 手动加默认路由(机器 B 的起因):NM 里连接是 active 的,但默认路由不是 NM 添加的 →
PrimaryConnection空 → OFFLINE → 白屏 - systemd-networkd 管理网络:NM 把设备标为
unmanaged,NM 里根本没有 active connection →PrimaryConnection空 → 同样 OFFLINE → 白屏
本质都是 NM 不认为自己有主连接(详细依赖链见 Step 3b)。
绕开路径:
- 网络交给 NM 管理(推荐):
nmcli con up <连接>让 NM 接管,PrimaryConnection 非空即可 - 不用 3389 登录界面:用户级独占 3389 直连桌面(本页机器 B 最终方案)不依赖 NM——用户级 daemon 由 systemd 管,不经过 gsd-sharing。代价是没有 GDM 登录界面,不能走 handover 登录接管。
副作用提醒:nmcli con up <网桥> 重新激活网桥时,会把挂在网桥上的虚拟机 tap 网卡(如 vnet0)踢出网桥导致虚拟机失联。修复:
sudo ip link set vnet0 master br0 # 重新挂回
ls /sys/class/net/br0/brif/ # 确认成员里有 vnet0
Step 3d:3390 直连 Unknown monitor — 用 extend 模式(2026-08-11 补充)
现象:直连 3390(用户级桌面共享)时,认证通过、通道协商成功,但随后被服务器断开:
[ERROR][com.freerdp.core.peer] - ERRINFO_RPC_INITIATED_DISCONNECT [0x00010001]
gnome-remote-de: Failed to record monitor: GDBus.Error:org.freedesktop.DBus.Error.Failed: Unknown monitor
根因:用户级 daemon 默认共享模式是 mirror-primary——镜像现有桌面的主物理显示器。当物理会话被注销(seat0 上只剩 GDM 登录界面)、目标会话是无 seat 的 headless 会话(gnome-shell 日志 No seat assigned, running headlessly)时,没有物理屏可镜像 → Unknown monitor → 拒绝连接。
修复:切换到 extend 模式(创建虚拟显示器,不依赖物理屏):
gsettings set org.gnome.desktop.remote-desktop.rdp screen-share-mode extend
systemctl --user restart gnome-remote-desktop.service
结论:extend 模式不需要物理会话登录。实测在物理会话注销、只剩 RDP 登录的无 seat 会话时,3390 直连成功、桌面正常显示。两种模式对比:
| 模式 | 画面来源 | 需要物理屏? | 适用场景 |
|---|---|---|---|
mirror-primary(默认) | 镜像会话主物理显示器 | 是 | 有物理登录会话,想看物理屏内容 |
extend | mutter 创建虚拟显示器(如 Meta-0) | 否 | 无头/远程登录会话,或不想暴露物理屏内容 |
注意:extend 模式下 RDP 会话用的是独立的虚拟显示器,看不到物理屏上的实际内容;mirror-primary 才是"看物理屏"。
补充(2026-08-11 晚):extend 设置只对 SCREEN_SHARE(无参数)模式有意义。改用
--headless模式后 daemon 永远自建虚拟显示器,不存在Unknown monitor问题——用 headless 模式可以一劳永逸绕开本坑(详见下文「grd 的 4 种运行模式」)。且实测发现 dconf 的 extend 设置可能被 GUI 设置面板等重置回 mirror-primary(dev 上 20:17 被覆盖),headless 模式不受此影响。
Step 3e:物理会话 vs 远程会话 — SSH 无法创建物理会话(2026-08-11 补充)
问题:物理会话注销后,能否通过 SSH 重新创建 seat0 物理会话?
结论:不能。 loginctl unlock-session 只能解锁已存在的会话,没有创建会话的能力。物理会话(Seat=seat0 的 user 会话)只能由 GDM 在 seat0 上登录产生,路径只有三条:
- 物理机前键盘登录(最直接)
- GDM 自动登录(模拟物理登录):
然后# /etc/gdm3/custom.conf [daemon] AutomaticLoginEnable = true AutomaticLogin = petersudo systemctl restart gdm3生效 - 3389 RDP 登录:创建的是远程会话(
Remote=yes、无 seat),不是物理会话——但 RDP 用起来一样
重启 gdm3 的副作用(执行前想清楚):
- 终止 GDM 管理的所有会话:当前 RDP 会话、远程 greeter 全部断开
- 重启后自动登录产生新的 seat0 物理会话 → 用户级 daemon 需要
systemctl --user restart gnome-remote-desktop重新绑定 - 安全代价:自动登录后物理屏直接进桌面且一直保持登录,任何能碰到机器的人都能操作——比「物理屏停在登录界面」风险高
会话选择判断标准(loginctl list-sessions):
| 特征 | 会话类型 | 能否 unlock/lock |
|---|---|---|
Seat=seat0 + Class=user | 物理会话(解锁目标) | ✅ unlock-session / lock-session |
Class=user + Remote=yes + Type=wayland | RDP 远程桌面会话 | ❌ 从不锁定,无需操作 |
Class=greeter | GDM 登录界面 | ❌ 别碰 |
Class=manager | systemd 管理 | ❌ 别碰 |
实测案例:dev 的物理会话 803 注销后,seat0 只剩 greeter c27。此时 3390 用 extend 模式共享远程会话 4259 正常工作——物理会话对 RDP 使用不是必需的。
认证通过后如何切到用户级 daemon?重定向 + fd 传递(2026-09-11 源码核实,更正)
原文曾写「RDP Server Redirection,不是 fd 传递」「客户端重建连接到用户守护进程(3390)」,这个结论是错的。grd 48.1 源码(
grd-daemon-system.c/grd-daemon-handover.c)显示实际链路是:
- 系统守护进程向客户端发送
RDP_SERVER_REDIRECTION包(含 routing token、用户名、密码、证书,不含目标地址)- 客户端断开后重连回同一地址的 3389,携带 routing token;系统守护进程的 RDP server 以
incoming-redirected-connection信号识别 token(on_incoming_redirected_connection),暂存新 socket 并发TakeClientReady- 用户级 handover daemon 调
TakeClient→ 系统守护进程把该 socket 的 fd 通过 D-Bus(GUnixFDList,即SCM_RIGHTS)传给 handover daemon(on_handle_take_client)→ handover daemon 用grd_rdp_server_notify_incoming把连接注入自己的 RDP server- handover daemon 不监听任何端口(所以
ss里看不到它),只接收注入的连接也就是说:既有应用层重定向(让客户端重连并携带 token),也有内核级 fd 传递(交接 socket)——「不是 fd 传递」的说法不成立。日志中仍能看到
Sending server redirection。
Step 4:闪退排查 — NTLM MIC 不是 mstsc 独有的
机器 A 的推测是「mstsc 特有」。但机器 B 用的 Remmina(Linux)同样出现此问题:
[ERROR][com.winpr.sspi.NTLM] - [ntlm_read_AuthenticateMessage]: Message Integrity Check (MIC) verification failed!
[WARN][com.winpr.sspi] - [winpr_AcceptSecurityContext]: AcceptSecurityContext status SEC_E_MESSAGE_ALTERED [0x8009030F]
[ERROR][com.freerdp.core.transport] - [transport_accept_nla]: client authentication failure
结论修正:这是 grd 46 用户守护进程与 NLA (CredSSP) 的普遍不兼容,不限于 mstsc。需要通过系统守护进程的认证路径才能绕过——但要先解决白屏。最终选择了只用用户级独占 3389。
Step 5:Session creation inhibited(第二个锁屏坑)
凭证修复后 NTLM 通过了!但出现新错误:
gnome-remote-de: Failed to start remote desktop session:
GDBus.Error:org.freedesktop.DBus.Error.Failed: Session creation inhibited
[ERROR][com.freerdp.core.peer] - ERRINFO_RPC_INITIATED_DISCONNECT [0x00010001]
检查会话状态:
loginctl show-session 803 | grep LockedHint
# LockedHint=yes
dbus-send --session --dest=org.gnome.ScreenSaver --print-reply \
/org/gnome/ScreenSaver org.gnome.ScreenSaver.GetActive
# boolean true
根因:物理会话锁屏了(机器闲置一周多)。GNOME Remote Desktop 在会话锁定时拒绝创建远程桌面会话。
意外发现:SSH 远程登录可以直接解锁物理会话(同用户间 polkit 信任):
# 找到物理会话的 ID
loginctl list-sessions
# SESSION UID USER SEAT LEADER CLASS TTY IDLE SINCE
# 803 1000 peter seat0 38320 user tty2 yes 1 week ago
# 3 1000 peter - 2373 user - no -
# 4 1000 peter - 2393 manager - no -
# c3 110 Debian-gdm - 158722 greeter - no -
# 物理会话的特征:有 SEAT(seat0)、有 TTY(tty2)、CLASS=user
# SSH 等远程会话:SEAT 为 -、CLASS=user、TTY 为 -
# GDM greeter:CLASS=greeter
# 精准定位当前登录桌面的会话 ID:
loginctl list-sessions --no-legend | awk '$4 == "seat0" {print $1}'
# → 803
# 解锁
loginctl unlock-session 803
# LockedHint=no
如何区分会话 ID:
- 物理桌面会话:
SEAT=seat0、TTY=tty2、CLASS=user。这就是你要解锁的目标 - SSH 远程会话:
SEAT=-、TTY=-。不算物理会话,不需要也不能用来解锁 - GDM greeter:
CLASS=greeter、Remote=yes。系统守护进程为 RDP 连接创建的登录界面
无需物理接触。防止复锁:
# 禁止锁屏(注销/锁屏后 RDP 无法进入)
gsettings set org.gnome.desktop.screensaver lock-enabled false
# 禁止屏保空闲时自动激活(避免再次锁屏)
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
# 屏保/锁屏的空闲触发时间设为 0(永不超时)
gsettings set org.gnome.desktop.session idle-delay 0
# 接电源时永不自挂起
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
# 用电池时也永不自挂起
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing'
安全警告:这些设置会让物理控制台一直处于已登录状态。RDP 断开后,任何能接触到物理机器的人都能直接操作桌面。
也可以写一个 systemd 服务,在 RDP 会话断开时自动锁定(见下文「自动化」)。用系统级(远程登录/GDM greeter)天然没有这个问题——注销后回到登录界面。只用用户级(桌面共享)才有这个风险。
Step 6:headless 模式取舍
机器 B 初始以 --headless 模式运行(有 drop-in 配置文件):
# ~/.config/systemd/user/gnome-remote-desktop.service.d/headless.conf
[Service]
Type=simple
ExecStart=
ExecStart=/usr/libexec/gnome-remote-desktop-daemon --headless
在 headless 模式下仍然出现 Session creation inhibited。去掉 --headless 后,守护进程直接共享已有 Wayland 会话,配合解锁,连接成功。
机器 B 最终方案
方案演进(2026-08-11):下文「用户级非 headless 独占 3389」是当时(8月5-6日)的临时方案——它依赖 keyring 里有凭证 + 物理会话可镜像,两个条件都脆弱。后来通过源码确认 SCREEN_SHARE 模式凭证只读 keyring(credentials.ini 无效),并实测
--headless模式(凭证走 credentials.ini + 自建虚拟屏)是远程无 GUI 环境下最可靠的用户级方案。新机器推荐直接上「4 种运行模式」章节的 douhao 修复后组合(--system + --headless + --handover),本文方案仅作历史记录。
停掉系统守护进程,用户守护进程非 headless 独占 3389:
# 停系统守护进程
sudo systemctl stop gnome-remote-desktop.service
sudo systemctl disable gnome-remote-desktop.service
# 去 headless
rm ~/.config/systemd/user/gnome-remote-desktop.service.d/headless.conf
systemctl --user daemon-reload
# 确保凭证正确
vim ~/.local/share/gnome-remote-desktop/credentials.ini
# 重启
systemctl --user restart gnome-remote-desktop.service
客户端直连 3389,直接看到已有桌面,不经过 GDM 登录界面。
日常使用流程:SSH 解锁 → RDP 使用 → 主动锁屏
用用户级桌面共享时,每次使用 RDP 的完整流程:
1. SSH 登录,检查会话状态
# 查看所有会话
loginctl list-sessions
# 查看物理会话的锁定和空闲状态
loginctl show-session <ID> | grep -E "LockedHint|IdleHint|State|Type"
# LockedHint=yes → 已锁定,需要解锁
# IdleHint=yes → 空闲中(可能锁屏了)
# State=active → 会话在运行
# 一行命令判断是否需要解锁
loginctl show-session $(loginctl list-sessions --no-legend | awk '$4=="seat0"{print $1}') \
2>/dev/null | grep "LockedHint=yes" && echo "需要解锁" || echo "已解锁"
会话状态组合:
| LockedHint | IdleHint | 含义 | 动作 |
|---|---|---|---|
| yes | yes | 已锁屏且空闲 | 需要解锁 |
| yes | no | 已手动锁定 | 需要解锁 |
| no | yes | 已解锁但空闲(屏保无密码) | 可直接连 |
| no | no | 已解锁且活跃 | 可直接连 |
2. 解锁物理会话
SESSION_ID=$(loginctl list-sessions --no-legend | awk '$4=="seat0"{print $1}')
loginctl unlock-session "$SESSION_ID"
3. RDP 连接
用 Remmina / mstsc 连 主机:3389。
4. 使用完毕后,主动锁屏(重要!)
loginctl lock-session "$SESSION_ID"
不锁的话物理控制台会一直处于已登录状态,任何能碰到机器的人都能操作。
自动化:RDP 断开时自动锁屏
监听用户守护进程日志中的断连事件,触发自动锁定:
# 方式一:systemd path unit 监听 + shell 脚本
# ~/.local/bin/rdp-lock-on-disconnect.sh
#!/bin/bash
SESSION_ID=$(loginctl list-sessions --no-legend | awk '$4=="seat0"{print $1}')
journalctl --user -u gnome-remote-desktop.service -f --no-pager | while read line; do
if echo "$line" | grep -q "Network or intentional disconnect"; then
loginctl lock-session "$SESSION_ID"
fi
done
# ~/.config/systemd/user/rdp-auto-lock.service
[Unit]
Description=Auto-lock session on RDP disconnect
After=gnome-remote-desktop.service
[Service]
ExecStart=%h/.local/bin/rdp-lock-on-disconnect.sh
Restart=on-failure
[Install]
WantedBy=default.target
chmod +x ~/.local/bin/rdp-lock-on-disconnect.sh
systemctl --user daemon-reload
systemctl --user enable --now rdp-auto-lock.service
5. 可选:事后恢复锁屏策略
如果之前为了 RDP 禁用了自动锁屏,可以在断开后恢复:
gsettings set org.gnome.desktop.screensaver lock-enabled true
或者写进上述自动脚本,RDP 断开时一起执行。
远程登录架构与会话机制(GNOME 45+ / GDM,douhao 实测)
grd 的 4 种运行模式(2026-08-11 汇总,源码 grd-daemon.c 确认)
gnome-remote-desktop 只有一个二进制 gnome-remote-desktop-daemon,4 种运行模式由启动参数决定,互斥(count_trues(3, headless, system, handover) > 1 时直接报 Invalid option: More than one runtime mode specified 退出):
| 参数 | 运行模式 | daemon 类 | 角色 | 端口 | 凭证来源 |
|---|---|---|---|---|---|
| 无参数 | SCREEN_SHARE | 用户级 grd_daemon_user_new | 共享现有桌面(录制已有画面) | 3389(可避让) | 只读 keyring |
--headless | HEADLESS | 用户级 grd_daemon_user_new | 无头共享(自建虚拟显示器,不依赖物理屏) | 3389(可避让) | 读 credentials.ini |
--system | SYSTEM | 系统级 grd_daemon_system_new | GDM 远程登录(登录界面 → handover) | 3389 | 设置读 grd.conf;凭证走 GKeyFile credentials.ini(见下方更正) |
--handover | HANDOVER | 用户级 grd_daemon_handover_new | 登录接管(等 --system 移交连接) | 不监听 | one-time(system 传) |
凭证位置更正(2026-09-11,grd 48.1 源码核实):
--system的凭证不在 grd.conf,而是和--headless一样走 GKeyFile 后端(无 TPM 时),位于运行用户gnome-remote-desktop的数据目录:/var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/credentials.ini(grd-settings.c的create_credentials()里SYSTEM与HEADLESS共用create_headless_credentials())。grd.conf 只存 enabled/tls-cert 等设置。「写入后运行中的 daemon 不重载、需重启」的行为不变(文件在内存中缓存)。
对应 4 个 systemd unit(归属固定,不能混用):
| unit | 归属 | 运行用户 | ExecStart |
|---|---|---|---|
gnome-remote-desktop.service | system(sudo systemctl) | gnome-remote-desktop 用户 | --system |
gnome-remote-desktop.service | user(systemctl --user) | 当前用户 | (无参数)SCREEN_SHARE |
gnome-remote-desktop-headless.service | user | 当前用户 | --headless |
gnome-remote-desktop-handover.service | user | 当前用户 | --handover |
常见疑问(已实测):
- 每个服务都支持 sudo / --user 启动吗? 不行,归属是固定的:
--system只作为系统服务(sudo systemctl)跑;其余三个只作为用户服务(systemctl --user)跑。反过来用会因 bus 名/权限不匹配而失败。 --headless和--system能同时设置吗? 不能,互斥——同时给直接退出报Invalid option: More than one runtime mode specified(实测确认)。--handover与另外两个也同样互斥。--headless和--system的关系:--system的官方帮助文案就是 "Run in headless mode as a system g-r-d service"——它本质也是 headless(无物理屏的 GDM 登录界面),只是以系统用户跑、角色是登录。两者是不同角色(登录 vs 共享)而不是同角色的两个变体。- 端口冲突:SCREEN_SHARE 和 HEADLESS 都是用户级、默认都想绑 3389。
--system占用 3389 时,用户级自动避让到 3390(negotiate-port)。两个用户级(SCREEN_SHARE + HEADLESS)不能同时跑(抢同一端口),unit 里Conflicts=gnome-remote-desktop.service保证互斥。
"远程登录"和"桌面共享"不是并列的两种服务,而是流程的两段:
远程登录(--system, 3389)→ 看到 GDM 登录界面 → 输密码 → handover(--handover)→ 进入你的桌面
桌面共享(无参数 或 --headless, 3390)→ 直接看到你的桌面画面(不需要经过登录界面)
本博客各次配置实际用的模式对照:
| 配置场景 | --system | 无参数 | --headless | --handover | 说明 |
|---|---|---|---|---|---|
| 机器 A 方案 A1(推荐) | ✅ 3389 | ✅ 3390 | — | ✅ | 标准双守护进程 |
| 机器 B 初始(headless 试错) | — | — | ✅ 3389 | — | 遇到 Session inhibited |
| 机器 B 最终方案(用户级独占) | — | ✅ 3389 | — | — | 共享 803 物理会话 |
| douhao(正常参照) | ✅ 3389 | ✅ 3390 | — | ✅ | 一直正常 |
| dev 当前(修复后) | ✅ 3389 | ✅ 3390 | — | ✅ | 白屏修复后 |
| douhao 修复后(本次) | ✅ 3389 | — | ✅ 3390 | ✅ | 绕开 keyring 死结 |
| douhao 现状(2026-09-11) | ✅ 3389 | — | ✅ 3390 SCREEN_SHARE | ✅ | 改回共享物理桌面(mirror-primary),见下方更新 |
为什么 douhao 修复后改用
--headless:SCREEN_SHARE 模式的凭证只从 keyring 读,而 keyring 在无显示环境(SSH)锁定/创建时必然挂起——3390 永远Credentials are not set。--headless模式读 credentials.ini,完全不碰 keyring,是远程无 GUI 环境下唯一可靠的 3390 路径(实测 3390 认证通过、画面正常)。
2026-09-11 更新(douhao 现状):douhao 的 3390 已改回 SCREEN_SHARE(
mirror-primary,共享物理桌面)——需求从「无头共享」变成「看到物理屏」。代价是回到 keyring 路径,但并非不可用:物理会话活跃且 login keyring 已解锁时,SSH 里带上XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESS(GDBus 会自动回退到$XDG_RUNTIME_DIR/bus)执行grdctl rdp set-credentials可正常写入,且写入即时生效(daemon 每次连接现查 keyring,无需重启);headless 读 credentials.ini 才需要重启 daemon。注意物理会话被踢后 keyring 会随会话内 daemon 退出而重新锁定,需登录后解锁(keyring 密码=创建时的登录密码)。另外:screen-share-mode是 GSettings 绑定的活配置,改完对新连接立即生效,同样不需要重启 daemon。
双守护进程 + handover
douhao 上系统级与用户级守护进程并存,各管一段:
| 进程 | 角色 |
|---|---|
gnome-remote-desktop-daemon --system(系统级) | 管理 GDM 登录界面(greeter) 的 RDP——"远程登录"端 |
gnome-remote-desktop-daemon + --handover(用户级) | 登录后接管连接,共享用户桌面 |
完整链路:RDP 连 3389 → 看到 GDM 登录界面 → 输密码 → 登录瞬间 handover 把连接交给用户级 daemon → 无缝进入真实桌面。
会话列表解读(loginctl,实测会话)
c8 Debian-gdm greeter active wayland local seat0 tty1 ← 本地登录界面,永远挂着
c9 Debian-gdm greeter active wayland remote - - ← 第一次 RDP 连接的 greeter
c10 Debian-gdm greeter active wayland remote - - ← 第二次 RDP 连接的 greeter
3670 peter user active wayland remote - - ← peter 已登录的 GUI 会话
- 每个 RDP 连接 = 一个独立远程 greeter 会话(c9、c10…),不复用,登录 handover 后也不销毁(GDM 保留),连几次就几个
- 本地 seat0 greeter 永远 active:物理屏没人登录它就在——"有 greeter = 没登录"的判断是错的
- 判断是否已登录 GUI:看 peter 有没有
active的wayland/x11user 会话(loginctl list-sessions | awk '$3==peter && $4==user && $6==wayland'),而不是看 greeter 是否在场 - 远程会话(含 RDP 的 greeter 和用户会话)无 seat(显示
-);本地物理会话才有 seat0
渲染链路:服务器端 Wayland 渲染
远程登录界面不是 X11 转发,而是服务器端完整渲染:
RDP 客户端 ←── RDP(H.264 帧编码 + 输入事件) ──→ grd 系统守护进程
│
GDM 按需拉起无 seat 的 greeter 会话(c9/c10)
│
里面跑 Mutter(Wayland 合成器),渲染进虚拟输出
│
grd 用 Mutter ScreenCast API 捕获帧 → 编码 → 推给客户端
客户端鼠标键盘 → RemoteDesktop API 反向注入合成器
登录后同理:handover 后 grd 用户守护进程捕获用户会话合成器的画面。
虚拟输出的底层技术(douhao 实测)
douhao 的 card0-Virtual-1 不是 VKMS,而是 virtio-gpu——douhao 本身是 QEMU/KVM 虚拟机:
/sys/class/drm/card0/device/uevent: DRIVER=virtio-pci, PCI_ID=1AF4:1050(virtio-gpu)
lsmod: virtio_gpu
| 技术 | 本质 | 场景 |
|---|---|---|
| VKMS(drm/vkms) | 纯内核虚拟 KMS,无 PCI 设备 | 无头服务器测试 KMS 路径 |
| virtio-gpu(douhao 用) | 半虚拟化 GPU,宿主提供真实帧缓冲,自带虚拟 EDID | 虚拟机标准虚拟显卡 |
| Mutter 虚拟显示器 | 合成器内部软件虚拟输出,不进内核 DRM | gnome-remote-desktop 无头会话 |
640x480 之谜:virtio-gpu 的 Virtual-1 连接器自带 EDID,支持 5120x2160 等大模式——但 RDP greeter 画面可能只有 640x480。原因是远程 greeter 的合成器走 Mutter 内置虚拟显示器(软件路径),其尺寸由 RDP 客户端连接时请求的桌面尺寸决定(grd 系统守护进程默认 EXTEND 模式,用客户端 Client Core Data 的 DesktopWidth/Height 建虚拟屏;Mutter 按 PipeWire 协商尺寸创建/调整虚拟显示器,见 meta-screen-cast-virtual-stream-src.c 的 create_virtual_monitor/ensure_virtual_monitor),不经过 KMS 连接器的模式列表。更正(2026-09-11):原文写「Mutter 默认 640x480」不准确——实测客户端请求 /size:1000x700 时服务端渲染内容就是 1000x700,尺寸由客户端请求驱动;greeter 显示 640x480 是因为客户端请求了 640x480(把客户端分辨率调大即可)。两条渲染路径不冲突:连接器支持大分辨率,不代表 greeter 会用它。
多显示器与显示器热插拔(2026-08-18 补充)
共享哪个屏:mirror-primary 只镜像主显示器
- 双屏/多屏时,
mirror-primary共享的只是主显示器(Primary)的画面,副屏上的窗口/内容不在 RDP 会话里——远程端看不到,没有其他"平面"可看。 - 想看到副屏内容只有三条路:① 客户端启用多显示器(RDP 多 monitor 会话,见下);② 换成
extend/--headless共享虚拟屏(但那是另一块屏,不是副屏内容);③ 把窗口拖到主屏。 - 主显示器判定:GNOME 设置 → 显示器 → 主显示器(或
xrandr --listmonitors的*标记)。
客户端侧选项(Remmina 1.4.43 实测)
Remmina 没有 mstsc 那种字面叫"使用所有显示器"的选项,等价物在 Basic(基本)标签页下半段("智能卡 PIN"一组之后、"分辨率"之前,需向下滚动):
| 选项 | 含义 |
|---|---|
| Enable multi monitor | 每个本地显示器映射为一个独立 RDP monitor(≈ mstsc 的"使用所有显示器",全屏生效) |
| Force multi monitor when toggled | 强制多显示器模式 |
| Span screen over multiple monitors | 多屏拼接成一个虚拟大桌面(单一逻辑 monitor,窗口可跨屏拖动) |
| List monitor IDs | 逗号分隔指定共享哪些显示器(如 0,1),可精确排除某个屏 |
服务端(grd 0.4.x / GNOME 46+)侧本身支持多显示器:每个物理显示器独立 ScreenCast 流 → RDP 多 monitor。两端都要开启才生效。
显示器移除(热插拔)对共享的影响
- 本地会话不受影响:mutter 的"输出配置"与"桌面会话"解耦。拔掉显示器 → DRM connector 断开 → mutter 把该输出从 monitor 配置移除(唯一输出被移除则进入"无活动输出"状态),窗口/进程原样保留;插回 → 热插拔事件 → mutter 自动重建输出恢复显示。
- 共享会话受影响:
mirror-primary镜像的是主物理显示器,主屏被拔 → 没有可镜像的输出 → 远程端黑屏/断开(与 Step 3d 的Unknown monitor场景同类,只是触发原因不同:物理会话注销 vs 拔屏)。 - 想"拔屏后共享不断":用
extend或--headless(自建虚拟屏),但看不到物理屏内容——虚拟屏与物理屏是同一会话的不同输出(扩展布局,窗口各自落位),两边内容不镜像。 - 结论:要"远程看到和本地用户同一画面"(远程调试场景),必须物理屏存在 +
mirror-primary;headless/虚拟屏是"换一块屏",不是"镜像本地"。
踩坑总表
| 问题 | 现象 | 机器 | 根因 | 解法 |
|---|---|---|---|---|
| xrdp 占端口 | 开关弹回 | A | xrdp-sesman 开机抢先绑 3389 | systemctl stop/disable xrdp xrdp-sesman |
| 0x907 证书 | mstsc 硬拒绝 | A | Windows 缓存了旧证书指纹 | 清注册表 / 换地址重连 |
| GDM 白屏 + CPU 飙升(greeter 无虚拟显示器) | 3389 连上白屏、无渲染错误 | B | 手动默认路由 → NM PrimaryConnection 空 → gsd-sharing 判 OFFLINE → --handover 不启动 → greeter 无虚拟显示器 | nmcli con up <连接> 让 NM 接管默认路由(见 Step 3b);systemd-networkd 同理(见 Step 3c) |
| NTLM MIC 失败 | Remmina 闪退 | B | 用户守护进程 NLA 普遍不兼容 | 用系统守护进程 / 3389 登录路径 |
| Session inhibited | 认证通过后断开 | B | 物理会话锁屏 | loginctl unlock-session |
| 凭证不持久化 | NTLM 永远失败 | B | grdctl 写 keyring 失败(SSH 无 keyring daemon),回退 credentials.ini 的写入路径不工作 | 通过 3389 GDM 登录一次解锁 keyring 后写凭证,或改用 --headless 模式(凭证走 credentials.ini) |
| SSH 可远程解锁 | — | B | 同用户会话间 polkit 信任 | loginctl unlock-session <id> |
| 3390 Unknown monitor | 认证通过后 RPC 断开 | B | mirror-primary 模式无物理屏可镜像(物理会话注销后) | 短期:screen-share-mode extend;根治:改用 --headless 模式(永远自建虚拟屏,见 Step 3d) |
| 3390 凭证死结 | 认证被拒 Credentials are not set,grdctl 写 keyring 挂起 | B/douhao | SCREEN_SHARE 模式凭证只读 keyring;keyring 锁定/无显示环境无法写入 | 改用 --headless 模式(读 credentials.ini,不碰 keyring) |
| keyring 解锁弹窗密码错 | RDP 登录后弹解锁框,输登录密码报错 | B | 8月6日 排查期间重建的 login.keyring 密码与当前登录密码不一致(GDM 登录创建的 keyring 密码=当时登录密码,改密码不同步) | 备份后删除 login.keyring,下次 GUI 登录以当前密码重建(丢其中存的少量密码) |
| 系统级凭证不重载 | 3389 改密码后认证仍 STATUS_LOGON_FAILURE | A | 运行中的系统 daemon 不重读 grd.conf | 写凭证后 systemctl restart gnome-remote-desktop.service(先确认无活跃 gdm 会话,见下行) |
| 重启系统 daemon 踢物理会话 | 脚本跑着跑着物理桌面退出、自动登录恢复 | A | 活跃 RDP greeter 被杀 → GDM on_display_added 断言失败 → seat 会话被踢 | 默认不重启系统 daemon(--restart 显式才重启,先清空 user=gdm 会话) |
| SCREEN_SHARE 改密码不生效 | 3390 还是旧密码 | A | SCREEN_SHARE 凭证只读 keyring,credentials.ini 无效 | grdctl rdp set-credentials 写 keyring(物理会话活跃时即时生效) |
| 共享模式被悄悄切回 extend | 3390 连上看不到物理屏 | A | 「无物理会话自动回退 extend」在会话切换空档误判 | 默认 mirror-primary,只有显式 --extend 才配置 |