MongoDB账号管理及实践

勿忘初心2018-10-19 10:00

此文已由作者温正湖授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


目前蜂巢(云计算基础服务)MongoDB上已经有数十个实例,其中不少是企业用户或公司内部产品用户的。用户多了,那就会反馈一些问题。其中一个就是MongoDB实例访问账号,虽能够提供创建和删除集合、索引,创建数据库等用户所需的权限,但无法删除数据库,这个问题虽然不严重,但多少会影响用户体验。那么这是为什么呢?本文以此作为入口来谈谈MongoDB的账号管理。


MongoDB作为一个成熟的数据库,像MySQL一样,也提供账号管理,但又跟MySQL不大一样,MongoDB使用基于角色的访问权限控制(Role-Based Access Control,RBAC),包括账号(Users,直译为用户,但似乎账号更好理解)、角色(Roles)和权限(Privileges)三层关系。权限指的是允许对某种资源进行的某些操作。这里所说的资源,包括数据库、集合和集群等。在进行MongoDB权限管理操作时,资源使用一个文档来表示,比如数据库mydb下的集合mycoll作为一种资源表示为{db: "mydb", collection: "mycoll"}。{db: "mydb", collection: ""}表示数据库mydb下的所有集合。对应地,{db: "", collection: "mycoll"}就是所有数据库下的mycoll集合,集群资源表示为{cluster: true},如果要将所有数据库和集群作为一种资源那么可以表示为{anyResource: true}。所说的操作包括对集合的CRUD,查看数据库下的集合列表,查看复制集或分片集群当前状态等。根据操作类型,又可以分为:读写操作,数据库管理操作,集群部署操作,复制集操作,分片集群操作,系统管理操作,诊断操作,内部操作等8大类。


MongoDB对外提供的是账号,作为访问MongoDB认证单位。内部,一个账号可以由0到多个角色组成,角色分为内建角色(Built-In Roles)和自定义角色(User-Defined Roles)。为了方便使用,MongoDB提供了多种内建角色,分为数据库用户,数据库管理员,集群管理员,备份和恢复角色,跨数据库角色,超级用户,内部用户等7大类,每个大类下面有可以分为不同权限的角色,比如数据库用户大类可细分为读read和读写readWrite两种角色,而每个角色拥有一定的权限,比如readWrite角色具有创建/删除集合、创建删除索引和集合CRUD等16种权限,跨数据库角色表示该角色不仅对某个数据库有指定的操作权限,还对实例中其他所有(用户创建的)的数据库都具备同样的权限,比如readWriteAnyDatabase,具备对所有数据库有readWrite权限。这里有个特例,就是给用户授权角色时,如果授权时指定的数据库是admin,则即使仅指定readWrite,也同样具备readWriteAnyDatabase角色,也就是说对admin具有readWrite角色,那么对其他数据库也具有,天然具有AnyDatabase光辉。


蜂巢(云计算基础服务)MongoDB提供了MongoDB复制集实例,用户可以创建readWriteAnyDatabase权限的账号来访问MongoDB,为什么要AnyDatabase呢?因为我们还未提供MongoDB的数据库和账号管理功能(接管用户所有的数据库/集合、用户创建和权限管理操作),无法限制用户创建数据库,所以也就无法为特定某个数据库授予readWrite权限。readWriteAnyDatabase权限具备了复制集场景下用户所需的绝大部分权限,包括集合、索引的创建和删除,数据库/集合等统计信息。就像文章刚开始的描述一样,其不具备删除数据库(dropDatabase)这个权限,虽然有点坑,但还不是大问题,只需在后续提供MongoDB的数据库和账号管理功能中,将dropDatabase权限纳入管理即可。但在分片集群场景下,readWriteAnyDatabase所具有的的权限已经根本无法满足用户的正常操作,比如对数据库启用分片功能(enableSharding),人工进行分片的chunk拆分(splitChunk),在我们没有数据库和账号管理功能前,这些操作都需要用户端来执行,而readWriteAnyDatabase无法满足。那么是不是有其他内建角色满足需求呢,查阅官方文档发现集群管理员下的clusterManager角色提供了我们所需的权限,但其还提供了诸如在分片集群中添加和删除分片服务器,在复制集中进行复制集重配置等重型权限,如果给用户这些权限将是非常危险的。所以,在蜂巢分片集群实例中,无法通过为用户提供拥有多个内建角色(如readWriteAnyDatabase、clusterManager两个)的账号来满足用户正常使用的需求。这个时候,自定义角色就派送用场了,在创建自定义角色时,既可以指定对资源的操作权限,还可以选择继承另一个角色所拥有的权限,比如,提供给用户的新账号继承了readWriteAnyDatabase角色的所有权限,还额外添加了dropDatabase、enableSharding和splitChunk等权限,这样一来就顺利解决了面临的问题。


关说不练假把式,结合以上的描述,演示如何为MongoDB进行账号管理操作。


1、首先在mongod配置文件中增加权限相关设置 security: {authorization: enabled, keyFile: /home/mongo/keyfile}。其中authorization表示是否启用访问权限认证,keyFile指定了MongoDB复制集或分片集群内部各组件相互间通信的认证文件;MongoDB提供了本地例外机制,避免用户在启用认证前未设置账号导致无法访问MongoDB实例的尴尬;


2、创建用户所需的数据库账号,如下:

mongos> use admin

mongos> db.createUser(  {    user: "myuser",    pwd: "xxxxx",    roles: [ { role: "readWrite", db: "admin" } ]  } )

指定了认证数据库为admin,角色采用内建的readWrite,由于指定的数据库是admin,那么等同于创建了readWriteAnyDatabase角色的账号。用该账号登陆:

use admin

db.auth("myuser","wzh123")


3、但该账号无法执行如下这些操作:

mongos> use mydb
mongos> db.dropDatabase()
{
	"ok" : 0,
	"errmsg" : "not authorized on shardtest to execute command { dropDatabase: 1.0 }",
	"code" : 13,
	"codeName" : "Unauthorized"
}
mongos> sh.enableSharding("wzhshard")
{
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { enableSharding: \"wzhshard\" }",
	"code" : 13,
	"codeName" : "Unauthorized"
}


4、所以,只能通过创建自定义角色来满足需求,先切换到root账号:

use admin

db.auth("root","wzh123") // root为已创建的具备超级用户角色的管理账号。

创建自定义角色: db.createRole({ role: "wzhRole", privileges: [{ resource: { db: "", collection: "" }, actions: ["enableSharding","dropDatabase" ] }], roles: ["readWriteAnyDatabase"] })

privileges字段指定了该角色具有的对resource字段所述资源具有actions字段所述的操作权限enableSharding和dropDatabase,同时通过roles字段继承了readWriteAnyDatabase的所有权限。


5、进一步操作myuser账号,先去掉其readWrite角色,操作如下:

db.revokeRolesFromUser(
    "myuser",
    [
      { role: "readWrite", db: "admin" }
    ]
)

确认权限已收回:

mongos> db.getUser("myuser")
{
	"_id" : "admin.myuser",
	"user" : "myuser",
	"db" : "admin",
	"roles" : [ ]
}


6、为myuser增加wzhRole角色:

db.grantRolesToUser(
    "myuser",
    [
      { role: "wzhRole", db: "admin" }
    ]
)

确认权限已经赋予:

db.getUser("myuser")
{
	"_id" : "admin.myuser",
	"user" : "myuser",
	"db" : "admin",
	"roles" : [
		{
			"role" : "wzhRole",
			"db" : "admin"
		}
	]
}


7、再使用myuser登陆

use admin

db.auth("myuser","wzh123")


8、执行操作:

mongos> sh.enableSharding("wzhshard")
{ "ok" : 1 }
mongos> sh.shardCollection("wzhshard.table1",{mykey:1})
{ "collectionsharded" : "wzhshard.table1", "ok" : 1 }
mongos> use wzhshard
switched to db wzhshard
mongos> db.dropDatabase()
{ "dropped" : "wzhshard", "ok" : 1 }


一切看起来那么美好!

MongoDB在权限管理中引入了角色这一中间层,一般情况下,直接基于内建角色来创建账号即可,有特殊需求的话,可以创建自定义角色来满足需求,这样一来就无需暴露繁多的具体权限。对于新司机,更容易上手。但不足之处在于MongoDB数据库并未像MySQL一样未提供IP过滤功能(未提供Host字段用来设置账号所适用的主机IP列表)。

最后,网易数据库团队正在热火朝天地调研和开发MongoDB分片集群功能,计划上半年在蜂巢(云计算基础服务)上提供MongoDB分片集群能力,届时将带给大家更加强大的文档数据库能力。


网易云数据库RDS是一种稳定可靠、可弹性伸缩的在线关系型数据库服务,当前支持MySQL引擎,提供基础版,高可用版,金融版针对不同业务场景的高可用解决方案,同时提供多重安全防护措施,性能监控体系,专业的数据库备份、恢复及优化方案,使您能专注于应用开发和业务发展。


参考资料:

1、https://docs.mongodb.com/manual/reference/built-in-roles

2、https://docs.mongodb.com/manual/reference/privilege-actions/

3、https://docs.mongodb.com/manual/reference/resource-document/

4、https://docs.mongodb.com/manual/core/authorization/

5、https://docs.mongodb.com/manual/core/security-user-defined-roles/

6、https://docs.mongodb.com/manual/tutorial/manage-users-and-roles/


网易云免费体验馆,0成本体验20+款云产品! 

更多网易技术、产品、运营经验分享请点击




相关文章:
【推荐】 MySQL MGR集群搭建