MongoDB 6.0 單例項基於使用者角色實現授權登入

上古南城發表於2023-12-04

現代資料庫系統能夠儲存和處理大量資料。因此,由任何一個使用者單獨負責處理與管理資料庫相關的所有活動的情況相對較少。通常,不同的資料庫使用者需要對資料庫的某些部分具有不同級別的訪問許可權:某些使用者可能只需要讀取特定資料庫中的資料,而其他使用者則必須能夠插入新文件或修改現有文件。同樣,應用程式可能需要獨特的許可權,僅允許其訪問其執行所需的資料庫部分。

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

# 下載 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"} ]})