Mongodb 权限管理

背景

出于安全因素,数据管理员通过权限管理来保护数据, 防止数据库的数据被恶意或者错误删除。数据库基本上也提供这样的功能。

本文的测试版本是基于mongodb 3.2.10

开启mongodb的权限管理功能

mongodb 启动的时候通过--auth 参数来开启权限管理功能。需要说明的是,第一次启动mongodb时, 数据库里只有local 一个数据库,而数据库的用户数据是存在admin数据库的system.users collection中, 因此第一次创建mongodb时, 数据库管理员需要通过mongo shell 登陆到数据中创建管理员用户。 在mongodb中, 用户分两种:

- 数据库用户
  用于操作数据库, 比如对数据库进行添加、 删除、 修改、 查找等

- 管理数据库用户的用户(超级用户)
  用于添加、删除、修改数据库用户。超级用户的role 是UserAdmin, 后面用例子说明userAdmin 和userAdminAnyDatabase的区别

第一次启动和添加超级用户

启动后用mongo shell 连接数据库,通过show dbs 查看,并没有admin 数据库

[17:12 kxing@bcdev4 article] mongod --dbpath /home/kxing/mongodb/article --logpath /home/kxing/mongodb/article/log --port 56000 --fork
about to fork child process, waiting until server is ready for connections.
forked process: 112787
child process started successfully, parent exiting
[17:12 kxing@bcdev4 article] 
[17:12 kxing@bcdev4 article] mongo --port 56000
MongoDB shell version: 3.2.10
connecting to: 127.0.0.1:56000/test
Server has startup warnings: 
2019-07-15T17:12:29.353+0800 I CONTROL  [initandlisten] 
2019-07-15T17:12:29.353+0800 I CONTROL  [initandlisten] ** WARNING: /proc/sys/vm/overcommit_memory is 2
2019-07-15T17:12:29.353+0800 I CONTROL  [initandlisten] **          Journaling works best with it set to 0 or 1
2019-07-15T17:12:29.353+0800 I CONTROL  [initandlisten] 
2019-07-15T17:12:29.353+0800 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 8192 processes, 262144 files. Number of processes should be at least 131072 : 0.5 times number of files.
> 
> show dbs
local  0.000GB
> 
> use admin       #先use admin, 然后再用createUser创建用户
switched to db admin
> db.createUser({user: "root", pwd: "root", roles: [{role: "userAdminAnyDatabase", db: "admin"}]})  #创建一个超级管理员
Successfully added user: {
    "user" : "root",
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        }
    ]
}

> db.system.users.find()  #用户已经添加到数据库中, 只有role是userAdmin的用户才能添加、删除、修改用户
{
    "_id" : "admin.root",
    "user" : "root",
    "db" : "admin",
    "credentials" : {
        "SCRAM-SHA-1" : {
            "iterationCount" : 10000,
            "salt" : "H0qjUARDao5pvIW2Uf8VrQ==",
            "storedKey" : "uVRbobfJLZO/73mYAE+d7LnA1vc=",
            "serverKey" : "qFQylQpcN/Bbho1MUkHD67asNys="
        }
    },
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        }
    ]
}

这里是先use admin后再通过createUser 创建用户, 因此被创建的用户必须在admin下验证, 不能在其他数据库下验证。

规则一:用户是跟着数据库走的,在那个数据库下创建,就在该数据库下验证

解释下面这条语句:

db.createUser({user: "root", pwd: "root", roles: [{role: "userAdminAnyDatabase", db: "admin"}]})
user:    用户名
pwd:  密码
roles:   指定用户角色, 是一个数组的数据类型, 意味一个用户可以用多个角色, 举例说明:
db.createUser({user: "test", pwd: "test", roles: [  {role: "read", db: "test_1"},
                                                    {role: "readWrite", db: "test_2"},
                                                 ]})
用户test对test_1 数据库用户读的权限, 同时对test_2数据库有读写权限。

关闭mongodb然后加--auth参数重启

> mongod --dbpath /home/kxing/mongodb/article --logpath /home/kxing/mongodb/article/log --port 56000 --fork --auth

添加数据库用户和验证后操作数据库

用mongo shell 连接数据库, 用show dbs查看会数据库就报错。原因是此时数据库已经开启了权限控制功能, 必须要先输入用户和密码后才能查看数据库。

[17:35 kxing@bcdev4 article] mongo --port 56000
MongoDB shell version: 3.2.10
connecting to: 127.0.0.1:56000/test
> 
> show dbs
2019-07-15T17:35:29.278+0800 E QUERY    [thread1] Error: listDatabases failed:{
    "ok" : 0,
    "errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }",
    "code" : 13
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1
shellHelper.show@src/mongo/shell/utils.js:761:19
shellHelper@src/mongo/shell/utils.js:651:15
@(shellhelp2):1:1
> 

由于已经启动了权限控制功能, 先要验证后才可以操作。

> use A
switched to db A
> db.auth("root", "root")
Error: Authentication failed.   #验证不成功, 因为root用户是在admin下创建的。参考规则一
0
> 
> use admin
switched to db admin
> db.auth("root", "root")
1
> use A
switched to db A   
> db.A.insert({"title": "test authorization"})  #在admin验证了root用户, 但root用户的role是
WriteResult({                          #userAdminAnyDatabase, 换句话说root用户只能添加、删除、修改用户,                                           
    "writeError" : {                   #不能操作数据库, 下一步应该用root用户来创建数据库用户
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"A\", documents: [ { _id: ObjectId('5d2d23715f5c1a61441a0222'), title: \"test authorization\" } ], ordered: true }"
    }
})

下面在A数据库下创建两个用户, 一个用户只能读, 另一个用户可以读写操作。

> use admin
switched to db admin 
> db.auth("root", "root")    #一定要先去admin数据库验证root用户才能有权限添加用户
> use A
switched to db A
> db.createUser({user: "read_A", pwd: "read_A", roles:[{role: "read", db: "A"}]})
Successfully added user: {
    "user" : "read_A",
    "roles" : [
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
> db.createUser({user: "write_A", pwd: "write_A", roles:[{role: "readWrite", db: "A"}]})
Successfully added user: {
    "user" : "write_A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        }
    ]
}
#创建用户后, 可以在A数据库下用show users查看当前数据库下有多少个用户
> show users;
{
    "_id" : "A.read_A",
    "user" : "read_A",   #只读用户
    "db" : "A",
    "roles" : [
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
{
    "_id" : "A.write_A",
    "user" : "write_A",  #读写用户
    "db" : "A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        }
    ]
}

到此已经创建了两个用户, 但是还不能读写A数据库, 还需要在A数据库下验证, 想往A数据库中插入数据, 那么我们需要验证write_A用户,验证read_A是不能插入数据的,只能读。

> db.auth("read_A", "read_A")
1
> db.test.insert({"title": "This is a test for authorization"})  #插入不成功。
WriteResult({
    "writeError" : {
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"test\", documents: [ { _id: ObjectId('5d2d2851f5c3c3e1771e2707'), title: \"This is a test for authorization\" } ], ordered: true }"
    }
})
> db.auth("write_A", "write_A")
1
> db.test.insert({"title": "This is a test for authorization"})
WriteResult({ "nInserted" : 1 })
> db.test.find()
{
    "_id" : ObjectId("5d2d2865f5c3c3e1771e2708"),
    "title" : "This is a test for authorization"
}
>

以上就是简单的用户创建和用户验证操作数据库。接下来我们看看用户的角色(role)说明。

用户角色(role)

mongodb 有以下的内置角色(Built-In Roles).

1. 数据库用户角色:read、readWrite;
2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;
3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
4. 备份恢复角色:backup、restore;
5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
6. 超级用户角色:root  
    #这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
7. 内部角色:__system

Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
#只能在admin数据库中用的意思是: 如果用户的角色(role)是以下这些角色, 那么db必须是admin([role: 'xxx', db: "admin"])
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。

#root 角色的用户既可以操作所有的数据库,也可以编辑所有的用户。
root:只在admin数据库中可用。超级账号,超级权限

修改role

append role to user

db.grantRolesToUser("read_or_write", [{role: "readWrite", db: "A"}])

remove role from user

db.revokeRolesFromUser("read_or_write", [ {role: "readWrite", db: "A"}])

update user's role

db.updateUser("read_or_write", { roles: [ {role: "readWrite", db: "A"}]})

> use A
switched to db A
> db.createUser({user:"read_or_write", pwd: "read", roles: [{role: "read", db: "A"}]})
Successfully added user: {
    "user" : "read_or_write",
    "roles" : [
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
> db.auth("read_or_write", "read")
1
> show tables
test
> db.test.find()
{
    "_id" : ObjectId("5d2d2865f5c3c3e1771e2708"),
    "title" : "This is a test for authorization"
}
...
> db.test.insert({"title": "hello world"})
WriteResult({
    "writeError" : {
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"test\", documents: [ { _id: ObjectId('5d2d88c5f5c3c3e1771e270d'), title: \"hello world\" } ], ordered: true }"
    }
})
> db.grantRolesToUser("read_or_write", [{role: "readWrite", db: "A"}])
> show users
{
    "_id" : "A.read_or_write",
    "user" : "read_or_write",
    "db" : "A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        },
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
> db.test.insert({"title": "hello world"})
WriteResult({ "nInserted" : 1 })
> db.revokeRolesFromUser("read_or_write", [ {role: "readWrite", db: "A"}])
> show users
{
    "_id" : "A.read_or_write",
    "user" : "read_or_write",
    "db" : "A",
    "roles" : [
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
> db.test.insert({"title": "hello world"})
WriteResult({
    "writeError" : {
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"test\", documents: [ { _id: ObjectId('5d2d9198f5c3c3e1771e2710'), title: \"hello world\" } ], ordered: true }"
    }
})

> db.updateUser("read_or_write", { roles: [ {role: "readWrite", db: "A"}]})
> show users
{
    "_id" : "A.read_or_write",
    "user" : "read_or_write",
    "db" : "A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        }
    ]
}
> db.test.insert({"title": "hello world"})
WriteResult({ "nInserted" : 1 })
> db.test.find()
{
    "_id" : ObjectId("5d2d2865f5c3c3e1771e2708"),
    "title" : "This is a test for authorization"
}
...
> 

自定义role

db.createRole({role: "custonRole", privileges: [{resource:{db:"A", collection:"test"}, actions:["find"]}],roles:[]})

> show users
{
    "_id" : "A.read_or_write",
    "user" : "read_or_write",
    "db" : "A",
    "roles" : [
        {
            "role" : "read",
            "db" : "A"
        }
    ]
}
> db.test.insert({"title": "hello world"})
WriteResult({
    "writeError" : {
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"test\", documents: [ { _id: ObjectId('5d2d95fcf5c3c3e1771e2711'), title: \"hello world\" } ], ordered: true }"
    }
})
> db.createRole({role: "customRole_2", privileges: [{resource:{db:"A", collection:"test"}, actions:["find", "insert"]}],roles:[]})
{
    "role" : "customRole_2",
    "privileges" : [
        {
            "resource" : {
                "db" : "A",
                "collection" : "test"
            },
            "actions" : [
                "find",
                "insert"
            ]
        }
    ],
    "roles" : [ ]
}
> db.updateUser("read_or_write", { roles: [ {role: "customRole_2", db: "A"}]})
> db.test.insert({"title": "hello world"})
WriteResult({ "nInserted" : 1 })
> show users
{
    "_id" : "A.read_or_write",
    "user" : "read_or_write",
    "db" : "A",
    "roles" : [
        {
            "role" : "customRole_2",
            "db" : "A"
        }
    ]
}
> 
db.createUser({user:"write", pwd: "write", roles:[{role: "readWrite", db: "A"}]})
Successfully added user: {
    "user" : "write",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        }
    ]
}
> db.auth("write", "write")
1
> db.Person.insert({"name": "kxing"})
WriteResult({ "nInserted" : 1 })
> show tables
Person
test
> db.auth("read_or_write", "read")
1
> db.Person.insert({"name": "kxing"})
WriteResult({
    "writeError" : {
        "code" : 13,
        "errmsg" : "not authorized on A to execute command { insert: \"Person\", documents: [ { _id: ObjectId('5d2d978cf5c3c3e1771e2715'), name: \"kxing\" } ], ordered: true }"
    }
})
> db.test.insert({"name": "kxing"})
WriteResult({ "nInserted" : 1 })

登陆验证

  • mongo shell 登陆
mongo --port 27017 -u manager -p 12345678 --authenticationDatabase admin
  • mongo-c-driver 登陆
mongoc_client_t *client =  mongoc_client_new ("mongodb://user:password@localhost/?authSource=mydb");

userAdmin 和 userAdminAnyDatabase的区别

这里主要针对mongo shell 连接的验证

  • userAdminAnyDatabase的用户可以看到所有的数据库, 也可以在任何数据库下创建用户
  • userAdmin只能在admin数据库下验证后创建admin的用户(db: "admin")
> use admin
switched to db admin
> show users   #有两个用户root 和 uad, role 分别是userAdminAnyDatabase和userAdmin
{
    "_id" : "admin.root",
    "user" : "root",
    "db" : "admin",
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        }
    ]
}
{
    "_id" : "admin.uad",
    "user" : "uad",
    "db" : "admin",
    "roles" : [
        {
            "role" : "userAdmin",
            "db" : "admin"
        }
    ]
}
> db.auth("uad", "uad")
1
> show dbs
2019-07-16T16:00:03.763+0800 E QUERY    [thread1] Error: listDatabases failed:{
    "ok" : 0,
    "errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }",
    "code" : 13
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1
shellHelper.show@src/mongo/shell/utils.js:761:19
shellHelper@src/mongo/shell/utils.js:651:15
@(shellhelp2):1:1

> db.auth("root", "root")
1
> show dbs
A      0.000GB
B      0.000GB
admin  0.000GB
local  0.000GB
> 
> use A
switched to db A
> show users
{
    "_id" : "A.write_A",
    "user" : "write_A",
    "db" : "A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "A"
        }
    ]
}
{
    "_id" : "A.write_B",
    "user" : "write_B",
    "db" : "A",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "B"
        }
    ]
}
> db.createUser({user:"read", pwd: "read", roles: [{role: "read", db: "A"}]})
Successfully added user: { "user" : "read", "roles" : [ { "role" : "read", "db" : "A" } ] }
> use admin
switched to db admin
> db.auth("uad", "uad")
1
> use A
switched to db A
> db.createUser({user:"read_1", pwd: "read_1", roles: [{role: "read", db: "A"}]})
2019-07-16T16:02:43.644+0800 E QUERY    [thread1] Error: couldn't add user: not authorized on A to execute command { createUser: "read_1", pwd: "xxx", roles: [ { role: "read", db: "A" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 30000.0 } } :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1267:15
@(shell):1:1

> use admin
switched to db admin
> db.createUser({user:"read_1", pwd: "read_1", roles: [{role: "read", db: "A"}]})
2019-07-16T16:02:57.852+0800 E QUERY    [thread1] Error: couldn't add user: not authorized on admin to execute command { createUser: "read_1", pwd: "xxx", roles: [ { role: "read", db: "A" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 30000.0 } } :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1267:15
@(shell):1:1

> show users
{
    "_id" : "admin.root",
    "user" : "root",
    "db" : "admin",
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        }
    ]
}
{
    "_id" : "admin.uad",
    "user" : "uad",
    "db" : "admin",
    "roles" : [
        {
            "role" : "userAdmin",
            "db" : "admin"
        }
    ]
}
> db.auth("uad", "uad")
1
> db.createUser({user:"read_1", pwd: "read_1", roles: [{role: "read", db: "A"}]})
2019-07-16T16:03:37.790+0800 E QUERY    [thread1] Error: couldn't add user: not authorized on admin to execute command { createUser: "read_1", pwd: "xxx", roles: [ { role: "read", db: "A" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 30000.0 } } :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1267:15
@(shell):1:1

> db.createUser({user:"read_1", pwd: "read_1", roles: [{role: "read", db: "admin"}]})
Successfully added user: {
    "user" : "read_1",
    "roles" : [
        {
            "role" : "read",
            "db" : "admin"
        }
    ]
}
> 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容