【安全科普】MongoDB 权限控制系统简介

https://p2.ssl.qhimg.com/t016badebf20954cba6.png


背景简介

开发:“给我们最高权限吧,我们用起来方便”

两天后… 

开发:“喂喂 DBA 吗我们的库怎么巨卡无比,你们搞什么啊?”

DBA :“….哦,你们是不是创建索引没加backgroud:true ?” 

开发:“…. …. … 好像是”

这就是我们没有权限控制前的日常,建索引不加 [backgroud:true] 、直接误删业务库或集合数据、对集合每个字段添加单列索引导致容量急剧膨胀还有各种突破认知范围的误操作。由于 MongoDB 早期版本自身对权限控制极其简单粗暴,DBA 为了兼容不同版本的可用性在权限控制上也只能对业务用户授予“超级”(root)用户权限。这也就相当于给伟大的开发开启了邪恶的“番多拉之盒”。 随着MongoDB3.x 版本的大规模上线,为了避免线上误操作及一些其他人为低级错误,我们迫切需要引入更精细化的权限控制机制。从 MongoDB 2.6  版本 开始 MongoDB 已经开始尝试引入相对精细的权限控制,不过直到 MongoDB3.0 版本权限体系才算相对完善,所以本文将主要介绍 MongoDB3.0 版本的权限控制机制。

权限概念

要想理解清楚MongoDB的权限必须先了解如下一些关键字

user

即用户,用于提供客户端连接MongoDB的认证账户

role

即角色,数据权限的集合,创建用户的时候必须要指定对应的角色,否则用户无法操作数据库

resource

即资源,包括database或collection也可以是database和collection的组合

例如:

{ db: <database>, collection: <collection> }

当然你也可能看到一种特殊的资源:

 {"cluster" : true}

它其实表示的是全局资源

actions

即权限操作,"actions" 定义了"user"能够对 "resource document"执行的操作

例如,增删改查包括如下操作:

find、insert、remove、update

privilege

即权限,"privilege" 是一组"resource" 和 "actions" 的组合

authenticationDatabase

认证库,即创建角色或用户时所在的库

例如,在 admin 下创建了 MongoDB 用户那么登陆的时候需要指定认证库

mongo -u mongo -p xxx --host xxx --port xxx --authenticationDatabase=admin

角色

MongoDB 里角色分为 ”内建角色“ 和 ”用户自定义“ 角色两种,内建角色是 MongoDB 为了方便用户管理和内部操作进行的预定义的一些角色具体见文末连接:MongoDB内建角色介绍

多数时候为了精细化权限控制 MongoDB 的内建角色无法满足我们的需求,因此需要 DBA 自定义角色来进行更加详细的权限控制。

创建角色

use admin ; // 进入名为admin的数据库下,下同
db.createRole(
       { role:"testrole", //角色名称
       privileges: [ // 权限集
                  {
                     resource: //资源 {
                             db:"lidan",     // 创建的testrole角色具有对lidan库的操作权限,具体权限建actions
                             collection:""   // lidan库下对应的集合名 如果为"" 表示所有集合
                },
                     actions: [ "find", "insert", "remove","update" ]   //角色可进行的操作,注意这里是一个数组
                   }
                  ],
         roles: [ ] // 是否继承其他的角色,如果指定了其他角色那么新创建的角色自动继承对应其他角色的所有权限,该参数必须显示指定
       }
)

上述语句在 admin 库里创建了一个名为 testrole的角色,该角色具有对数据库lidan下的所有集合进行 find、insert、remove、update的操作的权限。

角色创建完毕后 MongoDB 会在系统库 admin 下创建一个系统collection名叫system.roles  里面存储的即是角色相关的信息。

可以使用如下语句进行查看:

db.system.roles.find();

另外, MongoDB 所有权限操作列表详见文末连接:MongoDB 权限操作列表

查看角色

use admin; // 数据库必须是创建所要查看角色时的数据库,下同
db.getRole( 
            "testrole", //要查看角色的名字
            { 
               showPrivileges: true   //指定查看角色信息的时候是否显示它所拥有的权限信息,也可不指定
            } 
          )

角色继承

use admin;
//角色继承
db.grantRolesToRole(  
                     "testrole",
                     [ "otherrole1","otherrole2" ] // 将 otherrole1、otherrole2 两个角色(假设之前已经创建好)的权限授予testrole角色
                    )
//角色权限回收
db.revokeRolesFromRole(
                         "testrole" ,
                         [ "otherrole2" ] // 撤销testrole角色从otherrole2角色所继承的所有权限
                      )

角色授权

use admin;
db.grantPrivilegesToRole(
    "testrole",
        [
        {
            resource: //权限可操作的资源
               {
                    db:"lidan_1",  // 授予testrole角色具有操作lidan_1库的权限
                    collection:"" // lidan_1库下的集合 如果为"" 表示所有集合
                },                                                 
            actions:  // 权限允许的操作
            [ "createCollection", "dropCollection","convertToCapped"] //权限可进行的操作
        } 
        ]
)

执行完操作后testrole角色便可以对库lidan_1下的所有集合进行 "createCollection", "dropCollection","convertToCapped" 操作。

角色权限回收

use admin;
db.revokePrivilegesFromRole(
      "testrole",
      [
          {
              resource: //权限可操作的资源
              {
                  db:"lidan_1",  // 回收角色对库lidan_1的actions 操作权限
                  collection:"" //  lidan_1库下所有的集合 如果为"" 表示所有集合
              },                                                 
              actions:  // 权限允许的操作
                  [ "createCollection", "dropCollection","convertToCapped"] //需要回收的权限
              } 
      ]
)

执行完操作后testrole角色对库lidan_1下的所有集合无法进行 "createCollection", "dropCollection","convertToCapped" 操作。

删除角色

use admin;
db.dropRole("testrole") // 删除角色比较简单直接指定要删除角色的名称即可

其他关于角色的对应操作请参考文末连接:MongoDB角色管理方法

用户

创建用户

MongoDB 创建用户时可以指定内建角色也可以使用用户自定义角色,DBA 可以根据需求自行决定(注意:用户不允许在local库下创建用户)

user admin;
// 指定内建角色来创建用户
db.createUser(
    {
        user:'mongo', // 用户名
        pwd:'123',    // 密码
        roles:[ 
                  {
                      role:'root',// 通过指定内建角色root 来创建用户
                      db:'admin'  // 指定角色对应的认证数据库,内建角色通常认证库为admin
                  }
              ]
    }
) ;
// 指定自定义角色来创建用户,这里是在admin下创建的用户故认证库也是admin
db.createUser(
    {  
        user:"mongo",// 用户名
        pwd:"123",   // 密码
        roles:["testrole"] //通过指定用户自定义角色来创建用户,注意这里是数组
    }
)

用户创建成功后可以使用如下语句登陆:

mongo   -u mongo -p 123 --host 127.0.0.1 --port 9999 --authenticationDatabase=admin

查看用户

use admin;
db.getUser("mongo") // 查看用户比较简单只需要指定用户名即可

为用户 添加/回收 角色

use admin;
//为用户添加角色
db.grantRolesToUser(
    "mongo", // 用户名
        [ 
            { role: "testrole", // 需要添加的角色名 
                db: "admin" // 角色对应的认证库,即角色创建时所在的数据库
            } 
        ]
)
//对用户的角色进行回收,如果将用户所有的角色都回收完毕,那么用户只有对所属库的连接权限
db.revokeRolesFromUser(
    "mongo", // 用户名
        [ 
            { role: "testrole", // 需要回收的角色名 
                db: "admin" // 角色对应的认证库,即角色创建时所在的数据库
            } 
        ]
)

删除用户

use admin;
db.dropUser("mongo"); // 删除用户比较简单直接指定用户名即可

另外,除上述对用户的操作方法外还有其他对用户的管理方法。这里就不一一列举可详见文末连接:MongoDB 用户管理方法


注意事项

1. 在 MongoDB 中删除库和集合并不会级联删除对应的角色和用户。因此如果想彻底删除对应的业务库因该先删除库及其对应的角色和用户。

2. 如果既想实现精细化权限控制又想简化用户管理,原则上建议只给开发创建一个账户,并且使用 admin 做认证库,这样可避免清理过期业务库而导致无法登陆的问题。

相关连接:

MongoDB 内建角色介绍:

https://docs.mongodb.org/manual/reference/built-in-roles/

MongoDB  权限操作列表:

https://docs.mongodb.org/manual/reference/privilege-actions/#security-user-actions

MongoDB 角色管理方法:

https://docs.mongodb.org/manual/reference/method/js-role-management/

MongoDB  用户管理方法:

https://docs.mongodb.org/manual/reference/method/js-user-management/

t01c2e85c7fa3d32fc1.png

HULK一线技术杂谈

(完)