QOwnNotes 在 Raspberry Pi OS 上的安装指南:多版本 APT 仓库配置与 Secret Service 密钥环问题排查
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
命令逐条解析
四组命令结构完全相同,仅仓库子路径不同,其含义如下:
wget … Release.key -O - | sudo apt-key add -:从子仓库根目录拉取 OBS 生成的Release.key(GPG 公钥)并通过apt-key导入系统信任列表,使apt能校验该仓库Release文件及其包索引的签名,防止软件包被篡改。echo 'deb … /' >> /etc/apt/sources.list.d/qownnotes.list:以deb行格式把仓库地址追加到独立的 sources 文件qownnotes.list中。采用独立文件(而非直接改/etc/apt/sources.list)的好处是:日后想移除仓库,只需删除这一个文件即可,不会污染系统主配置。sudo apt-get update:刷新包索引,拉取新仓库的软件包列表与校验信息。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/todoCalendarCalDAVPasswordai/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标志防止重复执行)。
五、安装后的验证建议
- 确认软件包来源:
apt-cache policy qownnotes,应显示版本对应的候选版本并列出download.opensuse.org仓库源; - 启动程序:终端执行
QOwnNotes,或在桌面菜单中查找 QOwnNotes 图标(.desktop文件由软件包部署到usr/share/applications); - 确认密钥环状态:在 Nextcloud/ownCloud 设置页保存一次连接密码后查看日志。若仍出现
Could not write secret to keychain,可先用busctl --user list | grep -E 'secrets|kwallet'(或qdbus旧工具)确认会话 D-Bus 上是否存在org.freedesktop.secrets/org.kde.kwalletd*服务;不存在则回到第四节的方案补齐组件并重启桌面会话; - 无头场景说明:若树莓派长期以无桌面方式运行,该警告属于预期行为而非故障,凭据将按 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 本地加密存储,之后补齐密钥环时启动阶段会自动完成一次性迁移。