我们最近发布了一项新功能,使你可以为数据库密码定义更加精细化的角色。当你生成一个新密码时,可以选择以下角色:**只读 (read-only)**、**只写 (write-only)**、**读写 (read/write)** 和 **管理员 (admin)**。
我们使用 Vitess 的访问控制列表 (Access Control Lists) 和 VTTablet 实现了密码角色,但在开发途中遇到了一些挑战。在本文中,我们将回顾实现过程中面临的问题,以及我们如何克服这些问题。


Vitess 如何处理用户授权

Vitess 通过一个静态配置文件为其 vttablet 提供授权支持。以下显示了这个文件的示例:

{
  "table_groups": [
    {
      "name": "planetscale user groups",
      "table_names_or_prefixes": ["%"],
      "readers": ["planetscale-reader", "planetscale-writer", "planetscale-admin"],
      "writers": ["planetscale-writer", "planetscale-writer-only", "planetscale-admin"],
      "admins": ["planetscale-admin"]
    }
  ]
}

分析 ACL 配置文件

  • table_names_or_prefixes:适用于该策略的表列表。% 表示所有表。
  • readers:可以从数据库中的表和视图读取数据及架构的用户集合。
  • writers:可以向数据库中的表写入数据的用户集合。
  • admins:可以读取、更新数据并修改数据库架构的用户集合。

虽然这个配置是静态的,但 Vitess 允许你通过使用 --table-acl-config-reload-interval 参数,在特定时间间隔下从磁盘重新加载此文件,从而在 vttablet 的运行时动态调整此列表。
这种静态文件方式适合以下情况的用户:

  1. 用户已预先定义:数据库访问的用户集是已知的,并且维护配置文件中的访问组比较简单。
  2. ACL 配置文件数量较少:每个 Vitess 集群有少量定制的 ACL 配置文件。
  3. 维护时间表可控:更新 Vitess 部署的 pod/共享卷上的文件有明确的维护计划。

为什么这方法不适合 PlanetScale?

上述条件并不适用于 PlanetScale 的客户。作为设计原则,我们尽量避免在 vttablet pod 自身上存储认证和授权状态。
依赖 vttablet pod 上文件的刷新间隔可能会导致多种问题。以下是一些示例:

  1. 无法立即启用新密码

我们希望能够实现”客户创建带角色的密码,并立即生效”的用户故事,这要求密码无需依赖 vttablet 的定时刷新循环。由于无法预测文件的下次刷新时间,我们无法保证新凭证能够立即生效。

  1. 不同 pod 的状态可能冲突

负责管理 vttablet 的操作人员需要向 pod 写入文件,但可能无法同时更新所有 vttablet pod,这可能导致使用某个凭证时出现竞态条件。例如,同一密码在一个 pod 上可能是管理员角色,而在另一个 pod 上可能是只读角色。

  1. 服务重启后的状态恢复问题

如果 pod 出现故障并需要重启,我们没有外部 ACL 存储来确定每个数据库应该具有的 ACL 状态,从而在启动之前无法恢复权限状态。

  1. 独立维护每个数据库的状态

我们需要为每个客户数据库维护一套单独的状态,无法与其他 PlanetScale 数据库共享统一状态。
考虑到这些问题,我们提出了一个解决方案,为客户提供他们期望的无缝 PlanetScale 用户体验。


我们如何使用静态 ACL 文件实现密码角色

对于用户创建的每个密码,我们会在凭证数据库中存储以下信息:
| 显示名称 (Display name) | 角色 (Role) | password_sha1_hash | password_sha2_hash |
角色属性决定你将映射到的三个 vttablet ACL 角色(**readers**、**writers** 或 **admins**)中的哪个。
例如,如果你创建了只写的密码并连接到你的 PlanetScale 数据库,查询将通过用户查询前端(一个负责 PlanetScale 数据库用户交互功能的服务)。如下图所示,我们的解决方案能够解决之前部分中讨论的所有问题:
ACL 转换示意图


我们的解决方案优势

  1. 动态用户凭证存储

动态用户凭证存储允许我们即时创建或删除用户角色映射,无需依赖刷新间隔。

  1. 预定义用户名与角色映射

我们已预定义一组用户名,这些用户名描述了各角色支持的权限,例如 planetscale-reader 仅能读取数据和架构。通过将所有 PlanetScale 用户的角色映射到 Vitess ACL 配置中的用户名,我们可以”即时”重写安全主体,让数据库连接获得正确的访问级别。

  1. 统一的 ACL 状态

由于所有认证和授权数据都存储在外部数据存储中,我们为某个数据库创建的所有 pods 都会拥有相同的 ACL 状态。

  1. 简化问题调试

由于基础 ACL 配置在所有 PlanetScale 数据库中都是统一的,因此调试 ACL 执行中的问题以及修复过程变得更加简单。



幕后的故事:我们如何构建密码角色插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://www.choupangxia.com/2025/09/07/behind-the-scenes/