- 后端
- 数据库
- 文档数据库
【免费下载链接】FerretDB
A truly Open Source MongoDB alternative
本文基于 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-auth | FERRETDB_TEST_ENABLE_NEW_AUTH | false |
启用方式很简单,任选其一:
# 通过命令行 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)覆盖了EmptyMechanism、BadAuthMechanism等用例,验证了机制为空、机制非法等边界行为;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 挑战-响应握手,客户端与服务器之间通过saslStart和saslContinue两个 MongoDB 命令完成。下面结合仓库源码说明四个阶段。
阶段一:saslStart开启对话
入口在 internal/handler/msg_saslstart.go 的saslStart方法中:
- 从请求中读取
mechanism,若不为SCRAM-SHA-256,则返回ErrMechanismUnavailable(见 msg_saslstart.go)。也就是说,v1.21 实验模式下握手阶段实际以SCRAM-SHA-256为默认机制; - 解析
payload中的客户端首条消息(client-first),从中提取用户名; - 通过
documentdb_api_internal.ScramSha256GetSaltAndIterations向后端获取该用户的 salt 与迭代次数; - 生成服务端首条消息(server-first),连同
conversationId一并返回客户端。
阶段二:saslContinue验证客户端证明
客户端收到 server-first 后计算客户端证明(client proof),再次通过saslContinue提交。入口在 internal/handler/msg_saslcontinue.go:
- 从连接上下文中取出之前保存的 SCRAM 会话(
Conv),若无会话则报ErrProtocolError("No SASL session state found"); - 调用
conv.ClientFinal解析客户端最终消息,计算认证消息(auth message); - 调用
documentdb_api_internal.AuthenticateWithScramSha256校验证明; - 成功后返回服务端签名(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 对update与findAndModify命令中的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 的兼容性矩阵中查询(如
convertToCapped、cloneCollectionAsCapped尚未实现,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=true或FERRETDB_TEST_ENABLE_NEW_AUTH=true; - 创建用户:使用
createUser命令,配合dropUser、updateUser、usersInfo等命令管理账号; - 连接:在连接串中直接携带用户名密码,按 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
相关推荐
EMQX Dashboard JSON API 的 SCRAM-SHA-256 挑战-响应认证接入指南
EMQX Dashboard JSON API 的 SCRAM SHA 256 挑战 响应认证接入指南 本指南介绍 EMQX 5.x Dashboard JSO
后端物联网消息队列通信EMQX Dashboard 登录启用 SCRAM-SHA-256 挑战-响应认证:从密码登录到 SCRAM 的安全迁移指南
EMQX Dashboard 登录启用 SCRAM SHA 256 挑战 响应认证:从密码登录到 SCRAM 的安全迁移指南 EMQX Dashboard 的
后端物联网消息队列通信MongoDB用户认证终极指南:Robo 3T快速配置SCRAM-SHA-256加密
MongoDB用户认证终极指南:Robo 3T快速配置SCRAM SHA 256加密 在现代数据库管理中, MongoDB用户认证机制 是保障数据安全的重要屏障
数据库客户端桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考