MongoDB 单服务器创建用户并授权

MongoDB 单服务器创建用户并授权

前言

之前使用 MongoDB 时对于用户的认证和授权一直模模糊糊,各种教程看的半半拉拉,最后勉强能用。现在有时间了来总结一下。

基础

1. 常见的角色:

read:允许用户读取指定数据库;
readWrite:允许用户读写指定数据库;
dbAdmin:允许用户进行索引创建、删除,查看统计或访问system.profile,但没有角色和用户管理的权限;
userAdmin:提供了在当前数据库中创建和修改角色和用户的能力;
dbOwner:提供对数据库执行任何操作的能力。这个角色组合了 readWritedbAdminuserAdmin 角色授权的特权;

除此之外还有一种特殊的数据库,就是 admin 数据库。可以在此表中创建管理其它数据库的“超级用户”。对应的角色为:
readAnyDatabase(所有数据库的读权限)、
readWriteAnyDatabase(所有数据库的读写权限)、
userAdminAnyDatabase(所有数据库的 userAdmin 权限)、
dbAdminAnyDatabase(所有数据库的 dbAdmin 权限)

2. createUser 方法(全文重点)

先来看下官方文档

语法为:

// 1
use XXX

// 2
db.createUser({
    user: "<USER_NAME>",
    pwd:  "<PASSWORD>",
    roles: [{ role: "<ROLE1>", db: "<DB1>" },{ role: "<ROLE2>", db: "<DB2>" }...]
})

红框中的字大概理解一下:MongoDB 是根据数据库来授权的,即授权信息存放在某一数据库 A 中,登录时输入用户名密码,并指定该数据库,如果指定错误会提示授权失败。但即使存储在 A 中,授权信息也可包含数据库 B 的角色 ,下一段会演示这种情况。

示例中,首先使用了 use test,这就表明要存放授权信息的数据库为 test。之后 roles 数组里面对象中的 db 属性值可以为当前数据库 test,也可以为别的数据库 reporting

!!!以下为重点中的重点!!!

按照示例写法,连接串应该为 mongodb://<USERNAME>:<PASSWORLD>@<ADDERSS>:<PORT>/?authSource=test。因为最一开始使用了 use test,表示授权数据库为 test,所以连接串中的 authSource(表示授权数据库的属性) 就应为 test。连接后,可以使用 myTester 用户访问 数据库 testreporting(创建用户时 roles 中的设置)。

我一开始遇到了很多的坑就是理解错了 authSource 的意思,将其理解成了要访问的数据库,汗!

知晓这一点后,我认为的最佳实践应该是:在每个数据库中,为该数据库设置角色为 dbOwner 的用户,并在 admin 数据库中,为 admin 的高权限角色,如 readWriteAnyDataBase,以掌握全局。应避免在一个数据库中设置访问另一个数据库的角色,否则会混乱。

即,如果要访问 Demo 数据库,应当这样操作:

// 在 Demo 中创建 Demo 数据库的用户
// 1
use Demo

// 2
db.createUser({
    user: "DemoUser",
    pwd:  "DemoPassword",
    roles: [{ role: "dbOwner", db: "Demo" }]
})

// 在 admin 中 设置高权限用户
// 1
use admin

// 2
db.createUser({
    user: "AdminUser",
    pwd:  "AdminPassword",
    roles: [{ role: "readWriteAnyDataBase", db: "admin" }]
})

实践(过程比较啰嗦,赶时间可以不看XD)

1. 启动服务

原本不是本文的内容,在此也记录一下。在 Docker 中使用 MongoDB 的启动命令(这里就不再说 Docker 的易用便捷性了/doge/)。 本机环境 Windows 10 21H2,Docker Desktop 4.3.0,Engine 20.10.11,使用的 MongoDB 镜像为:4.2.20-bionic

Powershell 中执行命令:

docker run -itd --name <CUSTOM_NAME> `
	-e MONGO_INITDB_ROOT_USERNAME=<ROOT_USERNAME> `
	-e MONGO_INITDB_ROOT_PASSWORD=<ROOT_PASSWD> `
	-p <CUSTOM_PORT>:27017 `
	mongo:<TAG>

(也可以使用 -v <YOUR_PATH>:/data/db 映射到本地路径,在 Windwos 中的写法需要形如 /c/Users/lbwnb/Documents)

mongodb 默认没有密码即具有读写权限,因此一定要注意设置密码,并加上 --auth 启动参数,否则放在公网必然被攻击。

未设置 --auth 的服务器被攻击

我们指定了参数 MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORD ,设置了 root 角色(最高权限)的用户名和密码。由于MongoDB 容器启动命令并不包含 --auth 参数,指定这两个参数后即相当于用 mongod --auth 启动

在本机我们实际执行的命令为:(映射到本地的 27018 端口)

docker run -itd `
	-e MONGO_INITDB_ROOT_USERNAME=RootAdmin `
	-e MONGO_INITDB_ROOT_PASSWORD=RootPassword `
	-p 27018:27017 `
	mongo:4.2.20-bionic

创建了 root 角色的用户,用户名 RootAdmin,密码为 RootPassword

2. 创建测试用户:

首先在实例中创建两个数据库 AAA 和 BBB,并分别为其创建集合 AAAC 和 BBBC。

(1)操作1

AAA 中,为数据库 AAA 创建拥有 readWriteAnyDatabase 角色的用户 user1 (失败)

(2)操作2

在数据库 AAA 中,为数据库 admin 创建拥有 readWriteAnyDatabase 角色的用户 user2 (成功,接下来会用到)

(3)操作3

在数据库 AAA 中,为数据库 AAA 创建 dbOwner 角色的用户 user3 (成功,接下来会用到)

(4)操作4

在数据库 AAA 中,为数据库 BBB 创建 dbOwner 角色的用户 user4 (成功,接下来会用到)

3. 连接测试

使用 .NET Core 3.1 和 MongoDB.Driver 2.15.0

程序主要是连接到数据库并读取一些配置,仅用于测试能否连接,比较简单。代码如下:

class Program
    {
        static void Main(string[] args)
        {
            IMongoClient client = new MongoClient(<CONNECTION_STRING>);
            IMongoDatabase database = client.GetDatabase(<DB_NAME>);
            IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>(<COLLECTION_NAME>);

            Console.WriteLine($"Count: { collection.CountDocuments(a => true) }");
            Console.ReadKey();
        }
    }
用例(1)

user2 (对于 admin 数据库角色为 readWriteAnyDatabase) 使用 AAA 作为 authSource,连接 AAA,结果成功(数据库中没有数据,可以打印出字符即为成功):

同样,user2 (对于 admin 数据库角色为 readWriteAnyDatabase) 使用 AAA 作为 authSource,连接 BBB,结果也可以成功(因为它针对 数据库admin 设置了角色 readWriteAnyDatabase)。

突然想到一个 “恰当的例子” (bushi),在 roles 中的对象将 db 属性值设置为 admin,就好像《原神》里向“风神像”供奉“风神瞳”,获得的体力提升是全局的,而不是仅仅在风神眷顾的蒙德区域。

用例(2)

user3 (对于 AAA 数据库角色为 dbOwner) 使用 AAA 作为 authSource,连接 AAA,结果成功(理所当然)!

user3 (对于 AAA 数据库角色为 dbOwner) 使用 AAA 作为 authSource,连接 BBB,结果自然是失败的(违规了):

Command aggregate failed: not authorized on BBB to execute command { aggregate: "BBBC", pipeline: [ { $match: {} }, { $group: { _id: 1, n: { $sum: 1 } } } ], cursor: {}, $db: "BBB", lsid: { id: UUID("eb40030d-8962-423f-a69f-ed3d9dc3a8c9") } }.

画外音:一般形如 not authorized on XXX to execute command 的报错是因为角色错误,而不是用户不存在或密码错误。

用例(3)

user4(对于 BBB 数据库角色为 dbOwner) 使用 AAA 作为 authSource,连接 BBB,结果成功!

user4(对于 BBB 数据库角色为 dbOwner) 使用 BBB 作为 authSource,连接 BBB,结果自然是失败的(数据库 BBB 中没有 user4 这个用户):

Unable to authenticate using sasl protocol mechanism SCRAM-SHA-1.
Command saslStart failed: Authentication failed.

画外音:一般这样的报错是因为用户不存在(authSource 指定错误 或 用户根本不存在 或 密码错误)

其它的一些问题

在进行上述操作的过程中还遇到了一些细节问题,花了很久才弄清楚

1. 使用 mongodump 备份命令

mongodump 的基本语法就不谈了,可以百度一下。我遇到的情况是这样,因为 使用 Docker 创建容器时指定了 Root 角色用户的用户名和密码,我想直接拿来还原数据库(已经从别处取得了备份)。结果一直报错:

Failed: error connecting to db server: server returned error on SASL authentication step: Authentication failed.

我作为 root 角色,居然不能还原数据库,这不是滑天下之大稽吗。经过一番搜索,发现遗漏了这个选项:--authenticationDatabase,应将其指定为 admin,正确的命令应该是:

mongorestore --authenticationDatabase admin -h localhost:27017 -u RootAdmin -p RootPassword -d <DB_Name> <BACKUP_DB_DIR>

好吧QAQ

2. 启动时的 --auth 选项

起因是发现了一个奇怪的现象,如果在上面将 对于 admin 的权限设置为 userAdminAnyDatabase,则读取时也会报错,提示没有权限。但是我查询了公司测试服务器的数据库,发现用户也是这个权限,而且读取没有任何问题?经过一番探查,发现公司的数据启动时,并没有加 --auth 这个选项,以至于我使用 MongDB Comopse 连接时甚至不需要指定用户名和密码。

随后我再次在测试服务器的数据库中创建了一个 针对read 角色的用户,发现读取数据库也不会报错。猜测:只要 authSource 和用户名、密码指定正确,即可进行任何操作

再来看下官方文档

这里的 authorization 应该指的是根据用户角色判断权限

总结

只要紧紧抓住 authSource(或 authenticationDatabase)roles 数组 里面对象的 role 属性db 属性,就可以很好的把握用户权限了!

本文仅仅是针对开发过程中简单的创建用户、读写数据等操作,探究一些自己之前迷糊的东西。还有很多复杂的角色和权限本文没有涉及,集群的情况亦没有涉及。

参考

官方文档
MongoDB 权限认证
MongoDB认证和授权
MongoDB 查看所有用户账号信息

posted @ 2022-08-19 07:45  battor  阅读(302)  评论(0编辑  收藏  举报