QOwnNotes 在 Raspberry Pi OS 上的安装指南:多版本 APT 仓库配置与 Secret Service 密钥环问题排查

原创2026-10-12 14:07:511,983 阅读
文章标签:桌面应用知识管理富文本插件系统跨平台

QOwnNotes 在 Raspberry Pi OS 上的安装指南:多版本 APT 仓库配置与 Secret Service 密钥环问题排查

本文以 QOwnNotes 官方安装文档 raspberry-pi-os.md(韩文版 与其内容一致)为核心,完整覆盖 Raspberry Pi OS 12/11/10 与 Raspbian 9.0 四个版本的 openSUSE Build Service(OBS)仓库安装步骤,并结合仓库内的 Debian 打包文件与加密服务源码,深入讲解安装命令的每一步含义、软件包的实际内容,以及 Could not write secret to keychain 警告的产生原理与解决方案。读完后,你可以独立完成树莓派上的 QOwnNotes 部署,并理解其密码存储(系统密钥环 + 本地加密回退)的工作机制。

一、安装方式背景:按发行版划分的 OBS 子仓库

QOwnNotes 官方在 openSUSE Build Service 的 home:/pbek:/QOwnNotes 项目下,为不同 Linux 发行版分别维护了构建子仓库。对于树莓派,文档中出现的 Raspbian_12、Raspbian_11、Raspbian_10、Raspbian_9.0 四个子路径,分别对应不同版本的 Raspberry Pi OS(旧称 Raspbian),子仓库内的 .deb 软件包针对各自的系统库版本独立构建。

系统版本 仓库子路径
Raspberry Pi OS 12 Raspbian_12
Raspberry Pi OS 11 Raspbian_11
Raspberry Pi OS 10 Raspbian_10
Raspbian 9.0 Raspbian_9.0

这种"一发行版一子仓库"的组织方式在仓库中可以得到佐证:

  • obs/qownnotes.spec 是 SUSE 系发行版使用的 RPM spec 文件,其文件头注释明确提到"Check for Linux distribution version numbers here: openSUSE Build Service cross distribution howto",即利用 OBS 的跨发行版构建能力为各发行版产出软件包;
  • src/debian/control 定义了 Debian/Ubuntu 系的软件包元数据:包名为 qownnotes、Architecture: any、Section: editors,其 Depends 字段列出了运行时依赖——Qt5 各组件(libqt5core5a、libqt5gui5、libqt5sql5-sqlite、libqt5websockets5、libqt5x11extras5 等)以及 libsecret-1-0 和 openssl;
  • src/debian/rules 中 export QT_SELECT=qt5 说明该打包流程基于 Qt5 构建。

注意 libsecret-1-0 这一依赖:它就是后文"系统密钥环"功能的运行库(libsecret),也是文档中提示部分要求安装 libsecret-1-0 的原因。

二、按版本执行的安装命令

以下命令与官方文档完全一致,请按实际系统版本选择对应小节执行。

1. Raspberry Pi OS 12

第一步,信任仓库(导入 GPG 签名密钥):

wget http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_12/Release.key -O - | sudo apt-key add -

第二步,添加仓库并安装 QOwnNotes:

sudo bash -c "echo 'deb http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_12/ /' >> /etc/apt/sources.list.d/qownnotes.list"
sudo apt-get update
sudo apt-get install qownnotes

如需手动挑选软件包文件,可直接访问浏览目录(原文档的"Direct Download"链接):http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_12

2. Raspberry Pi OS 11

信任仓库:

wget http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_11/Release.key -O - | sudo apt-key add -

添加仓库并安装:

sudo bash -c "echo 'deb http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_11/ /' >> /etc/apt/sources.list.d/qownnotes.list"
sudo apt-get update
sudo apt-get install qownnotes

手动下载目录:http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_11

3. Raspberry Pi OS 10

信任仓库:

wget http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_10/Release.key -O - | sudo apt-key add -

添加仓库并安装:

sudo bash -c "echo 'deb http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_10/ /' >> /etc/apt/sources.list.d/qownnotes.list"
sudo apt-get update
sudo apt-get install qownnotes

手动下载目录:http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_10

4. Raspbian 9.0

信任仓库:

wget http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_9.0/Release.key -O - | sudo apt-key add -

添加仓库并安装:

sudo bash -c "echo 'deb http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_9.0/ /' >> /etc/apt/sources.list.d/qownnotes.list"
sudo apt-get update
sudo apt-get install qownnotes

手动下载目录:http://download.opensuse.org/repositories/home:/pbek:/QOwnNotes/Raspbian_9.0

命令逐条解析

四组命令结构完全相同,仅仓库子路径不同,其含义如下:

  1. wget … Release.key -O - | sudo apt-key add -:从子仓库根目录拉取 OBS 生成的 Release.key(GPG 公钥)并通过 apt-key 导入系统信任列表,使 apt 能校验该仓库 Release 文件及其包索引的签名,防止软件包被篡改。
  2. echo 'deb … /' >> /etc/apt/sources.list.d/qownnotes.list:以 deb 行格式把仓库地址追加到独立的 sources 文件 qownnotes.list 中。采用独立文件(而非直接改 /etc/apt/sources.list)的好处是:日后想移除仓库,只需删除这一个文件即可,不会污染系统主配置。
  3. sudo apt-get update:刷新包索引,拉取新仓库的软件包列表与校验信息。
  4. sudo apt-get install qownnotes:安装软件包并自动解析依赖。

补充说明:apt-key 机制在新版本的 Debian 系发行版中已属于遗留做法(新发行版更倾向于将 GPG 密钥 dearmor 后放入 /usr/share/keyrings 并通过 signed-by 选项引用)。本文照录官方文档使用的 apt-key 写法,因为该流程与 OBS 提供的标准接入方式一致,且在 Raspberry Pi OS 12 等目标系统上仍然可用。

三、仓库里装的是什么:软件包内容的源码级佐证

安装完成后,根据 installation 目录总览 的说明,直接执行 QOwnNotes 命令即可启动程序("If not stated otherwise you can run QOwnNotes afterward by executing QOwnNotes")。

软件包内部署了哪些文件,可以从 src/debian/qownnotes.install 确认:

  • 主程序安装到 usr/bin(即 QOwnNotes 可执行文件);
  • 桌面入口文件 PBE.QOwnNotes.desktop 安装到 usr/share/applications;
  • 16×16 至 512×512 各尺寸的 PNG 图标与可缩放 SVG 图标安装到 usr/share/icons/hicolor/ 对应目录;
  • 各语言的 QOwnNotes_*.qm 编译翻译文件安装到 usr/share/qt5/translations,其中包括 zh_CN、zh_TW 等中文翻译。

当前仓库的主程序版本号定义在 src/version.h 中(#define VERSION "26.10.3"),可供核对安装后 QOwnNotes 的实际版本。此外,spec 文件 obs/qownnotes.spec 中 %files 段与 Debian install 文件列出的内容基本对应(%{_bindir}/QOwnNotes、hicolor 图标、PBE.QOwnNotes.desktop、Qt5 翻译目录),说明树莓派仓库中的包与仓库内这套打包定义同源。

四、Could not write secret to keychain:产生原理与解决方案

这是官方文档在 Raspberry Pi OS 安装页末尾特别提示的常见问题:如果 QOwnNotes 日志中出现 Could not write secret to keychain,说明系统缺少可用的 Secret Service 密钥管理服务,需要安装对应桌面环境的密钥环组件并重启桌面会话。

1. 这条日志从哪里来

该警告直接出自加密服务的写入逻辑。在 src/services/cryptoservice.cpp 中,writeSecret() 通过 QKeychain::WritePasswordJob 向系统密钥环写入凭据,失败时打印:

qWarning() << "Could not write secret to keychain:" << key << job->errorString();

写入失败后,storeSecret() 会立即降级:

// src/services/cryptoservice.cpp#L494-L506
QString CryptoService::storeSecret(const QString &key, const QString &text) {
    if (writeSecret(key, text)) {
        return markerForKey(key);
    }

    // A missing keychain was already reported once in isKeychainAvailable()
    if (isKeychainAvailable()) {
        qWarning() << "Could not store secret in keychain, falling back to legacy encryption:" << key;
    }

    return legacyEncryptToString(text);
}

也就是说:密钥环不可写时,程序并不会中断,而是回退到本地加密存储(legacy encryption)——这正是文档所说 "QOwnNotes will fall back to legacy encryption if the desktop keychain is unavailable" 的源码依据。

2. QOwnNotes 把哪些敏感信息交给密钥环

从 src/services/cryptoservice.cpp 中的两组密钥列表可以看到,走密钥环(encryptedSettingsKeys)的包括:

  • networking/proxyPassword(代理密码)
  • ownCloud/password(ownCloud/Nextcloud 连接密码)
  • ownCloud/todoCalendarCalDAVPassword
  • ai/groq/apiKey、ai/openai/apiKey(AI API Key)

另有 plaintextFallbackSettingsKeys 组(WebSocket Server 令牌、Web App Client 令牌、ai/mcpServerToken、languageToolApiKey):它们同样尝试写入密钥环,但如果历史上以明文保存,则在无法写入密钥环时允许保留明文回退值。

此外,云连接数据库(cloudConnection 表中的 password 字段)和脚本设置的变量(settings_variables_json)中的旧格式密钥,也会在启动时通过 migrateSecrets() 迁移进密钥环。

3. "密钥环可用"的判定机制(Linux 专属探测)

这是树莓派场景的关键。Qt 自带的 QKeychain::isAvailable() 在 Linux 上只检查 libsecret 库能否加载,而这并不等于系统里真的有一个密钥服务在运行。因此 QOwnNotes 增加了 D-Bus 层面的探测,见 src/services/cryptoservice.cpp:

// isDBusSecretServiceReachable()
static const QStringList serviceNames = {
    QStringLiteral("org.freedesktop.secrets"),  // Secret Service API(gnome-keyring 等)
    QStringLiteral("org.kde.kwalletd6"),        // KWallet (Qt6)
    QStringLiteral("org.kde.kwalletd5"),        // KWallet (Qt5)
};

程序会在会话 D-Bus 上检查这三个服务是否已注册、或是否可通过 D-Bus 激活机制拉起(该探测只查询服务名,不触碰任何密钥条目,因此永远不会触发解锁弹窗)。两项检查(D-Bus 服务可达 + QKeychain::isAvailable())全部通过才认为密钥环可用,结果按应用运行周期缓存一次;判定为不可用时,打印的是另一条警告:"No system keychain is available, secrets will be stored with the legacy encryption"(见 src/services/cryptoservice.cpp#L408-L421)。

另外,所有密钥环操作都在同步事件循环中执行并受超时保护:executeKeychainJob() 使用 keychainTimeout() 秒作为时限(默认 DefaultKeychainTimeout = 2 秒,设置项 keychainTimeout 的取值被限制在 1~120 秒之间,见 src/services/cryptoservice.cpp#L397-L402),避免密钥服务无响应时卡死界面。密钥环中的服务名默认为 QOwnNotes(带会话后缀时形如 QOwnNotes-<session>,见 src/services/cryptoservice.cpp#L63-L76),便于多开时隔离。

4. 按桌面环境安装缺失组件

依据官方文档的提示,针对树莓派上常见的两类桌面环境:

GNOME 及其他基于 Secret Service 的桌面:

sudo apt install gnome-keyring libsecret-1-0 seahorse
  • gnome-keyring:提供 gnome-keyring-daemon,在会话 D-Bus 上实现 org.freedesktop.secrets;
  • libsecret-1-0:应用程序侧访问密钥服务的客户端库;
  • seahorse:图形化管理工具(查看/解锁密钥环条目)。

KDE Plasma:

sudo apt install kwalletmanager

KWallet 守护进程(kwalletd5/kwalletd6)实现同样的 Secret Service 接口,kwalletmanager 提供图形化管理界面。

安装完成后重启桌面会话(注销重新登录或重启树莓派),让密钥守护进程随会话启动。之后 QOwnNotes 的 D-Bus 探测即可命中对应服务,凭据将正常写入系统密钥环,首次保存密码时可能弹出一枚解锁/授权提示,属正常行为。

5. 回退方案:legacy 本地加密是什么

如果树莓派运行在无桌面环境(纯 SSH/无头模式)或确实不想安装密钥环组件,QOwnNotes 会自动回退到 legacy 存储。从源码结构看,legacy 存储基于仓库自带的 SimpleCrypt 库(见 src/libraries/simplecrypt/simplecrypt.h):

  • 使用 64 位密钥,CryptoService 构造时若配置中没有 cryptoKey 会自动随机生成并保存(src/services/cryptoservice.cpp#L204-L220);
  • 简单加密结果以 Base64 字符串形式存回配置文件,与密钥环引用(形如 qtkeychain: 前缀 + Base64Url 编码的密钥名)互斥共存,decryptToString() 会根据前缀自动选择走密钥环读取还是本地解密(src/services/cryptoservice.cpp#L281-L289);
  • 需要指出的是,SimpleCrypt 头文件自身就声明 "The encryption provided by this class is NOT strong encryption"——它只是防止密码被一眼看出,并不具备对抗针对性破解的能力。因此从安全角度看,安装密钥环组件(让密码进入经过会话保护的系统密钥环)始终优于长期依赖回退存储;
  • 反过来,一旦日后补齐了密钥环,启动时的 migrateSecrets() 会自动把 settings、云连接数据库、脚本变量三处的旧格式(SimpleCrypt 密文)密钥解密后写入密钥环,并把配置值改写为 qtkeychain: 引用(src/services/cryptoservice.cpp#L596-L605),迁移是单向的、一次性的(_migrationStarted 标志防止重复执行)。

五、安装后的验证建议

  1. 确认软件包来源:apt-cache policy qownnotes,应显示版本对应的候选版本并列出 download.opensuse.org 仓库源;
  2. 启动程序:终端执行 QOwnNotes,或在桌面菜单中查找 QOwnNotes 图标(.desktop 文件由软件包部署到 usr/share/applications);
  3. 确认密钥环状态:在 Nextcloud/ownCloud 设置页保存一次连接密码后查看日志。若仍出现 Could not write secret to keychain,可先用 busctl --user list | grep -E 'secrets|kwallet'(或 qdbus 旧工具)确认会话 D-Bus 上是否存在 org.freedesktop.secrets / org.kde.kwalletd* 服务;不存在则回到第四节的方案补齐组件并重启桌面会话;
  4. 无头场景说明:若树莓派长期以无桌面方式运行,该警告属于预期行为而非故障,凭据将按 legacy 加密保存在本地配置中,功能不受影响。

小结

  • QOwnNotes 在 Raspberry Pi OS 上通过 OBS 项目 home:/pbek:/QOwnNotes 的发行版专属子仓库(Raspbian_12 / Raspbian_11 / Raspbian_10 / Raspbian_9.0)以标准 APT 方式安装:导入 Release.key → 写入 /etc/apt/sources.list.d/qownnotes.list → apt-get update && apt-get install qownnotes;
  • 软件包由 src/debian/control 与 obs/qownnotes.spec 定义的打包流程产出,运行依赖 Qt5 组件、libsecret-1-0 与 openssl;
  • Could not write secret to keychain 的根因是系统缺少 Secret Service/KWallet 守护进程:GNOME 系安装 gnome-keyring libsecret-1-0 seahorse,KDE 系安装 kwalletmanager,重启桌面会话即可恢复;
  • 密钥环不可用时 QOwnNotes 自动回退到 SimpleCrypt 本地加密存储,之后补齐密钥环时启动阶段会自动完成一次性迁移。
登录后查看全文
QOwnNotes