配置存储
配置存储在各设备之间同步CLI配置。配置在设备端使用服务器永远不会持有的内容加密密钥(CEK)加密。安全性一节准确说明了这能防住什么、不能防住什么。
解锁方式(密钥槽)
每个存储只有一个CEK,但会为每种解锁方式独立包装,类似LUKS的密钥槽机制。任意一个密钥槽都能打开同一把密钥,且可以随时添加或移除密钥槽,无需重新加密您的数据:
| 方式 | 说明 | 备注 |
|---|---|---|
| 通行密钥 | 支持PRF扩展的WebAuthn通行密钥 | 最强选项;由硬件保障安全 |
| 主密码 | 您自选的密码,经PBKDF2-SHA256拉伸(60万次迭代) | 无需支持PRF的硬件即可使用;同时支持无浏览器的CLI注册 |
| 恢复码 | 系统生成的 RC1-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX 格式代码 | 仅在创建时显示一次,请妥善保存 |
每种方式都走同一条流程:密钥槽产出一个密钥片段,与服务器持有的密钥片段结合后解开CEK。任何一方单独持有的片段都不足以解密,密钥槽的密钥片段永远不会到达服务器。三种方式中,主密码是最弱的一种:服务器掌握了离线尝试破解密码所需的一切,因此密码必须足够强。通行密钥和恢复码密钥槽没有这个弱点。
密钥槽在门户的配置存储页面统一管理。只希望允许硬件解锁的组织可以启用要求通行密钥策略,该策略会拒绝并撤销整个存储中所有非通行密钥的密钥槽。
解锁是按设备进行的:在新设备上解锁一次后,日常的CLI操作(push/pull)就无需再触碰通行密钥或输入密码。
先决条件
- 您的账户已启用双因素认证
- 若使用通行密钥方式:需要支持PRF的通行密钥提供商,例如FIDO2安全密钥(如YubiKey)、iCloud Keychain、Google Password Manager、1Password或Dashlane
- 浏览器:Chrome 133+、Edge 133+、Firefox 130+或Safari 17+
PRF要求仅适用于通行密钥这一种密钥槽。主密码和恢复码方式在任何受支持的浏览器上均可使用。
设置
- 在侧边栏导航至配置存储,然后点击设置配置存储
- 需求检查表验证您的浏览器、双因素认证和会话状态
- 选择第一种解锁方式,然后点击 创建配置存储:
- 通行密钥:适用于支持 PRF 的提供商。你需要触碰安全密钥两次,一次用于注册,一次用于派生加密密钥。
- 主密码:适用于任何浏览器,也适用于 Bitwarden 等不支持 PRF 的通行密钥提供商。
- 可选的 恢复码:只显示一次,需要在创建存储前保存好。
- 设置完成。CLI 会把解锁密钥保存在操作系统的钥匙串中。
通行密钥可以稍后在 Config Storage 页面添加。请至少保留两种解锁方式,以免丢失或不受支持的验证器把你锁在外面。
PRF提供商兼容性
| 提供商 | PRF支持 | 平台 |
|---|---|---|
| YubiKey / FIDO2安全密钥 | ✅ | Windows 11、macOS、Linux |
| iCloud Keychain | ✅ | macOS 15+、iOS 18+ |
| Google Password Manager | ✅ | Android |
| 1Password | ✅ | Android、iOS |
| Dashlane | ✅ | 跨平台 |
| Bitwarden扩展 | ❌ | 请改用主密码 |
| Windows Hello | ❌ | 不支持 |
无浏览器的CLI注册
没有浏览器的机器(服务器、CI运行器、执行器守护进程)可以通过主密码方式注册到已有的存储中:
rdc config remote enable --password
前提条件:
- 已通过门户配置好主密码密钥槽(配置过程需要浏览器持有密钥,因此这一步本身无法在无浏览器环境下完成)
- 拥有具备
config:enroll权限范围的API令牌,用于验证此次调用
注册操作只读取数据:CLI获取该密钥槽的公开KDF参数和已包装的密钥,在本地推导出密码密钥片段,并在设备上解开CEK。该操作只授予设备解密和同步配置的能力,不会修改存储本身。
启用与离线读取
rdc config remote enable 会将当前配置连接到存储。如果存储为空,启用操作会用您当前的本地配置为存储播种:本地资源会作为存储的第一个版本被推送上去,然后再拉取回来,以验证这一往返过程无误。如果存储中已有内容,启用操作会与其进行协调,而不是直接覆盖(如果确实存在分歧,除非您传入 --force,否则操作会中止)。
启用之后,配置会保留一份完整的读取缓存,采用与任何本地配置相同的机制进行静态加密,因此即使无法连接账户服务器,存储依然可用:
- 读取可离线进行。 缓存内容会附带过期警告输出到stderr,并标注缓存版本和时间戳(
cachedVersion/cachedAt)。 - 写入需要服务器,且在无法连接时会安全失败(fail closed)。 不存在离线写入队列:无法到达服务器的写入会直接报错并指明该服务器。只要写入命令成功返回,改动就已经在服务器上了。
- 来自两台机器的并发编辑通过拉取-重放-再推送(pull-replay-repush)解决:服务器只接受建立在被替换版本之上的推送,落败的推送会在最新副本上重放,因此别处的同时编辑不会覆盖您的更改。
- 本地配置(没有
remote块的配置)不受此影响,完全可以离线工作。
同步内容
配置中的所有内容都会同步,包括每个仓库的网络ID,但以下几个设备本地字段除外:schemaVersion、version、remote、encryption、renetPath、credentials.masterPasswordVerifier,以及登录相关字段account.accountServer和account.e2ePublicKey。登录和退出登录是按设备进行的。
版本与恢复
服务器为每个配置保留最近50个版本。
rdc config remote versions
rdc config remote restore <version>
恢复操作会把旧内容作为新版本发布在当前版本之上,版本号绝不会倒退。每台设备会在下次拉取时获得恢复后的内容,恢复操作也会记录在审计日志中。
密钥轮换
轮换存储的CEK会以新的世代重新包装密钥:
- 恢复码在轮换后总是失效,请在轮换完成后重新生成并保存新的恢复码
- 主密码密钥槽只有在轮换向导中重新输入密码才能保留
- 停留在旧世代的密钥槽会被报告为过期,而不是报出难以理解的解密错误
- 其他成员的配置令牌会被吊销,仍持有旧密钥的设备会被提示通过
rdc config remote enable重新启用
成员管理
配置存储按组织划定范围。成员通过Web门户管理:
- 查看成员:配置存储 → 成员
- 添加成员:目前仅通过CLI(Web界面计划中)
- 移除成员:点击成员页面上的移除按钮(需要双因素认证 + 重新认证)
安全保护措施防止移除最后一个活跃成员或移除自己。
存储中的配置还会按团队划定范围,但这种划分是服务器端的访问控制,而非加密隔离:整个组织共用同一个CEK加密所有团队的配置,而某个成员能读取哪些团队的配置,由服务器强制执行。
安全性
受保护的内容。 存储的配置能够抵御服务器存储遭到入侵,以及被动的运营者。每个blob都绑定到其所属的存储、配置、团队和版本,因此服务器无法把某个配置的blob替换成另一个的,也无法在不被察觉的情况下篡改它。
不受保护的内容。 若运营者提供了恶意的门户代码,就能在浏览器中读取密钥。运营者还可以对从未见过更新版本的设备,扣留其最新版本。
| 服务器能做的 | 服务器不能做的 |
|---|---|
| 查看配置ID、团队、版本号、时间戳、blob大小、某个配置提交了多少字段及每个字段的类型,以及客户端IP地址 | 查看机器、仓库或存储的名称(这些已被隐去)或任何配置值 |
| 拒绝、延迟或删除配置及其历史记录 | 在设备察觉不到的情况下,把一个配置的内容当作另一个提供,或对其进行编辑 |
| 向从未见过更新版本的设备提供旧版本 | 向CLI提供比它已经见过的版本更旧的版本(CLI会拒绝) |
| 离线尝试破解主密码 | 打开通行密钥或恢复码密钥槽 |
其他防护措施:
- 分割密钥:解密需要密钥槽的密钥片段(设备端)和服务器的密钥片段两者兼备
- 删除需要知情:从配置中移除一个已提交的值,需要证明曾经知道该值,因此只有部分访问权限的代理无法悄悄删除字段
- 轮换令牌:每次请求都会轮换配置令牌;令牌绑定到首次使用时的IP地址,并在7天后过期
- 撤销:移除成员会同时删除其密钥槽和令牌;对方已经拉取过的内容仍留在其设备上,而CEK轮换会让其保留的密钥无法再打开之后的版本
故障排除
| 错误 | 原因 | 修复方法 |
|---|---|---|
| PRF not supported | 认证器缺少PRF扩展 | 使用YubiKey、iCloud Keychain、1Password或Dashlane,或添加一个主密码密钥槽 |
| X25519 not supported | 浏览器版本过旧 | 更新至Chrome 133+、Edge 133+、Firefox 130+或Safari 17+ |
| Already configured | 您的组织已存在存储 | 访问/account/config-storage进行管理 |
| Config storage not configured | 服务器缺少blob存储 | 联系管理员配置R2/RustFS |
| Token expired | 7天无活动,或设备更换了网络 | 会通过登录自动刷新;如果没有已保存的登录,请运行rdc subscription login或rdc config remote enable |
| Config came back at an older version | 服务器返回的副本比该设备已经见过的版本更旧 | 本地没有任何改动;请重试,如果持续出现请报告 |
| Cannot remove last member | 将永久锁定存储 | 先添加另一个成员 |
| Stale slot | 密钥槽早于上一次密钥轮换 | 重新添加该密钥槽(恢复码必须在每次轮换后重新生成) |