MongoDB生产环境执行grantRolesToUser命令权限不足,本地正常
1. 场景说明
我有一个主数据库portal_mongodb,其中存在用户portal_mongodba,其角色权限为{'role': 'dbOwner', 'db': 'portal_mongodb'}。
环境配置如下:
MONGODB_CONNECTION=mongodb MONGODB_HOST=localhost MONGODB_PORT=27017 MONGODB_DATABASE=portal_mongodb MONGODB_USERNAME=portal_mongodba MONGODB_PASSWORD=portal_password MONGODB_MASTER_DATABASE=portal_mongodb
需求:动态创建新数据库,不为新库单独创建用户,而是给现有用户portal_mongodba赋予新库的以下角色:
[ {role: 'dbOwner', db: 'new_db'}, {role: 'readWrite', db: 'new_db'}, {role: 'dbAdmin', db: 'new_db'} ]
其中new_db为动态创建的新数据库。对应的PHP实现代码如下:
$connectionString = 'mongodb://' . env('MONGODB_USERNAME') . ':' . env('MONGODB_PASSWORD') . '@' . env('MONGODB_HOST') . ':' . env('MONGODB_PORT') . '/?authSource=' . env('MONGODB_DATABASE'); $client = new MongoClient($connectionString); // 连接MongoDB服务器 $database = $client->$databaseName; // 指定新数据库 $manager = new MongoManager($connectionString); // 创建管理器用于执行命令 $command = array( 'grantRolesToUser' => env('MONGODB_USERNAME'), 'roles' => array( array( 'role' => 'dbAdmin', 'db' => $databaseName ), array( 'role' => 'readWrite', 'db' => $databaseName ), array( 'role' => 'dbOwner', 'db' => $databaseName ) ) ); // 授权命令结构 $manager->executeCommand(env('MONGODB_DATABASE'), new MongoCommand($command)); // 执行授权命令 $database->$collectionName->insertOne(array('key' => 'value')); // 向新库集合插入测试文档
2. 生产环境数据库与用户创建步骤
通过MongoShell执行以下命令:
- 选择admin数据库:
use admin - 在admin库下创建用户
portalAdmin:db.createUser({user: "portalAdmin", pwd: "portalAdminPassword", roles: [ { role:"userAdminAnyDatabase", db: "admin"}, {role: "root", db: "admin"} ] }) - 创建新数据库:
use lppportal_mongodb - 认证
portalAdmin后创建用户:db.auth( "portalAdmin", "portalAdminPassword" ) - 创建用户
portal_mongodba:db.createUser({user: "portal_mongodba", pwd: "portal_password", roles: [{ role: "dbOwner", db: "portal_mongodb"}]} )
3. 本地环境数据库与用户创建步骤
通过MongoDB Compass执行以下操作:
- 创建数据库:
use portal_mongodb - 创建用户
portal_mongodba:db.createUser({user: "portal_mongodba", pwd: "portal_password", roles: [{ role: "dbOwner", db: "portal_mongodb"}] })
4. 问题描述
上述代码在本地开发环境运行正常,但在生产环境报错。两地的用户结构与环境配置完全一致:
A. 生产环境用户结构
[ { _id: 'portal_mongodb.portal_mongodba', userId: UUID('8aa75c1c-e616-403e-a5e9-0bc260abc3b6'), user: 'portal_mongodba', db: 'portal_mongodb', roles: [ { role: 'dbOwner', db: 'portal_mongodb' } ], mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ] } ]
B. 本地环境用户结构
[ { _id: 'portal_mongodb.portal_mongodba', userId: UUID('9a95223e-393c-4c40-acca-4e3e46ee98b0'), user: 'portal_mongodba', db: 'portal_mongodb', roles: [ { role: 'dbOwner', db: 'portal_mongodb' } ], mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ] } ]
5. 错误信息
Error Message:- not authorized on portal_mongodb to execute command { grantRolesToUser: "portal_mongodba", roles: [ { role: "dbAdmin", db: "little_buds" }, { role: "readWrite", db: "little_buds" }, { role: "dbOwner", db: "little_buds" } ], $db: "portal_mongodb", lsid: { id: UUID("fd6bb158-f8bb-4eb8-8f45-c79667f66cc2") } }
问题根源
portal_mongodba仅拥有portal_mongodb数据库的dbOwner角色,而执行grantRolesToUser命令需要用户管理类权限(如userAdmin、userAdminAnyDatabase或root),dbOwner角色仅包含数据库的读写、管理权限,不具备用户角色的授予/修改权限,因此生产环境下因权限不足触发报错。本地环境可能因MongoDB未严格启用权限校验(或默认配置宽松)而未暴露该问题。
解决方法
方法一:使用高权限用户执行授权操作
修改PHP代码,使用生产环境中创建的portalAdmin用户(拥有全局用户管理和最高权限)连接MongoDB并执行授权命令,这是最安全且合规的方式:
// 注意:添加环境变量PORTAL_ADMIN_PASSWORD=portalAdminPassword $connectionString = 'mongodb://portalAdmin:' . env('PORTAL_ADMIN_PASSWORD') . '@' . env('MONGODB_HOST') . ':' . env('MONGODB_PORT') . '/?authSource=admin'; $client = new MongoClient($connectionString); $database = $client->$databaseName; $manager = new MongoManager($connectionString); // 简化授权角色:dbOwner已包含readWrite和dbAdmin,无需重复添加 $command = array( 'grantRolesToUser' => env('MONGODB_USERNAME'), 'roles' => array( array( 'role' => 'dbOwner', 'db' => $databaseName ) ) ); // 执行命令时指定admin数据库(portalAdmin的认证库) $manager->executeCommand('admin', new MongoCommand($command)); $database->$collectionName->insertOne(array('key' => 'value'));
方法二:为portal_mongodba添加用户管理权限
如果业务必须使用portal_mongodba执行授权操作,可通过MongoShell为其添加全局用户管理权限,但此方法会扩大用户权限范围,需评估安全风险:
use admin # 先认证高权限用户 db.auth("portalAdmin", "portalAdminPassword") # 授予全局用户管理权限 db.grantRolesToUser("portal_mongodba", [{role: "userAdminAnyDatabase", db: "admin"}])
额外优化
无需同时授予dbOwner、readWrite、dbAdmin三个角色,因为**dbOwner角色已经包含了readWrite和dbAdmin的所有权限**,重复授权无意义,可简化授权角色列表以减少冗余操作。
内容的提问来源于stack exchange,提问作者Saswat

