SSH 登录下解锁 GNOME Keyring 的两种方法

八月 11, 2026 [网络与安全] #SSH #gnome-keyring #Linux

SSH 登录下解锁 GNOME Keyring

背景

通过 SSH(无图形会话)远程登录一台 GNOME 桌面机器时,gnome-keyring-daemon 的 login collection 通常处于锁定状态——它只在用户通过 GDM/PAM 图形登录时自动解锁。

锁定的后果之一:gnome-remote-desktop 这类依赖 libsecret 同步读取凭据的程序,在初始化时调用 secret_service_async_initable_init_async 永不返回,导致 RDP/VNC 服务卡死、不监听端口。

判断 keyring 是否锁定

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
busctl --user get-property org.freedesktop.secrets \
  /org/freedesktop/secrets/collection/login \
  org.freedesktop.Secret.Collection Locked
# b true  → 锁定
# b false → 已解锁

注意:SSH 会话默认可能没有 DBUS_SESSION_BUS_ADDRESS,必须先指到用户会话总线(路径中的 1000 是 uid,按实际替换)。

方法一:gnome-keyring-daemon --unlock(推荐)

gnome-keyring-daemon 自带 --unlock 选项,可从 stdin 读取密码,无需交互弹窗,适合 SSH:

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
echo '你的keyring密码' | gnome-keyring-daemon --unlock \
  --control-directory=/run/user/1000/keyring

--control-directory 必须与正在运行的 keyring 实例一致(通常是 /run/user/<uid>/keyring),否则会起一个新的 daemon 而不是解锁现有实例。

方法二:Secret Service Unlock + Prompt

标准 D-Bus 流程,调用 org.freedesktop.Secret.Service.Unlock 拿到 prompt 路径,再用 Prompt.Complete 提交密码:

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
import dbus
bus = dbus.SessionBus()
svc = bus.get_object('org.freedesktop.secrets', '/org/freedesktop/secrets')
iface = dbus.Interface(svc, 'org.freedesktop.Secret.Service')

objects, prompt = iface.Unlock(dbus.Array(
    [dbus.ObjectPath('/org/freedesktop/secrets/collection/login')],
    signature='o'))

if prompt != '/':
    p = bus.get_object('org.freedesktop.secrets', prompt)
    dbus.Interface(p, 'org.freedesktop.secrets.Prompt').Complete('你的keyring密码')

关键:密码是哪一个?

修改 / 重置 keyring 密码(SSH)

unlock 的前提是密码正确;如果密码忘了,或想设成一个已知密码,SSH 下也有办法,无需图形界面。

修改密码(知道当前密码)

方式 A:GUI(需要图形桌面会话,如 Seahorse)

在桌面上打开「密码和密钥」(Seahorse)→ 左侧「密码」→ 右键 login → 「更改密码」,输入旧密码 + 新密码即可。

SSH 下想用 GUI 的话,可以 ssh -X/ssh -Y 转发 X11,或借助远程桌面的图形会话。

方式 B:纯 SSH 无 GUI

用 Secret Service 的 ChangeWithPrompt 方法,走 D-Bus,流程和「方法二」类似:

import dbus
bus = dbus.SessionBus()
col = bus.get_object('org.freedesktop.secrets',
                     '/org/freedesktop/secrets/collection/login')
iface = dbus.Interface(col, 'org.freedesktop.Secret.Collection')

old = '当前keyring密码'   # keyring 已解锁时可以为空字符串
new = '新密码'
prompt = iface.ChangeWithPrompt('/', old, new)

if prompt != '/':
    p = bus.get_object('org.freedesktop.secrets', prompt)
    dbus.Interface(p, 'org.freedesktop.secrets.Prompt').Complete(new)

ChangeWithPrompt 返回 / 表示直接改成功;返回其他路径时需按「方法二」的方式 Prompt.Complete 确认。改完后,--unlock 用的密码也要换成新密码。

重置密码(忘记密码)

密码忘了无法找回,keyring 里保存的凭据会一并丢失,只能重建:

mv ~/.local/share/keyrings/login.keyring ~/.local/share/keyrings.bak

下次使用时 gnome-keyring-daemon 会创建一个新的空 login keyring;之后若图形登录,PAM 会把它的密码设回当前登录密码。

一个常见坑

SSH 下 passwd 改了账户密码,keyring 密码不会跟着变——pam_gnome_keyring 只在图形登录时同步。所以会出现「账户密码正确、--unlock 却失败」,此时 keyring 密码仍是改密码前的那个,需要用旧密码先解锁再改。

验证

busctl --user get-property org.freedesktop.secrets \
  /org/freedesktop/secrets/collection/login \
  org.freedesktop.Secret.Collection Locked   # b false = 成功

解锁后,被卡住的 gnome-remote-desktop 守护进程需重启才能继续初始化:

systemctl --user restart gnome-remote-desktop.service

总结