1-MongoDB - 基于角色的访问控制

访问控制

MongoDB3.6.12 + centos7.9

MongoDB提供了各种功能,例如身份验证,访问控制,加密,以保护MongoDB的数据安全。

这里我们重点介绍基于角色的访问控制,也就是MongoDB中的用户管理。

MongoDB使用基于角色的访问控制(RBAC)来管理对MongoDB系统的访问。授予用户一个或多个角色,这些角色确定用户对数据库资源和操作的访问权限。

启用访问控制

MongoDB默认情况下不启用访问控制。但可以使用--authsecurity.authorization设置启用授权。

启用访问控制后,用户必须对自己进行身份验证

用户和角色

我们可以在创建用户时,同时指定角色,明确用户对于哪些资源可以进行操控。

我们也可以为现有用户授权或者撤销角色,后续会具体展开讲解。

内置角色和权限

MongoDB提供了内置角色,可提供数据库系统中通常所需的不同访问级别。

接下来简要介绍MongoDB中的内置角色和内置权限。

内置权限

Permission Description
root 只在admin数据库中可用。拥有对MongoDB的所有权限
read 允许用户读取指定数据库
readWrite 允许用户读写指定数据库
dbAdmin 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
dbOwner 允许用户对指定库的所有能力,包含readWrite、dbAdmin、userAdmin这些角色的权限
userAdmin 允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限

内置角色

Role Description
超级用户角色 root
数据库用户角色 read、readWrite
数据库管理角色 dbAdmin、dbOwner、userAdmin
集群管理角色 clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色 backup、restore
所有数据库角色 readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase

更多关于内置角色和权限参考官网:https://docs.mongodb.com/v3.6/core/security-built-in-roles/

注意,如果内置的角色无法满足一些特定的场景,MongoDB还允许我们自定义角色。

用户管理

我们通常先创建具有userAdmin或者userAdminAnyDatabase角色的admin用户后,再开启访问控制。

当开启访问控制后,MongoDB会强制执行访问用户的身份认证。

在展开讲解之前,需要了解一些概念。

认证库的概念

认证库(Authentication Database)也叫验证库,是MongoDB在用户安全方面的重要一部分,用户在登录时,必须加上认证库才能操作。

建议,请将认证库指定为使用的库。

用户管理的相关方法

Name Description
db.auth() 向数据库验证用户
db.changeUserPassword() 更改现有用户的密码
db.createUser() 创建一个新用户
db.dropUser() 删除一个用户
db.dropAllUsers() 删除与数据库关联的所有用户
db.getUser() 返回有关指定用户的信息
db.getUsers() 返回有关与数据库关联的所有用户的信息
db.grantRolesToUser() 向用户授予角色及其特权
db.revokeRolesFromUser() 从用户删除角色
db.updateUser() 更新用户数据

详情参考官档:https://docs.mongodb.com/v3.6/reference/method/js-user-management/#user-management-methods

创建用户

创建用户语法参考:

{
  user: "<name>",
  pwd: "<cleartext password>",
  customData: { <any information> },
  roles: [
    { role: "<role>", db: "<database>" },
    { role: "<role>", db: "<database>" },
    "role", "role",
  ],
  authenticationRestrictions: [
     {
       clientSource: ["<IP>" | "<CIDR range>", ...],
       serverAddress: ["<IP>" | "<CIDR range>", ...]
     },
     ...
  ]
}

其中:

  • user是用户名。
  • pwd是密码,且密码必须是字符串。
  • customData可以备注一些自定义的信息,比如说这个用户哪个员工在使用。
  • roles对应一个列表,列表套字典:
    • role是角色。
    • db是要从操作的库,同时也是认证库。
  • authenticationRestrictions限制身份,用的较少。

在创建用户时,我们通常先创建root超级用户,然后通过root用户来创建其他的用户。

# 创建admin用户,赋予userAdminAnyDatabase角色对应的权限,用于管理所有用户信息
use admin
db.createUser({
	user: "admin",
	pwd: "1234",
	roles: [
		{ role: "userAdminAnyDatabase", db: "admin" }
  ]
})

# 创建超级管理员用户,拥有所有权限
use admin
db.createUser({
	user: "root",
	pwd: "1234",
	roles: [
		{ role: "root", db: "admin" }
  ]
})

# 创建普通用户, 认证库是 admin, 对 t1 库具有 read 角色对应的权限,同时具有 backup角色对应的权限
use admin
db.createUser({
	user: "zhangkai1",
	pwd: "1234",
	roles: [
		{ role: "read", db: "t1" },
        "backup"
  ]
})

# 创建普通用户, 认证库是 t1, 对 t1 库具有 readWrite 角色对应的权限,同时具有 backup角色对应的权限
use t1
db.createUser({
	user: "zhangkai2",
	pwd: "1234",
	roles: [
		{ role: "readWrite", db: "t1" },
        "backup"
  ]
})

# 创建普通用户, 认证库是 t1,尚未分配角色
use t1
db.createUser({
	user: "zhangkai3",
	pwd: "1234",
	roles: []
})

注意:

  • 创建用户时,必须先use到某个数据库下,而且该数据库将作为用户的认证库。
  • 创建管理员类的用户时,必须先use admin后再创建用户,且用户的认证库就是admin
  • 创建普通的用户时:
    • use admin后在创建用户,该用户的认证库就是admin。
    • use到一个普通数据库下创建的用户,该用户的认证库就是这个普通数据库。
  • 一般建议管理员类的用户认证库为admin,普通用户将认证库和要操作的数据库设置为同一个即可。
  • 当用户登录时,必须指定认证库。

另外要说明的是,由于root权限比admin大,操作起来也更便捷,后续的所有用户管理的操作,我都以root用户来操作。

开启认证

开启认证有两种方式。

第一种就是通过在启动时配置--auth参数开启用户认证:

[mongod@cs ~]$ mongod -f /data/mongodb_data/27017/conf/mongodb.conf --fork --auth

第二种,则是直接在配置文件中进行配置:

[mongod@cs ~]$ vim /data/mongodb_data/27017/conf/mongodb.conf 

# 安全配置相关
security:
  # 当开启该配置时,登录需要用户认证,如果你没有可用的用户,请勿配置该参数
  authorization: enabled  
  
[mongod@cs ~]$ systemctl restart mongod
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to manage system services or units.
Authenticating as: root
Password: 
==== AUTHENTICATION COMPLETE ===

当开启认证之后,登录就要加认证了:

[mongod@cs ~]$ mongo	# 无认证,可以登录,但啥也做不了
> use admin
switched to db admin
> show tables
2021-01-20T15:02:19.518+0800 E QUERY    [thread1] Error: listCollections failed: {
	"ok" : 0,
	"errmsg" : "there are no users authenticated",
	"code" : 13,
	"codeName" : "Unauthorized"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:941:1
DB.prototype.getCollectionInfos@src/mongo/shell/db.js:953:19
DB.prototype.getCollectionNames@src/mongo/shell/db.js:964:16
shellHelper.show@src/mongo/shell/utils.js:853:9
shellHelper@src/mongo/shell/utils.js:750:15
@(shellhelp2):1:1

# 此时,就需要通过一个内置方法来进行认证
> db.auth("root", "1234")
1
> show tables
system.users
system.version
> exit

不推荐上面的认证方式,通常在登录时加上认证:

# 标准登录语法,当然,你不要在 -p 后直接跟密码  /admin 是认证库
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1:27017/admin
# 如果是默认端口,可以省略不写
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
# 如果是远程登录
[mongod@cs ~]$ mongo -uroot -p1234 10.0.0.200:27017/admin

认证成功,可以通过以下几个命令来查看用户了:

# 所有的用户都存在admin库下的system.users集合中
> use admin
switched to db admin
> show tables
system.users
system.version

# 查看所有用户
> db.system.users.find()
> db.system.users.find().pretty()

# 查看指定用户
> db.system.users.find({"user":"root"})

# 查看跟当前所在库绑定的用户/所有用户
> show users
> db.getUser("root")
> db.getUsers()

更改用户

如果仅更改用户密码:

# 首先use到对应的库
> use t1
switched to db t1

# 通过show命令查看当前库绑定的用户
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
# db.changeUserPassword("要修改的用户名", "新的密码")
> db.changeUserPassword("zhangkai3", "abc")
> exit
bye
[mongod@cs ~]$ mongo -uzhangkai3 -pabc 127.0.0.1/t1

更改用户其他信息:

> use t1
switched to db t1
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
> db.updateUser("zhangkai3",{
	customData: {"info": "除了用户名不能更改,其他的都可以更改"},
	pwd: "1234",
	roles: [
		{ role: "read", db: "t1" }
  ]
})
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [
		{
			"role" : "read",
			"db" : "t1"
		}
	],
	"customData" : {
		"info" : "除了用户名不能更改,其他的都可以更改"
	}
}

注意,db.updateUser方法中, roles参数用来更新用户的角色信息,比如为一个无角色用户添加角色,更改角色都用该参数指定。

删除用户

删除用户有以下几种方式:

# 首先 db.removeUser() 在MongoDB2.6版本开始被弃用,

# 使用 db.dropUser() 删除用户
# 删除当前库下的用户
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
> use t1
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
{
	"_id" : "t1.zhangkai4",
	"user" : "zhangkai4",
	"db" : "t1",
	"roles" : [ ]
}
> db.dropUser("zhangkai4")
true
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}


# 也可以从 system.users 集合中删除
# 必须先use到admin库中才能删除
> use admin
> db.system.users.deleteOne({"user": "zhangkai5"})


# 删除当前库下所有用户
# 先创建两个用户
> use t2
switched to db t2
> db.createUser({user: "zhangkai5", pwd: "1234", roles: []})
Successfully added user: { "user" : "zhangkai5", "roles" : [ ] }
> db.createUser({user: "zhangkai6", pwd: "1234", roles: []})
Successfully added user: { "user" : "zhangkai6", "roles" : [ ] }
> show users
{
	"_id" : "t2.zhangkai5",
	"user" : "zhangkai5",
	"db" : "t2",
	"roles" : [ ]
}
{
	"_id" : "t2.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t2",
	"roles" : [ ]
}
# 删除当前库绑定的所有用户
> db.dropAllUsers()
NumberLong(2)			# 结果提示有两个用户被删除

角色管理

角色管理的相关方法

Name Description
db.createRole() 创建一个角色并指定其特权
db.dropRole() 删除用户定义的角色
db.dropAllRoles() 删除与数据库关联的所有用户定义角色
db.getRole() 返回指定角色的信息
db.getRoles() 返回数据库中所有用户定义角色的信息
db.grantPrivilegesToRole() 将特权分配给用户定义的角色
db.revokePrivilegesFromRole() 从用户定义的角色中删除指定的特权
db.grantRolesToRole() 指定角色,用户定义的角色将从这些角色继承特权
db.revokeRolesFromRole() 从角色中删除继承的角色
db.updateRole() 更新用户定义的角色

详情参考官档:https://docs.mongodb.com/v3.6/reference/method/js-role-management/#role-management-methods

查看角色

查看角色有两个方法:

db.getRoles()   # 查看当前库下所有的自定义角色
db.getRole("read")   # 查看指定内置或者自定义角色

默认的, MongoDB为每个可用的数据库,绑定所有内置的角色:

[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
> use t3
switched to db t3
> db.getUsers()   # 此时 t3 还没有绑定用户
[ ]
> db.getRoles()   # 查看当前库下所有的自定义角色
[ ]
> db.getRole("read")   # 查看所有内置或者自定义角色
{
	"role" : "read",
	"db" : "t3",
	"isBuiltin" : true,  # 可以发现read角色时内置角色
	"roles" : [ ],
	"inheritedRoles" : [ ]
}

创建和添加角色

当内置角色不能满足特定场景时,可以通过db.createRole方法来创建自定义角色,相关语法:

# 语法参考
{
  role: "<name>",
  privileges: [
     { resource: { <resource> }, actions: [ "<action>", ... ] },
     ...
  ],
  roles: [
     { role: "<role>", db: "<database>" } | "<role>",
      ...
  ],
  authenticationRestrictions: [
    {
      clientSource: ["<IP>" | "<CIDR range>", ...],
      serverAddress: ["<IP>" | "<CIDR range>", ...]
    },
    ...
  ]
}
      
# 示例参考
use admin
db.createRole(
   {
     role: "myClusterwideAdmin",  # 自定义角色名字
     privileges: [  # 拥有哪些特权
       { resource: { cluster: true }, actions: [ "addShard" ] },
       { resource: { db: "config", collection: "" }, actions: [ "find", "update", "insert", "remove" ] },
       { resource: { db: "users", collection: "usersCollection" }, actions: [ "update", "insert", "remove" ] },
       { resource: { db: "", collection: "" }, actions: [ "find" ] }
     ],
     roles: [  # 对 admin 库有 read 权限
       { role: "read", db: "admin" }
     ]
   },
   { w: "majority" , wtimeout: 5000 }  # 写确认和写超时
)

来个示例:

[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
# 创建一个无角色用户
> use t3
> db.createUser({user: "zhangkai6", pwd: "1234", roles: []})
> db.getUser("zhangkai6")
{
	"_id" : "t3.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t3",
	"roles" : [ ]
}

# 创建一个自定义的角色 myRole1,拥有的特权:可以对 t3 库下所有集合进行 "find", "update", "insert", "remove" 操作
# 拥有对 t3 的 readWrite 权限
> db.createRole(
   {
     role: "myRole1",
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: [ "find", "update", "insert", "remove" ] }
     ],
     roles: [
       { role: "readWrite", db: "t3" }
     ]
   }
)
> db.getRole("myRole1")
{
	"role" : "myRole1",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}


# 将自定义角色赋予用户
> db.updateUser("zhangkai6", {"roles": [{"role": "myRole1", "db": "t3"}]})
> db.getUser("zhangkai6")
{
	"_id" : "t3.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t3",
	"roles" : [
		{
			"role" : "myRole1",
			"db" : "t3"
		}
	]
}

更改角色

为现有的自定义角色授予新的特权

# 先创建一个自定义角色
db.createRole(
   {
     role: "myRole2",
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: [ "find" ] }
     ],
     roles: [
       { role: "readWrite", db: "t3" }
     ]
   }
)

# 此时该自定义角色的 privileges 是这样的
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find"
		]
	}
]

# 通过下面方法添加新特权
> db.grantPrivilegesToRole(
	"myRole2",
	[
		{ resource: { db: "t3", collection: "" }, actions: ["update", "insert"] }
	]
)
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert",
			"update"
		]
	}
]

为现有自定义角色删除指定特权

# 现在,自定义角色具有以下特权
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert",
			"update"
		]
	}
]

# 通过 删除 update 特权
> db.revokePrivilegesFromRole(
   "myRole2",
   [
     {resource : {db : "t3",collection : ""}, actions : ["update"]}
   ]
)
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert"
		]
	}
]

# 删除多个特权
> db.revokePrivilegesFromRole(
   "myRole2",
   [
     {resource : {db : "t3",collection : ""}, actions : ["update"]},
     {resource : {db : "t3",collection : ""}, actions : ["find", "insert"]}
   ]
)

将内置或自定义角色赋予自定义的角色

# myRole1是现有的自定义角色,可以在列表中写多个自定义角色
> db.grantRolesToRole(
	"myRole1",
	["myRole2"]
)
> db.getRole("myRole1")
{
	"role" : "myRole1",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		},
		{
			"role" : "myRole2",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "myRole2",
			"db" : "t3"
		},
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}

从现有自定义角色中删除内置或自定义角色

# 现在,myRole1 角色有如下角色
> db.getRole("myRole1")["roles"]
[
	{
		"role" : "myRole2",
		"db" : "t3"
	},
	{
		"role" : "readWrite",
		"db" : "t3"
	}
]
> db.revokeRolesFromRole("myRole1", ["readWrite", "myRole2"])
> db.getRole("myRole1")["roles"]
[ ]

更新角色

# 现在的 myRole2 的角色信息
> db.getRole("myRole2")
{
	"role" : "myRole2",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}

# db.updateRole 将完全覆盖指定自定义角色的所有角色信息,慎用
> db.updateRole(
	"myRole2",
   {
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: ["find"] }
     ],
     roles: [
       { role: "read", db: "t3" }
     ]
   }
)
> db.getRole("myRole2")
{
	"role" : "myRole2",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "read",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "read",
			"db" : "t3"
		}
	]
}

删除角色

删除角色非常简单:

# 现在 t3 下有两个自定义角色
> db
t3
> db.getRoles()
[
	{
		"role" : "myRole1",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [ ],
		"inheritedRoles" : [ ]
	},
	{
		"role" : "myRole2",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		],
		"inheritedRoles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		]
	}
]


# 删除当前库下的指定自定义角色
> db.dropRole("myRole1")
true
> db.getRoles()  # 此时还剩一个自定义角色
[
	{
		"role" : "myRole2",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		],
		"inheritedRoles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		]
	}
]

# 删除当前库下的所有自定义角色
> db.dropAllRoles()
NumberLong(1)
> db.getRoles()   # 一个不剩
[ ]

that's all,see also:

https://docs.mongodb.com/v3.6/reference/method/js-role-management/ | https://docs.mongodb.com/v3.6/reference/method/js-user-management/

posted @ 2021-01-20 18:34  听雨危楼  阅读(696)  评论(0编辑  收藏  举报