必须用自动化脚本+权限隔离+审计验证三步闭环,否则等保测评直接扣分;因db.changeUserPassword()要求高权限账号在线、无操作上下文审计、不刷新旧连接、无失败重试与批量能力,不符合等保三级对权限管控和可追溯性要求。

不能只靠手动改密码,必须用自动化脚本 + 权限隔离 + 审计验证三步闭环,否则等保测评时会被直接扣分。
为什么 mongosh 的 db.changeUserPassword() 不适合生产轮换
这个函数看似方便,但实际踩坑点很多:它要求当前连接用户必须对目标用户所在数据库有 changeOwnPassword 或更高权限(比如 userAdmin),而等保三级明确禁止管理员账号长期在线、禁止高权限账号用于日常运维;另外该命令不记录操作上下文(如调用 IP、执行人、客户端标识),审计日志里只留一条模糊的 command 名,无法满足「谁、在何时、从哪台机器、为哪个账户」的四要素要求。
- 执行后不会自动刷新已建立的连接,旧密码仍可继续用到连接超时或主动断开
- 没有失败重试机制,网络抖动或临时认证失败会导致轮换中断,且无告警
- 不支持批量处理多个用户(比如 appdb 读写用户、backup 用户、monitor 用户需同步更新)
推荐做法:用 mongosh 脚本 + 环境变量 + 定时任务驱动
核心是把密码生成、更新、验证、通知拆成原子步骤,并确保每步可追溯。示例流程如下:
- 密码由外部密码管理器(如 HashiCorp Vault 或 AWS Secrets Manager)动态生成并返回,不硬编码在脚本中
- 使用
mongosh --eval连接 admin 数据库,执行db.updateUser()更新指定用户(注意不是changeUserPassword()),传入新凭据和digestPassword: false(避免二次哈希) - 更新后立即用新凭据尝试连接目标业务库(如
appdb),执行db.runCommand({ping:1})验证可用性 - 成功后写入本地审计文件,包含时间戳、用户名、Vault secret path、执行主机名
定时任务建议用 systemd timer 而非 crontab,便于绑定资源限制与日志追踪:
sudo systemctl edit mongodb-pw-rotate.timer [Timer] OnCalendar=monthly Persistent=true
副本集 / 分片集群中必须统一更新所有节点的内部通信凭据
如果用了 keyfile 认证(security.keyFile)或 x.509(clusterauthmode: x509),仅改应用层用户密码毫无意义——config server、mongos、shard 成员之间仍用旧密钥通信,一旦密钥泄露,整个集群控制权就丢了。等保检查时会专门抓取 rs.status() 和 sh.status() 输出,确认各节点 authMechanisms 是否一致、setVersion 是否最新。
- keyfile 轮换需停机:先在所有节点替换
keyFile内容,再逐个重启 mongod,期间集群不可写 - x.509 轮换可滚动:签发新证书后,在 mongod 配置中追加
net.tls.CAFile和新net.tls.certificateKeyFile,重启时不中断服务,旧证书有效期结束前保持兼容 - 务必检查
mongos进程是否也加载了新凭据,它不读 mongod.conf,需单独配置--keyFile或 TLS 参数
最容易被忽略的点:应用连接池里的密码没更新
Java 应用用 MongoClientURI 初始化连接池后,除非重建实例,否则不会重新读取 URI 中的密码字段;Node.js 的 mongodb 驱动默认启用连接复用,旧连接维持数小时不释放。结果就是:密码在数据库端已生效,但应用还在用旧凭据不断重连报 AuthenticationFailed,直到连接池自然淘汰或服务重启。
解决办法只有两个:spring.data.mongodb.uri 类配置项必须支持运行时刷新(如 Spring Cloud Config + RefreshScope),或者在轮换脚本末尾触发一次轻量级服务 reload(如发送 SIGHUP 给应用进程,前提是应用监听了该信号并重建 MongoClient)。


















