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

2023-12-04 12:04:11

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

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