现代数据库系统能够存储和处理大量数据。因此,由任何一个用户单独负责处理与管理数据库相关的所有活动的情况相对较少。通常,不同的数据库用户需要对数据库的某些部分具有不同级别的访问权限:某些用户可能只需要读取特定数据库中的数据,而其他用户则必须能够插入新文档或修改现有文档。同样,应用程序可能需要独特的权限,仅允许其访问其运行所需的数据库部分。

MongoDB采用强大的机制来控制对数据库系统的访问和权限,称为基于角色的访问控制(RBAC)。在本文中,您将了解RBAC的工作原理、最小权限原则的含义和目的,以及如何在实践中使用 MongoDB 的访问权限功能。

访问控制(也称为授权)是一种安全技术,涉及确定谁可以访问哪些资源。

为了更好地理解 MongoDB 中的访问控制,首先将其与另一个不同但密切相关的概念进行区分:身份验证。身份验证是确认用户或客户端是否确实是他们声称的身份的过程。另一方面,授权涉及为给定用户或用户组设置规则,以定义他们可以执行哪些操作以及他们可以访问哪些资源。

MongoDB 中的身份验证

在许多数据库管理系统中,用户仅通过用户名和密码对进行识别。当使用有效凭据连接到数据库时,用户将经过身份验证并被授予与该用户关联的访问级别。在这种方法中,用户目录是扁平的,这意味着对于整个数据库服务器,每个用户名必须是唯一的。

相比之下,MongoDB 采用更复杂的用户目录结构。在 MongoDB 中,用户不仅可以通过用户名来识别,还可以通过创建用户的数据库来识别。对于每个用户,创建他们的数据库称为该用户的身份验证数据库。

这意味着在 MongoDB 中,可以有多个用户具有相同的用户名(例如sammy),只要它们是在不同的身份验证数据库中创建的。要以用户身份进行身份验证,您不仅必须提供用户名和密码,还必须提供与该用户关联的身份验证数据库的名称。

人们可能会假设在给定身份验证数据库中创建的用户将具有仅对该特定数据库可用的访问权限,但事实并非如此。每个用户,无论是在哪个身份验证数据库中创建的,都可以具有跨不同数据库分配的权限。

MongoDB 中的授权(基于角色的访问控制)

在 MongoDB 中,您可以通过称为基于角色的访问控制(通常缩写为RBAC)的机制来控制谁有权访问数据库上的哪些资源以及访问的程度。

在基于角色的访问控制中,用户无权直接对资源执行操作,例如将新文档插入数据库或查询特定集合。这将使安全策略难以管理并与系统中的许多用户保持一致。相反,允许对特定资源执行操作的规则被分配给角色。

将角色视为给定用户的工作或职责之一可能会有所帮助。例如,经理可能对公司 MongoDB 实例中的每个文档具有读写访问权限,而销售分析师可能仅对销售记录具有只读访问权限。

角色是用一组一个或多个权限来定义的。每个权限都包含一个操作(例如创建新文档、从文档检索数据或创建和删除用户)以及可以执行该操作的资源(例如名为 的数据库或名为 的集合reports)orders。就像在现实生活中一样,一家公司可能有许多销售分析师和员工,他们承担多个职责,在 MongoDB 中,许多用户可以分配给同一角色,并且单个用户可以被授予多个角色。

角色通过角色名称和数据库的组合来标识,因为每个角色(在数据库中创建的角色除外admin)只能包含应用于其自己数据库的权限。通过向用户授予在其身份验证数据库之外的数据库中定义的角色,可以向用户授予对多个数据库进行操作的权限。可以在创建用户时或此后的任何时间授予角色。还可以随意撤销角色成员资格,从而可以轻松地将用户管理与访问权限管理分离。

MongoDB 提供了一组内置角色,描述数据库系统中常用的权限,例如read授予只读访问权限、readWrite授予读写权限或dbOwner授予对给定数据库的完全管理权限。对于更具体的场景,还可以使用自定义权限集创建用户定义的角色。

基于角色的访问控制可以为用户分配执行各自任务所需的最低、精确级别的访问权限。这是一种重要的安全实践,称为最小特权原则。

  • 绑定IP地址

mongod 参数:--bind_ip << ip address >>

默认值是所有的IP地址都能访问,该参数指定MongoDB对外提供服务的绑定IP地址,用于监听客户端Application的连接,客户端只能使用绑定的IP地址才能访问mongod,其他IP地址是无法访问的。

  • 设置监听端口

mongod 参数:--port

MongoDB默认监听的端口是27017,该参数显式指定MongoDB实例监听的TCP端口,只有当客户端Application连接的端口和MongoDB实例监听的端口一致时,才能连接到MongoDB实例。

  • 启用用户验证

mongod 参数:--auth

默认值是不需要验证,即 --noauth,该参数启用用户访问权限控制;当mongod使用该参数启动时,MongoDB会验证客户端连接的账户和密码,以确定其是否有访问的权限。如果认证不通过,那么客户端不能访问MongoDB的数据库。

  • 权限认证

mongo 参数:-u << username >> -p << password >>
mongo 参数:--authenticationDatabase 指定创建User的数据库;在特定的数据库中创建User,该DB就是User的authentication database。

在连接mongo时,使用参数 --authenticationDatabase,会认证 -u 和 -p 参数指定的账户和密码。如果没有指定验证数据库,mongo使用连接字符串中指定的DB作为验证数据块。

准备环境

[root@MongoDB-Server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@MongoDB-Server ~]# uname -r
3.10.0-1160.el7.x86_64

[root@MongoDB-Server ~]# setenforce 0
[root@MongoDB-Server ~]# sed -i.bak '7s/enforcing/disabled/' /etc/selinux/config

[root@MongoDB-Server ~]# systemctl stop firewalld
[root@MongoDB-Server ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:firewalld(1)
[root@MongoDB-Server ~]# firewall-cmd --state
not running

安装 MongoDB 6.0

  • 前文《》已使用过yum源安装,则本文使用二进制安装包。
# 下载 MongoDB, Mongosh
[root@MongoDB-Server ~]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-6.0.11.tgz
[root@MongoDB-Server ~]# wget https://downloads.mongodb.com/compass/mongosh-2.0.2-linux-x64.tgz

# 解压缩
[root@MongoDB-Server ~]# mkdir /data/apps/ -p
[root@MongoDB-Server ~]# tar -xf mongodb-linux-x86_64-rhel70-6.0.11.tgz -C /data/apps/
[root@MongoDB-Server ~]# tar -xf mongosh-2.0.2-linux-x64.tgz -C /data/apps/

# 重命名目录
[root@MongoDB-Server ~]# cd /data/apps/
[root@MongoDB-Server apps]# ls
mongodb-linux-x86_64-rhel70-6.0.11  mongosh-2.0.2-linux-x64
[root@MongoDB-Server apps]# mv mongodb-linux-x86_64-rhel70-6.0.11 mongodb
[root@MongoDB-Server apps]# mv mongosh-2.0.2-linux-x64 mongosh
[root@MongoDB-Server apps]# ls
mongodb  mongosh

# 环境变量
[root@MongoDB-Server ~]# ln -s /data/apps/mongodb/bin/* /usr/local/bin/
[root@MongoDB-Server ~]# ln -s /data/apps/mongosh/bin/* /usr/local/bin/

[root@MongoDB-Server ~]# ls -l /usr/local/bin/
lrwxrwxrwx 1 root root 38 Nov  1 11:49 install_compass -> /data/apps/mongodb/bin/install_compass
lrwxrwxrwx 1 root root 29 Nov  1 11:49 mongod -> /data/apps/mongodb/bin/mongod
lrwxrwxrwx 1 root root 29 Nov  1 11:49 mongos -> /data/apps/mongodb/bin/mongos
lrwxrwxrwx 1 root root 30 Nov  1 11:49 mongosh -> /data/apps/mongosh/bin/mongosh
lrwxrwxrwx 1 root root 42 Nov  1 11:49 mongosh_crypt_v1.so -> /data/apps/mongosh/bin/mongosh_crypt_v1.so


# 创建Mongod相关目录
[root@MongoDB-Server ~]# useradd -r -M /var/lib/mongo -s /bin/false
[root@MongoDB-Server ~]# install -d /var/lib/mongo /var/log/mongodb -o mongod -g mongod

[root@MongoDB-Server ~]# ls -dl /var/lib/mongo /var/log/mongodb
drwxr-xr-x 2 mongod mongod 6 Nov  1 12:22 /var/lib/mongo
drwxr-xr-x 2 mongod mongod 6 Nov  1 12:22 /var/log/mongodb

# 使用mongod用户启动/停止mongod服务(命令行方式)
[root@MongoDB-Server ~]# su mongod -s /bin/bash -c "mongod -fork -dbpath /var/lib/mongo/ -logpath /var/log/mongodb/mongod.log -logappend"
[root@MongoDB-Server ~]# su mongod -s /bin/bash -c "mongod -fork -dbpath /var/lib/mongo/ -logpath /var/log/mongodb/mongod.log -logappend --shutdown"

登录Mongodb,创建用户

[root@MongoDB-Server ~]# mongosh
test> show dbs
admin   40.00 KiB
config  60.00 KiB
local   72.00 KiB

test> use admin
switched to db admin

admin> show collections
system.version

# 创建root用户
admin> db.createUser({user:"root", pwd:"root@123", roles:[{role:"root", db:"admin"}]})

# 创建admin用户
> db.createUser({
      user: "myAdmin",
      pwd: passwordPrompt(), // or cleartext password
      roles: [
        { role: "userAdminAnyDatabase", db: "admin" },
        { role: "readWriteAnyDatabase", db: "admin" }
      ]
    })

开启认证方式

# 方式一:启动时配置--auth参数开启
[root@MongoDB-Server ~]# su mongod -s /bin/bash -c "mongod -fork -dbpath /var/lib/mongo/ -logpath /var/log/mongodb/mongod.log -logappend --auth"

# 方式二:配置文件中开启
[root@MongoDB-Server ~]# cat /etc/mongod.conf
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# Where and how to store data.
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true

# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.

# 如果你没有可用的用户,请勿配置该参数
security:
    authorization: enabled

# 使用mongod用户启动/停止mongod服务(配置文件方式)
[root@MongoDB-Server ~]# su mongod -s /bin/bash -c "nohup mongod -f /etc/mongod.conf &> /dev/null &"
[root@MongoDB-Server ~]# su mongod -s /bin/bash -c "mongod -f /etc/mongod.conf --shutdown &> /dev/null"

# 验证用户账号及其密码
[root@MongoDB-Server ~]# mongosh
test> use admin      # 切换至admin库
switched to db admin

admin> show dbs    # 提示需身份验证
MongoServerError: command listDatabases requires authentication
admin> db.system.users.find()
MongoServerError: command find requires authentication

admin> db.auth("root","root@123")
{ ok: 1 }    # 认证成功

admin> show dbs
admin   180.00 KiB
config   72.00 KiB
local    72.00 KiB

# 连接时进行身份验证
[root@MongoDB-Server ~]# mongosh --port 27017  --authenticationDatabase "admin" -u "myAdmin" -p

# 连接后进行身份验证
[root@MongoDB-Server ~]# mongosh --port 27017
> use admin
admin> db.auth("myAdmin", passwordPrompt()) // or cleartext password

MongoDB Roles(内置角色)

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

具体角色

  • Read:允许用户读取指定数据库
  • readWrite:允许用户读写指定数据库
  • dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
  • userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
  • clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
  • readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
  • readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
  • userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
  • dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
  • root:只在admin数据库中可用。超级账号,超级权限

用户管理

# 查询单用户
db.getUser("myUser")
db.system.users.find({user:"myUser"})

# 查询所有用户
show users
db.getUsers()
db.system.users.find()

# 身份验证
db.auth( <myUser>, passwordPrompt() )    # 提示输入密码
db.auth( <myUser>, <myUserpasswd> )          # 明文密码
# 返回:1为身份验证成功

# 创建新用户,授权myAdmin用户所有数据库的读写及创建、删除和管理用户的权限
use admin
db.createUser(
   {
     user: "myAdmin",
     pwd: passwordPrompt(),  // Or  "<cleartext password>"
    roles: [
      { role: "userAdminAnyDatabase", db: "admin" },
      { role: "readWriteAnyDatabase", db: "admin" }
   }
)

# 普通用户,授权myUser用户对myDBName的读写操作,该用户只能从192.0.2.0段连接到198.51.100.0段
use admin
  db.createUser(
     {
       user: "myUser",
       pwd: passwordPrompt(),      // Or  "<cleartext password>"
       roles: [ { role: "readWrite", db: "myDBName" } ],
       authenticationRestrictions: [ {
          clientSource: ["192.0.2.0"],
          serverAddress: ["198.51.100.0"]
       } ]
     }
  )

# 修改用户密码
use admin
db.changeUserPassword("myUser", passwordPrompt())
db.changeUserPassword("myUser", "myUserpasswd")

db.updateUser("myUser", {pwd:"myUserpasswd"})

# 删除单个用户
use admin
db.dropUser("myUser")
db.system.users.remove({user:"myUser"})
db.removeUser()    // 旧版本,已弃用

# 删除所有用户
db.dropAllUsers()     
db.system.users.remove({})

角色管理

# 授权myUser用户对myDBName数据库的读写操作
db.grantRolesToUser("myUser", [{role: "readWrite", db: "myDBName"}])

# 撤销myUser用户对myDBName数据库的读写操作
db.revokeRolesFromUser("myUser", [{role: "readWrite", db: "myDBName"}])

# 修改myUser用户仅对myDBName数据库只读操作
db.updateUser("myUser", {roles: [ {role: "read", db: "myDBName"} ]})