跳转到主要内容

持久化 IAM 撤销

2026-09-16 源码状态:#191#192 已合入 main,但不在已发布的 Server 20260903 中。它们改变 IAM 持久状态,要求协调升级。执行时使用升级恢复手册,安全边界见 SN-2026-013

SILO 保留已删除 IAM 记录的版本,使离线站点重新连接后不能恢复旧身份或旧授权。覆盖内置用户、服务账号、组、策略文档,以及用户、STS 父身份和组各自命名空间中的策略映射。内置用户删除后,即使有意重建同名用户,旧服务账号、STS 凭证与组授权也不能因此复活。

排序与持久化

删除记录保留在原 IAM 配置路径,包含源时间戳、Deleted 及必要的 RevokedBefore。身份删除记录不含 secret key 或 session token。普通 IAM 列表和授权隐藏已删除记录。对象存储与 etcd 都用分布式锁串行化每个路径的版本比较及写入;接收端持久化源时间戳,不替换成自己的接收时间。

旧事件不能覆盖新版本;相同时间戳下,删除胜过活记录。有意的本地重建取得比已有删除更晚的版本。重建之后才到达的旧用户或组删除,会保留撤销边界,同时保留较新的活记录。重复收到已应用的 tombstone 不重写或推进版本。冷启动加载后同样遵守这些规则。

用户或组版本写入是删除提交点,映射与子凭证清理随后发生。清理失败不能撤销已提交的撤销;API 返回清理错误,同时通知同后端节点重载提交状态。错误不表示身份仍然有效。同一撤销可以重试;重试期间应暂停同名重建与其它 IAM 修改。发生已提交但清理失败的错误时,admin 处理器不会执行即时跨站点 hook,跨站点传播依赖正常删除 healing 重试。同后端删除通知重读当前共享状态,因此延迟通知不会删除随后重建的身份。etcd 节点也通过 watch 接收持久变化。通知或 watch 失败属于最终传播,不是瞬时分布式撤销事务。

必须同步节点时钟并监控偏移。排序使用源墙钟时间戳,同一路径的本地写入保证单调推进,但不建立不同站点并发写入的因果顺序,也不保证相同时间戳的冲突活更新得到确定性的统一结果。

用户、子凭证与组

重建用户保留 RevokedBefore。新服务账号和内置 STS 签发携带签名的 siloParentRevocation claim,记录签发时已知的父身份撤销边界。编辑或重放旧子凭证不会更新该 claim;即使旧子凭证自身更新时间更晚,它仍然失效。针对重建父身份新签发的子凭证继续有效。加载与写入凭证时从已验证 token 中读取 claim。

较新的复制服务账号快照可以替换同 access key 的旧服务账号,包括有意更换所有者或 secret。本地重复创建仍被拒绝。快照保留 disabled 状态和账号自身的撤销边界,旧映射不能附着到新账号。等版本重试重载提交身份,不重写记录。周期性的活记录 healing 比较源版本,即使公共状态摘要没变,也会检测差异;禁用身份也可以作为 healing 来源。

服务账号快照保留绝对过期时间;接收站点不会重新套用本地新凭证的最短有效期。较新但已过期的快照仍覆盖旧 key,认证拒绝它,正常加载把它收集为持久服务账号 tombstone。缓存或 claim 加载失败返回错误等待重试,不能提前确认成功;替换提交后立即移除旧缓存 secret。与内置用户或缓存 STS 身份的 key 冲突会报错,需要管理员明确处理,复制不能悄悄改变凭证类型。完全相同时间戳的并发冲突服务更新仍可能在不同站点留下不同赢家,状态摘要不会解决该冲突。

组的每个成员都有独立 MemberGrants 时间戳。改变另一个成员或组启用状态不会重新授予其他所有成员权限。有效成员授权必须晚于用户及组各自保留的撤销边界;列表与策略计算使用同一有效成员关系。对端快照保留原授权时间,包括未知的旧格式授权时间,不能用最近快照时间替旧身份制造新授权。显式的新管理授权可以恢复访问。

这不是所有组成员编辑的通用冲突解决协议。继承的活组快照合并会添加成员,不会修复离线期间漏掉的普通成员移除。站点断开时从活组移除成员仍是已知限制;这里解决的是用户或组删除及同名重建的边界。

保留与过期

永久身份、组、策略文档与映射没有自动 tombstone TTL。断开的站点或旧备份可能很晚才返回;重放确认只是减少重复工作的优化,不是回收历史的授权。

不可变 STS token 自然过期时,物理删除 token-key 记录及旧式 token-key 映射,不制造永久 tombstone。提前撤销的 STS 则保留至 token 过期加现有时钟偏差余量;不能用更晚的事件时间戳重放同一已撤销 token 使其复活。etcd 使用过期 lease;对象存储在既有凭证加载与清理中回收过期 STS tombstone。未知过期时间的记录保守保留。

可选清理采用短锁预算,尽力写入。失败时,本轮停止可选回收、记录错误,仍加载正常用户;过期凭证保持拒绝并保留持久版本,下次加载再试。健康清理没有固定记录配额。可复用 STS 父身份策略映射不继承 token TTL。外部 IdP 禁用属于提前撤销,不是自然过期,其缓存 STS 与服务账号也纳入清理。过期服务账号的 key 可复用,旧版本可能永久有效,因此仍保留持久版本。

直接的跨站点逐 token RevokeTokens 交付不是本次新增保证。内置父身份删除的保证来自持久父边界,包括删除节点缓存中尚未出现的子凭证。

Healing、失败与运维成本

每个进程启动一个 healing 循环。丢失分布式 leader lease 时暂停,重新取得后恢复,不永久退出;配置重载不会增加循环。取得领导权以及每轮结束后等待 30 秒,初始锁重试和端点恢复还会增加延迟,因此 30 秒不是收敛 SLA。

普通 IAM 加载器维护不含 secret 的删除与保留边界内存索引。Healing 使用该索引,不每轮再完整遍历一次 config/iam/;既有的启动与刷新加载仍扫描持久记录,包括 tombstone。

每个 peer 每批最多接收 128 条记录。发送方记住对方已确认的路径/版本,无关更新不重置进度。失败批次保留待重试,后续独立批次仍可前进;响应丢失会导致安全的幂等重放。每轮有时长上限,超时前成功的确认不会丢失。协议报告节点名与进程实例;负载均衡在已知实例间切换不重置确认,新实例首次出现时保守作废旧确认。在恢复持久状态前必须停止相关进程,避免恢复后复用旧进程的确认。

稳定状态仍需遍历、排序保留的内存集合并检查 peer 协议状态,只是在确认后抑制重复删除 PUT。内存成本随路径与 peer 数增加,启动存储读取随历史增长;本次不提供通用历史压缩。升级后,旧活记录如果曾被接收站点赋予不同时间戳,可能触发一轮集中协调写入;维护窗口要预留其存储写与同后端通知成本。

指标包括 revocation_recordsrevocation_heal_failuresrevocation_heal_duration_millisrevocation_heal_last_success_timestamp_seconds,错误同时记录日志。断网、大量积压、锁争用及失败重试可能需要多轮。

缓存凭证查询只检查内存父版本索引,不额外访问存储。STS 签发与冷凭证加载仍读持久父版本。版本 I/O 与分布式锁等待释放 IAM 缓存锁,另一个本地 writer mutex 保持写顺序;这些操作都有受限 context,包括 etcd 锁及 lease 清理。

协议与支持的升级方式

服务端自有版本化路径为 /minio/admin/v3/site-replication/peer/iam-revisions,携带源版本、成员授权时间以及独立用户/组撤销项,不改变 admin 客户端 SDK 或 S3 API。旧服务器拒绝这个路径,发送方报告失败,不回退到丢失元数据的协议。旧 IAM 路由保留可读性,只提供尽力兼容,不让旧 peer 自动获得新保证。

所有参与服务器必须升级,包括未启用站点复制但共享 IAM 后端的节点。不支持同后端新旧混跑或滚动降级:旧二进制不能正确解释 tombstone 和签名父边界。请安排维护窗口:

  1. 暂停 IAM 修改,隔离状态未知的离线站点和备份。
  2. 备份各站点完整 IAM 存储及所需加密材料;活记录 admin 导出不包含删除历史,不能替代完整备份。
  3. 停止每个后端的全部节点,更换二进制并用新版本启动。所有参与站点升级完成后才能依赖新撤销语义。
  4. 检查 IAM 加载、站点复制错误和撤销收敛,验证代表性的旧凭证被拒绝、新签发凭证有效。
  5. 显式处理升级前已丢失的撤销历史:在仍有旧记录的站点删除它们,按批准状态重建旧离线 peer 后再准入,不能为了查看删除凭证而接回未知旧快照。

旧服务器为重建父身份签发的凭证缺少签名边界,必须由新服务器重发。没有保留撤销历史的父身份保持既有凭证行为。

未修改的内置策略仍不能本地删除。管理员显式覆盖内置策略后再删除覆盖,会留下持久删除,重载不再自动恢复内置策略。需要恢复时执行显式策略创建。本地删除不存在策略仍幂等,不创建新 tombstone;对端未知删除则保留版本。

回滚前停止并隔离站点,评估备份以来的变化,再恢复兼容状态。旧备份本身会丢掉后续撤销,开放访问前必须重新协调或换 key。不能在线删 tombstone,也不能只转换活记录来让旧二进制启动。Server、客户端、Console、包与部署验收分别成立。

回归与性能验证

定向测试包括 iam-revocation_test.goiam-revision_test.goiam-revision-lock_test.goiam-revision-boundary_test.goiam-replication-protocol_test.goiam-credential-retention_test.goiam-peer-reload_test.goiam-replay_test.go。用 SILO_TEST_IAM_REVOCATION_ETCD 指向可丢弃 etcd 才会包含对应后端生命周期、锁与边界测试;它们使用隔离 key 命名空间。

BenchmarkIAMCachedCredentialBenchmarkIAMSetTempUser 可与修复前源码比较。BenchmarkIAMRevisionConvergedHealing 覆盖 1,000/10,000 条保留记录,测量稳定状态的索引/网络工作,并断言没有重复 PUT;模拟 peer 不测持久追赶吞吐。BenchmarkIAMColdLoadExpiredServices 覆盖 100/1,000 个过期可复用凭证,使用 -benchtime=1x。还需要实际多站点签名 S3/STS 验证以及部署规模与延迟测量。

错误与可观测性

权威撤销提交后,依赖清理失败可能让 admin 删除返回 HTTP 500。这不是旧身份仍可使用的证据。暂停 IAM 修改和同名重建后重试预期撤销,再逐站点验证旧凭证与重新签发的凭证。持久父版本不可读或签发无法提交时,STS 通过内部错误路径拒绝签发,包括 STSInternalError,不能猜测一个缺失边界后放行。

抓取每个进程需认证的 /minio/metrics/v3/cluster/iam

指标 含义
minio_cluster_iam_revocation_records 本进程索引中保留的删除记录与父边界
minio_cluster_iam_revocation_heal_failures 进程启动后的失败协调轮数
minio_cluster_iam_revocation_heal_duration_millis 最近一轮耗时
minio_cluster_iam_revocation_heal_last_success_timestamp_seconds 最近一轮成功的 Unix 时间

索引是逐进程的,同后端节点求和会重复统计。Healing 要求启用站点复制并取得领导 lease;只有共享后端、没有站点复制的部署不运行站点 healing,三个 healing 指标为零并不表示故障。一个 leader 的健康计数也不能证明所有 peer 的凭证检查通过。

为什么更简单的方案不成立

直接物理删除会丢掉离线 peer 所需的排序证据。只比较子凭证更新时间也不够:父删除后编辑旧子凭证,会显得更新并可能恢复访问;签发时的签名父边界则不会随编辑改变。用接收时间代替源版本破坏源顺序,可能把重试变成新修改。实现保留源时间戳,只为有意本地写入单独推进版本。给永久 tombstone 加 TTL,或者仅恢复活记录导出,都会丢掉阻止重放的历史。

契约以版本化源码指标定义为依据。本文取代原服务端仓库的 IAM 设计记录,不是生产恢复验收证书。