FerretDB v1.21 发布解读:实验性 SCRAM-SHA-1/SCRAM-SHA-256 认证机制实战
2026/9/23 14:00:21 网站建设 项目流程
  • 后端
  • 数据库
  • 文档数据库

【免费下载链接】FerretDB

A truly Open Source MongoDB alternative

项目地址:https://gitcode.com/gh_mirrors/fe/FerretDB
点击查看免费下载

本文基于 FerretDB 官方 v1.21 版本发布说明,深入讲解该版本引入的实验性SCRAM-SHA-1/SCRAM-SHA-256认证机制:如何通过--test-enable-new-auth启用、如何用createUser创建用户并完成带认证的连接,并结合当前仓库源码剖析 SCRAM 握手在saslStart/saslContinue中的实现细节。同时梳理该版本对upsert与 capped collection 清理逻辑的修复,以及 v1.x 末期到 v2.0 的架构演进背景。读完本文,你将掌握 FerretDB 认证模式的基本原理、启用方式与排查思路。

版本背景:为什么 v1.21 需要新的认证机制

FerretDB 一直以 MongoDB 协议兼容为使命,认证是其中至关重要的一环。在 v1.21 之前,FerretDB 主要依赖外部数据库(PostgreSQL/SQLite)自身的账号体系来模拟 MongoDB 的用户认证,这在很多场景下体验割裂:用户管理命令支持有限、凭据无法由 FerretDB 独立管理。

v1.21 是 1.x 系列的重要节点,官方在该版本中加入了实验性的SCRAM-SHA-1/SCRAM-SHA-256认证机制。SCRAM(Salted Challenge Response Authentication Mechanism)是 RFC 5802/7677 定义的挑战-响应认证协议,也是 MongoDB 原生驱动默认使用的认证方式。FerretDB 引入该机制后,可以让驱动以与 MongoDB 一致的方式完成认证握手,从而显著提升兼容性。

需要说明的是,该功能在 v1.21 中标记为"实验性",需要显式开启;后续版本(v1.24 及 v2.x)在此基础上进一步演化,最终成为默认认证路径。本文以 v1.21 发布说明为主线,同时结合当前仓库中可验证的源码与测试进行佐证。

启用实验性认证模式

v1.21 中,新认证模式默认关闭,需要通过专用开关显式打开。官方文档(见 version-v1.24 的 flags 文档)明确给出了对应关系:

命令行 Flag环境变量默认值
--test-enable-new-authFERRETDB_TEST_ENABLE_NEW_AUTHfalse

启用方式很简单,任选其一:

# 通过命令行 flag ferretdb --test-enable-new-auth=true # 通过环境变量 FERRETDB_TEST_ENABLE_NEW_AUTH=true ferretdb

注意事项:

  • 该 flag 在 v1.21 时期属于测试用途的隐藏参数,可能不会出现在--help输出中;
  • 开启后,FerretDB 将自行管理用户账号,并解锁更多用户管理命令;
  • 只有在此模式下,才支持在连接串中使用createUser创建的用户凭据完成 SCRAM 认证。

在 Docker Compose 部署场景中,典型配置如下(Postgres 后端):

services: postgres: image: postgres environment: - POSTGRES_USER=username - POSTGRES_PASSWORD=password - POSTGRES_DB=ferretdb volumes: - ./data:/var/lib/postgresql/data ferretdb: image: ghcr.io/ferretdb/ferretdb:1 restart: on-failure ports: - 27017:27017 environment: - FERRETDB_POSTGRESQL_URL=postgres://username:password@postgres:5432/ferretdb - FERRETDB_TEST_ENABLE_NEW_AUTH=true - FERRETDB_SETUP_USERNAME=user - FERRETDB_SETUP_PASSWORD=pass - FERRETDB_SETUP_DATABASE=ferretdb networks: default: name: ferretdb

其中FERRETDB_SETUP_USERNAME/FERRETDB_SETUP_PASSWORD/FERRETDB_SETUP_DATABASE用于在首次启动时创建初始账号与初始数据库(这一能力在 v1.22 的"用户设置"功能中被正式完善)。启动后用docker compose up -d,即可通过mongosh "mongodb://user:pass@ferretdb/ferretdb"完成带认证的连接。

创建用户并连接

启用新认证模式后,就可以使用标准的createUser命令创建用户。其命令入口在仓库 internal/handler/msg_createuser.go 中实现,核心逻辑是解析文档、补充默认角色后调用documentdb.CreateUser将用户持久化到后端。

// mongosh 中创建用户 use admin db.runCommand({ createUser: "user", pwd: "pass", roles: [{ role: "clusterAdmin", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" }], mechanisms: ["SCRAM-SHA-256"] // 可选,默认机制为 SCRAM-SHA-256 })

关于mechanisms参数:从源码看,msg_createuser.go 中会先移除客户端传入的mechanisms字段,再交给底层存储处理,实际生效的机制以存储层返回为准。仓库中的集成测试(如 integration/auth/create_user_test.go)覆盖了EmptyMechanismBadAuthMechanism等用例,验证了机制为空、机制非法等边界行为;integration/auth/usersinfo_test.go 则验证了usersInfo返回的机制列表既有纯SCRAM-SHA-256的情况,也有["SCRAM-SHA-1", "SCRAM-SHA-256"]的组合情况。

创建完成后,即可在连接串中直接使用该用户凭据连接:

mongosh "mongodb://user:pass@localhost:27017/?authSource=admin&authMechanism=SCRAM-SHA-256"

新认证模式同时支持以下用户管理命令:

  • dropUser/dropAllUsersFromDatabase:删除用户;
  • updateUser:修改用户密码与角色;
  • usersInfo:查询用户信息与支持的认证机制。

这些命令分别对应仓库 internal/handler/msg_dropuser.go、msg_dropallusersfromdatabase.go、msg_updateuser.go、msg_usersinfo.go 中的实现。

SCRAM 认证握手:源码级流程解析

新认证模式的核心是一次标准的 SCRAM 挑战-响应握手,客户端与服务器之间通过saslStartsaslContinue两个 MongoDB 命令完成。下面结合仓库源码说明四个阶段。

阶段一:saslStart开启对话

入口在 internal/handler/msg_saslstart.go 的saslStart方法中:

  1. 从请求中读取mechanism,若不为SCRAM-SHA-256,则返回ErrMechanismUnavailable(见 msg_saslstart.go)。也就是说,v1.21 实验模式下握手阶段实际以SCRAM-SHA-256为默认机制;
  2. 解析payload中的客户端首条消息(client-first),从中提取用户名;
  3. 通过documentdb_api_internal.ScramSha256GetSaltAndIterations向后端获取该用户的 salt 与迭代次数;
  4. 生成服务端首条消息(server-first),连同conversationId一并返回客户端。

阶段二:saslContinue验证客户端证明

客户端收到 server-first 后计算客户端证明(client proof),再次通过saslContinue提交。入口在 internal/handler/msg_saslcontinue.go:

  1. 从连接上下文中取出之前保存的 SCRAM 会话(Conv),若无会话则报ErrProtocolError("No SASL session state found");
  2. 调用conv.ClientFinal解析客户端最终消息,计算认证消息(auth message);
  3. 调用documentdb_api_internal.AuthenticateWithScramSha256校验证明;
  4. 成功后返回服务端签名(server signature)作为 server-final,完成双向认证。

阶段三:会话状态管理

Conv结构体定义在 internal/util/scram/conv.go 中,它完整保存 client-first、server-first、client-final、server-final 四类消息,并用sync.RWMutex保证并发安全。值得注意的是,会话不可重启——每个连接必须新建一个Conv实例,且非当前对话的重复消息会被拒绝。连接级会话存放在conninfo中,由saslContinue在结束时清理。

阶段四:SCRAM 消息解析

消息解析在 internal/util/scram/message.go 的parseMessage中实现,其安全校验值得关注:

  • 客户端非ce(r)长度不得小于 16 字节;
  • 服务端 salt(s)长度不得小于 12 字节,且推荐使用 28 字节(与 DocumentDB 创建的用户凭据保持一致,否则会打印告警日志);
  • 迭代次数(i)不得小于 4096,防止弱迭代导致的口令爆破风险;
  • 仅支持c=biws(即n,,的 base64 编码),即不支持 channel binding;
  • 属性顺序需符合 RFC 5802 第 5.1 节的规定。

scram包的整体定位在 internal/util/scram/scram.go 的包注释中有明确说明:"Package scram provides an implementation of SCRAM-SHA-256 subset",即实现的是 SCRAM-SHA-256 子集。

兼容性细节

从 internal/handler/msg_hello.go 可以看到,hello命令会返回saslSupportedMechs: ["SCRAM-SHA-256"];而 internal/handler/msg_getparameter.go 中authenticationMechanisms参数的返回值为["SCRAM-SHA-1", "SCRAM-SHA-256"],这正是 v1.21 发布说明所称支持两种机制的组合体现。

本版本的缺陷修复

upsert处理重构:修复过滤字段被忽略的问题

v1.21 对updatefindAndModify命令中的upsert处理进行了重构,修复了upsert: true过滤字段(query 字段)被错误忽略、未拼接到新文档的问题。

仓库中的集成测试可以佐证该修复覆盖的行为,例如 integration/query/findandmodify_test.go 中的TestFindAndModifyCommandUpsert表驱动测试:

"UpsertNoSuchDocumentNoIdInQuery": { command: bson.D{ {"query", bson.D{{ "$and", bson.A{ bson.D{{"v", bson.D{{"$gt", 0}}}}, bson.D{{"v", bson.D{{"$lt", 0}}}}, }, }}}, {"update", bson.D{{"$set", bson.D{{"v", 43.13}}}}}, {"upsert", true}, }, lastErrorObject: bson.D{ {"n", int32(1)}, {"updatedExisting", false}, }, },

该用例验证了"查询条件不存在任何匹配文档、且 query 中没有_id时"的 upsert 行为。UpsertExpressionKey(query 使用_id: {$exists: false})、UpsertDocumentKey(query 使用嵌套文档_id)等用例则验证了不同类型的查询键在 upsert 时被正确保留。update 命令侧的类似场景由 integration/update_field_test.go 覆盖,其中通过find结果比对确认了 upsert 生成的_id及字段内容。

capped collection 清理逻辑改进

v1.21 同时改进了 capped collection(固定大小集合)的清理逻辑:当集合只配置了size参数、未配置max选项时,删除旧文档的逻辑现在能正确处理。此前该场景下文档删除行为存在偏差,此次修复保证了按大小限制滚动淘汰旧文档的语义符合预期。

注:capped collection 在 FerretDB 中的支持状态可在 website/docs/migration/compatibility.md 的兼容性矩阵中查询(如convertToCappedcloneCollectionAsCapped尚未实现,create支持等)。

展望:v2.0 的架构变革

v1.21 发布时官方明确表示:1.x 的迭代目标——提供生产可用的 MongoDB 开源替代品、收集多场景反馈——已经阶段性达成;而性能是"房间里的大象"。v2.0 作为架构级的分水岭,将彻底改变底层架构以换取大幅的性能与兼容性提升(当前仓库v2系列正是这一演进的结果)。

对于计划从 MongoDB 迁移或正在评估 FerretDB 的用户,官方建议关注:

  • 版本升级路径(见 website/docs/migration/migrating-from-v1.md);
  • 迁移前的兼容性预检(见 website/docs/migration/premigration-testing.md)。

小结

  • 启用实验认证--test-enable-new-auth=trueFERRETDB_TEST_ENABLE_NEW_AUTH=true
  • 创建用户:使用createUser命令,配合dropUserupdateUserusersInfo等命令管理账号;
  • 连接:在连接串中直接携带用户名密码,按 MongoDB 驱动习惯指定authMechanism=SCRAM-SHA-256
  • 原理:完整的 SCRAM 四阶段握手由saslStart/saslContinue驱动,底层scram包负责消息解析与会话状态,安全校验(nonce 长度、salt 长度、迭代次数下限)在 internal/util/scram/message.go 中严格把关。

v1.21 是 FerretDB 认证体系走向成熟的起点:从实验性 SCRAM 支持,到 v1.22 的用户初始化设置、v1.24 的 SQLite 认证支持,直至 v2 中认证成为默认能力——这条演进脉络清晰地展示了 FerretDB 如何一步步把"认证"这一 MongoDB 兼容性的关键拼图补齐。

  • 后端
  • 数据库
  • 文档数据库

【免费下载链接】FerretDB

A truly Open Source MongoDB alternative

项目地址:https://gitcode.com/gh_mirrors/fe/FerretDB
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询