代表性面试主题

系统设计面试:如何设计多租户 Secrets Rotation Service?

系统设计困难
OfferCC 编辑团队发布 更新

题干

请设计一个为多租户应用自动轮换数据库密码和 API 密钥的服务,并说明如何保证幂等、可恢复和不间断发布。

题目

设计一个多租户 Secrets Rotation Service。租户可以配置数据库密码、第三方 API 密钥或证书的轮换周期;服务负责生成新值、更新外部系统、写入版本库、逐步通知工作负载并停用旧值。请说明故障恢复、并发控制、审计、权限和回滚方案。

面试官考察点

  • 能否把轮换拆成可重试的状态机,而不是一个无法恢复的定时脚本。
  • 能否处理同一 secret 的并发任务、重复消息和租户之间的隔离。
  • 能否设计双版本或灰度发布,避免新值立即导致全量故障。
  • 能否说明旧值清理、泄露响应、审计证据与最小权限。

参考答案

核心对象包括 Secret、不可变 Version、RotationPolicy、Job 和 Lease。调度器按下一次轮换时间投递任务;工作队列按 secret_id 分区,消费者用带过期时间的租约取得所有权。数据库唯一约束 (secret_id, idempotency_key),让重试不会生成两次外部变更。

轮换状态可采用 scheduled → generating → external_updated → staged → rolling_out → verified → retired,每个状态保存外部请求标识、版本号和下一次重试时间。先在外部系统创建新凭据,再写入新版本并标记为 pending;工作负载通过版本引用或动态刷新逐步切换。健康检查、错误率和授权测试通过后,才把新版本提升为 current。

控制面保存元数据,秘密值放在专用加密存储中,应用只获得短时读取权限。每次状态变化写入不可篡改审计日志;日志中不出现秘密明文。回滚只切回仍有效的旧版本,不能把“撤销旧凭据”作为回滚后的默认动作。

架构草图

text
Scheduler -> Durable Queue -> Rotation Workers
     |              |              |
 Policy DB     Lease/Idempotency  External Provider
     |                             |
 Version Store + KMS        Rollout Controller -> Workloads
     |
 Audit Log / Metrics / Alerts

每个 worker 以租约续期,租约失效后其他 worker 才能接管。队列消息只携带 secret 标识和 job 标识;worker 从权限受限的版本存储读取值,避免把秘密放进消息、日志或指标标签。

关键流程

  1. 调度器创建带幂等键的 job,按租户配额排队。
  2. worker 取得租约,读取当前版本和轮换策略;若 job 已完成则直接返回。
  3. 生成新值并调用外部 provider,使用 provider 的幂等请求号。
  4. 写入 pending 版本,执行兼容性检查和小批量 rollout。
  5. 观察错误率、认证成功率和健康探针;达到阈值后提升 current。
  6. 等待所有消费者确认,再禁用并销毁旧版本;失败则重试或回滚。

每一步都持久化状态和外部响应。重启时从最后状态继续,而不是重新猜测外部系统是否已经更新。

常见误区

  • 只在 cron 中保存下一次时间,进程重启或重复触发后无法恢复。
  • 轮换完成就立即撤销旧值,忽略连接池、缓存和长连接的过渡时间。
  • 把秘密值放入队列、日志、追踪 span 或错误消息。
  • 用全局锁阻塞所有租户,或者没有租户级配额导致单租户耗尽 worker。
  • 把回滚理解为再次写入旧值,却不验证旧凭据仍未过期或未被撤销。

一致性与安全取舍

控制面可以使用强一致数据库保存状态机和唯一约束;通知与 rollout 使用至少一次投递,因此消费者必须幂等。版本读取可短暂缓存,但 current 版本变更和撤销事件必须有明确失效路径。租户权限应限制在自己的 secret、job 和审计记录,worker 只获得完成当前步骤所需的 provider 权限。

轮换周期不能只按固定时间决定,还要考虑密钥类型、暴露风险、外部 provider 限制和恢复窗口。NIST 的密钥管理建议强调使用期限与用途、保护等级和撤销流程应共同决定策略。

用故障注入验证:worker 在每个状态前后崩溃、消息重复、租约过期、provider 超时、部分实例 rollout 失败、数据库主从切换。断言同一 job 不会产生重复外部凭据,最终只有一个 current 版本,旧值在确认窗口结束后才撤销。再检查租户越权、审计脱敏和告警延迟。

  • AWS Secrets Manager 的 AWSPENDING/AWSCURRENT 轮换流程:版本标签和完成步骤。
  • Google Cloud Secret Manager 轮换建议:重试、不可并发轮换、渐进 rollout 与旧版本清理。
  • NIST SP 800-57 Part 1:密钥用途、保护、使用期限和撤销的管理原则。

追问

如何防止两个 worker 同时轮换同一个 secret?

用数据库租约或带 fencing token 的分布式锁,并把 token 写入每次状态更新。没有当前 token 的 worker 即使恢复,也不能覆盖新状态;租约需要续期和明确的过期时间。

provider 不支持幂等接口怎么办?

先在本地 job 中持久化请求指纹和外部资源标识,重试前查询 provider 的当前状态。若无法查询,就把该步骤标为需要人工确认,避免盲目创建多个凭据。

为什么不能只让应用读取 latest?

latest 可能把未经验证的新值立即推给全部实例。使用不可变版本、分批 rollout 和健康检查,可以把错误值的影响限制在小范围并保留回滚点。

轮换任务积压时如何保护系统?

按租户和 provider 设置并发配额,使用优先级和退避重试,并暴露最老任务年龄、失败率和剩余恢复窗口。接近过期的 secret 可以提升优先级,但不能绕过权限和幂等检查。

泄露事件发生时,轮换服务要做什么?

立即冻结相关 job 的普通调度,创建高优先级紧急轮换,缩短 rollout 观察窗口并保留取证日志。撤销旧值前确认所有关键消费者已切换,同时通知租户和安全响应流程。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比

相关面试工具

用 Solve 整理系统设计回答

从澄清需求开始,展开规模、架构、组件选择和取舍。

查看工具