RustFS KMS 管理 API 契约:路由、IAM 动作与密钥列表分页规则全解
【免费下载链接】rustfs🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs
导读:本文以 RustFS 官方运维文档 docs/operations/kms-admin-contract.md 为骨架,系统讲解 KMS 管理面(Admin API)的完整契约:每一个/rustfs/admin/v3/kms/*端点对应的 IAM 动作、风险等级、是否为 per-key 授权,以及GET /kms/keys密钥列表的严格分页规则。无论你是要接入 CLI、控制台还是自动化脚本,还是需要编写 IAM 策略来收紧 KMS 权限,读完后都能准确判断每个路由的授权语义,并写出可正确翻页、可容忍损坏密钥记录的客户端。
适用范围与事实来源
本文适用于将客户端(CLI、控制台、自动化脚本)接入 RustFS KMS 管理端点的场景,核心需要明确三件事:每条路由所需的 IAM 动作、风险等级、以及密钥列表的分页规则。
文档的事实来源(均可直接在本仓库中核对):
- rustfs/src/admin/route_policy.rs:每个路由的 action 与风险等级定义,由 rustfs/src/admin/route_registration_test.rs 断言保证路由注册与策略一致;
- crates/kms/src/backends/mod.rs:
DEFAULT_LIST_KEYS_PAGE_SIZE、MAX_LIST_KEYS_PAGE_SIZE、list_keys_page_size等分页常量与切页逻辑; - crates/kms/src/snapshots/ 与 rustfs/src/admin/handlers/snapshots/:各端点的响应形状由快照测试锁定。
所有 KMS 管理端点的 wire 前缀均为/rustfs/admin/v3。其中GET /kms/status与GET /kms/service-status返回不同类型的响应;/kms/status上的capabilities字段是增量式(additive)且可选的。Per-key 列表示该路由是否针对其命名的密钥进行授权(详见 Per-key KMS 授权);值为no表示该路由匹配调用者策略中的任意 KMS 资源。
端点矩阵:完整路由、动作与风险等级
下表是全部 KMS 管理端点的权威清单,来自 rustfs/src/admin/route_policy.rs 中ADMIN_ROUTE_POLICY_SPECS的 KMS 段落(route_policy.rs 第 816-939 行),与原文档逐条对应:
| Method and endpoint | IAM action | Risk | Per-key | Notes |
|---|---|---|---|---|
POST /kms/configure | kms:Configure | high | no | 持久化到集群存储、切换本节点、向对端广播尽力而为的重载 |
POST /kms/reconfigure | kms:Configure | high | no | 与 configure 契约相同 |
POST /kms/start | kms:ServiceControl | high | no | |
POST /kms/stop | kms:ServiceControl | high | no | |
POST /kms/reload | kms:ServiceControl | high | no | 重新读取持久化配置而不重新提交密钥;复用 configure 的响应形状 |
GET /kms/status | kms:ServiceControl | sensitive | no | 后端类型加能力矩阵 |
POST /kms/status | kms:ServiceControl | high | no | 兼容路由;不是客户端命令 |
GET /kms/service-status | kms:ServiceControl | sensitive | no | 携带cluster_config指纹与consistent标志 |
GET /kms/config | kms:Configure | sensitive | no | 包含运维路径;展示前必须脱敏 |
POST /kms/clear-cache | kms:ClearCache | high | no | 返回KmsClearCacheResponse({status,message}) |
POST /kms/keys | kms:Configure | high | no | 创建密钥与配置共用 Configure 动作 |
GET /kms/keys | kms:ListKeys | sensitive | no | 见下文密钥列表契约 |
GET /kms/keys/{key_id} | kms:DescribeKey | sensitive | yes | ?impact=true可选返回配置引用报告 |
DELETE /kms/keys/delete | kms:DeleteKey | critical | yes | JSON body;force_immediate还需要confirm_key_id和服务端RUSTFS_KMS_ALLOW_IMMEDIATE_DELETION开关 |
POST /kms/keys/cancel-deletion | kms:DeleteKey | high | yes | |
POST /kms/keys/enable | kms:EnableKey | high | yes | |
POST /kms/keys/disable | kms:DisableKey | high | yes | |
POST /kms/keys/rotate | kms:RotateKey | high | yes | 受 KMS 后端安全属性 中的轮换约束约束 |
POST /kms/keys/rekey | kms:Rekey | high | no | 批量 DEK 重包裹扫描;集群范围,见 KMS 批量 Rekey 契约 |
GET /kms/keys/rekey/status | kms:Rekey | sensitive | no | |
POST /kms/keys/rekey/cancel | kms:Rekey | high | no | |
POST /kms/keys/update-description | kms:UpdateKeyDescription | high | yes | |
POST /kms/keys/tag | kms:TagResource | high | yes | |
POST /kms/keys/untag | kms:UntagResource | high | yes | |
POST /kms/generate-data-key | kms:GenerateDataKey | high | yes | 响应携带 base64 明文数据密钥;绝不可在 UI 或 CLI 中展示 |
GET /kms/backup | kms:Backup | sensitive | no | 仅状态与就绪信息;不含 KEK 材料 |
POST /kms/backup | kms:Backup | high | no | 仅返回backup_id与元数据 |
POST /kms/restore/dry-run | kms:Restore | sensitive | no | 预检;不写任何数据 |
POST /kms/restore | kms:Restore | high | no | 需要confirm_backup_id与confirm_conflict_policy |
POST /kms/restore/abort | kms:Restore | high | no | 需要confirm_target_key_dir |
POST /kms/create-key,POST /kms/key/create | kms:Configure | high | no | mc遗留别名,等价POST /kms/keys;密钥名来自key-id查询参数(mc的形态)或name标签,两者同时携带且值不同时以400拒绝 |
GET /kms/describe-key,GET /kms/key/status | kms:DescribeKey | sensitive | yes | GET /kms/keys/{key_id}的遗留别名 |
GET /kms/list-keys | kms:ListKeys | sensitive | no | GET /kms/keys的遗留别名;列表契约相同 |
注意上表中的端点均省略了统一前缀/rustfs/admin/v3,实际请求形如POST /rustfs/admin/v3/kms/configure。
风险等级背后的设计意图
风险等级不是随意标注的,源码注释直接给出了理由,理解它有助于在接入时正确对待每个端点:
DELETE /kms/keys/delete是唯一一个critical级别的 KMS 路由。源码注释(route_policy.rs 第 868-878 行)明确指出:销毁主密钥会让所有在其下加密的对象永久不可读,且服务端没有任何机制能恢复这些对象。等待窗口(pending-deletion)与 cancel-deletion 是唯一的恢复路径——这正是POST /kms/keys/cancel-deletion只保持high级别的原因;POST /kms/keys/rekey使用独立的集群范围kms:Rekey动作,而不是复用某个 per-key 动作。源码注释(route_policy.rs 第 900-901 行)说明:rekey 扫描会跨 bucket 遍历并重写对象元数据,因此需要独立的集群级授权;kms:Backup与kms:Restore同样使用独立动作,因为它们作用于所有密钥的材料,而非某个具体密钥(route_policy.rs 第 915-916 行);- 密钥创建与后端配置共用
kms:Configure,这意味着“创建密钥”在权限模型上是集群管理员的职责,而不是密钥管理者的职责(见 Per-key KMS 授权 的说明)。
各端点组的职责划分
从端点矩阵可以清晰地划分出四组职责:
- 服务控制组(
kms:ServiceControl):start/stop/reload/status/service-status,负责 KMS 服务的启停、配置重载与状态查看。其中service-status返回的cluster_config携带每个节点的配置指纹与consistent标志,可用于检测集群内 KMS 配置分叉(详见 KMS 后端安全属性); - 密钥生命周期组(per-key 动作):创建(
kms:Configure)、DescribeKey、EnableKey、DisableKey、RotateKey、DeleteKey、UpdateKeyDescription、TagResource/UntagResource,以及批量Rekey; - 数据密钥组:
generate-data-key(kms:GenerateDataKey),是 SSE-KMS 数据路径的核心动作; - 备份恢复组(
kms:Backup/kms:Restore):backup、restore/dry-run、restore、restore/abort,以及clear-cache(kms:ClearCache)。
Per-key 授权:谁可以对哪个密钥做什么
端点矩阵中的Per-key列是本契约的核心概念之一:值为yes的路由会针对请求命名的那个密钥进行授权。密钥标识取自请求体的key_id字段,回退到keyId查询参数。而值为no的路由(如GET /kms/keys)匹配调用者策略中的任意 KMS 资源。
RustFS 的 KMS 授权基于身份策略(identity policy),一条语句可以限定其适用的密钥,因此授予kms:DisableKey不再意味着对集群内所有密钥生效。KMS 资源使用与 S3 相同的空账号 ARN 形态:
| 模式 | 匹配 |
|---|---|
arn:aws:kms:::key/<key_id> | 恰好该密钥 |
arn:aws:kms:::key/app-* | 所有以app-开头的密钥 id |
arn:aws:kms:::* | 所有密钥 |
arn:aws:kms:::alias/<name> | 保留;别名解析落地前不匹配任何内容 |
编写策略时有几条硬规则:混用kms:与s3:动作的语句会被拒绝;携带 KMS 资源但动作不是 KMS 动作的语句会被拒绝;Deny优先于Allow。仓库内置了三个 KMS 角色模板:KMSKeyAdministrator(密钥生命周期治理)、KMSKeyUser(读写 SSE-KMS 对象的工作负载)、KMSAuditor(仅可见性)。三者均不授予kms:Configure、kms:ServiceControl、kms:ClearCache、kms:Backup或kms:Restore,这些集群管理权限保留给consoleAdmin——这与本文端点矩阵中 per-key 动作与集群动作的划分完全一致。详细的资源语法、模板清单与 SSE-KMS 数据路径强制(RUSTFS_KMS_ENFORCE_SSE_KEY_POLICY)参见 Per-key KMS 授权。
密钥列表契约:GET /kms/keys
GET /kms/keys(以及遗留别名GET /kms/list-keys)共享同一个列表契约。这条契约是接入 KMS 客户端时最容易出错的部分,下面逐条展开。
limit:缺省、非法值与上限
limit是可选参数。缺省时服务端应用DEFAULT_LIST_KEYS_PAGE_SIZE(100);- 一旦提供,就必须能解析为非负整数:
limit=abc、limit=-1以及无值的limit都会以400拒绝,而不会静默按缺省值处理; limit=0是合法的“请求空页”请求,返回空页;- 任何超过
MAX_LIST_KEYS_PAGE_SIZE(1000)的页大小都被按 1000 提供——响应带truncated和可用的next_marker,因此只要客户端一直翻页直到truncated为 false,仍能到达每一个密钥; - 客户端绝不能假设返回的页就是它请求的大小。
源码实现印证了这些规则。在 crates/kms/src/backends/mod.rs 第 163-172 行 中,两个常量被明确定义:
pub(crate) const DEFAULT_LIST_KEYS_PAGE_SIZE: u32 = 100; pub(crate) const MAX_LIST_KEYS_PAGE_SIZE: u32 = 1_000;而list_keys_page_size(mod.rs 第 244-249 行)实现了解析逻辑:Some(0)返回None表示“请求了零个密钥”,大于上限的值被 clamp 到上限而非拒绝。该函数带有单元测试覆盖(mod.rs 第 1001-1041 行),包括limit=0、u32::MAXclamp 到 1000 等边界。
常量注释解释了为何必须设上限:一页并不是廉价的切片——列出的每个标识符都要消耗后端一次元数据查找(Local 上是磁盘读,Vault Transit 上是 HTTP 往返),无界的limit会把一次请求变成对密钥存储的无界扇出(fan-out)。上限在切页处统一应用,任何后端都无法绕过。
marker:不透明的游标
marker对客户端是不透明的:应把它当作一个原样回传的游标,绝不能当作可以自行构造的值。具体语义因后端而异:
- 在Local、Vault KV2、Vault Transit、Static后端上,它恰好是密钥标识符的排他下界(exclusive lower bound)——这正是分页能在列表过程中经受密钥创建/销毁而不出错的原因;
- 在AWS后端上,它是 AWS 自己的分页令牌,向其发送密钥 id 会被拒绝;
- 空
marker等价于没有 marker。
分页的实现位于paginate_keys(mod.rs 第 201-231 行):通过partition_point找到第一个大于 marker 的标识符作为起点,next_marker取当前页最后一个标识符。注释特别强调:marker 是标识符上的排他下界而非序列索引,因此密钥在排序中的任何位置被新增或删除——包括 marker 指向的密钥本身被删除——都不会导致列表跳过密钥或从头开始。
过滤器(filter)在切页之后应用,因此被过滤后的页可能很短——甚至为空——而后面仍有更多密钥。客户端必须翻页直到truncated为 false,而不是直到某页返回变短为止。
unreadable_key_ids:损坏密钥的诚实报告
unreadable_key_ids仅当服务端列出了某个密钥但其记录无法描述时出现——例如由更新版本构建写入的记录,或已损坏的材料。关键设计原则是:
- 这些标识符被报告而非省略,因此列表永远不会悄悄低估密钥集合;展示清单的客户端应将其标记为损坏而不是丢弃;
- 分页总是越过损坏密钥继续前进;
- 与具体密钥无关的失败(超时、
5xx、权限拒绝)仍会使整个列表失败,而不会出现在此字段中。
该字段的定义位于 crates/kms/src/types.rs 第 491-510 行 的ListKeysResponse结构:包含keys、next_marker、truncated和unreadable_key_ids四个字段。类型注释解释了其存在理由:无法解释的密钥记录绝不能从keys中悄然消失,否则库存会误读为“你没有这个密钥”,而删除扫描的普查也会基于一个从未完整看到的密钥集合进行。后端层面对应的分类逻辑在 mod.rs 第 266-276 行 的ListedKeyFailure枚举中:Vanished(密钥在扫描与读取之间消失,正常现象,丢弃并前进)与Unreadable(记录仍在存储中但当前构建无法解释,通过unreadable_key_ids上报而非省略或变成整页失败)。
响应形状由快照锁定:参见 rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_response_with_unreadable_keys.snap 与 rustfs__admin__handlers__kms_keys__tests__kms_admin_list_keys_api_response.snap。
一个刻意的错误而非报告:全空且不可读
有一个场景被刻意设计为错误而不是报告:一次覆盖了整个密钥集合的列表——即没有marker且truncated为 false——其中没有任何密钥可读。此时若返回空的keys数组,任何在此字段出现之前编写的客户端都无法区分“没有密钥的部署”和“全部损坏的部署”,而前者通常的处理方式是去新建密钥。因此这种情况返回500,并指名第一个失败;具体标识符记录在服务端日志中。
而截断的页或从 marker 恢复的页总是报告而非失败,所以损坏的密钥永远不会“卡住”它后面的密钥。
相关文档导航
本契约与以下运维文档紧密关联,接入或排障时可组合阅读:
- KMS 管理 API 契约(本文档):路由、动作与分页规则;
- Per-key KMS 授权:资源语法、内置角色模板、SSE-KMS 数据路径强制与迁移路径;
- KMS 后端安全属性:主密钥轮换、保留、销毁与升级顺序,各后端的能力与保密边界;
- KMS 批量 Rekey 契约:
POST /kms/keys/rekey批量重包裹作业的完整验收标准; - Vault KMS 认证手册:Vault 凭证来源、刷新与 fail-closed 窗口;
- KMS 可观测性手册:
KmsKeyRotationOverdue等告警与监控指标; - KMS 灾难恢复演练:密钥目录与备份恢复的演练流程。
接入时建议遵循的实践要点:客户端永远按truncated翻页;对unreadable_key_ids以“损坏”呈现而非丢弃;对GET /kms/status与GET /kms/service-status分别处理两种不同的响应类型;在展示GET /kms/config结果前先脱敏;generate-data-key返回的明文数据密钥绝不落入 UI 或日志。
【免费下载链接】rustfs🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考