MongoDB 用戶管理

背景

最近在掃盲MongoDB 4.2 的相關知識點,順便記錄下日常的一些操作。包括:用戶管理、索引管理、引擎管理、副本集管理、分片管理等。本文對MongoDB的用戶管理進行說明。

本文MongoDB的配置文件模板:

systemLog:
   verbosity: 0
   quiet: false
   traceAllExceptions: false
   syslogFacility: user
   path: /data/mongodb/logs/mongodb.log
   logAppend: true
   logRotate: rename
   destination: file
   timeStampFormat: iso8601-local
   component:
      accessControl:
         verbosity: 0
      command:
         verbosity: 0
      control:
         verbosity: 0
      ftdc:
         verbosity: 0
      geo:
         verbosity: 0
      index:
         verbosity: 0
      network:
         verbosity: 0
      query:
         verbosity: 0
      replication:
         verbosity: 0
         election:
            verbosity: 0
         heartbeats:
            verbosity: 0
         initialSync:
            verbosity: 0
         rollback:
            verbosity: 0
      sharding:
          verbosity: 0
      storage:
         verbosity: 0
         journal:
            verbosity: 0
         recovery:
            verbosity: 0
      transaction:
         verbosity: 0
      write:
         verbosity: 0

processManagement:
   fork: true
   pidFilePath: /data/mongodb/data/mongodb.pid
#   timeZoneInfo: <string>

net:
   port: 27017
   bindIp: 0.0.0.0
   maxIncomingConnections: 65536
   wireObjectCheck: true
   ipv6: false
   unixDomainSocket:
      enabled: true
      pathPrefix: /tmp
      filePermissions: 0700
   tls:
      mode: disabled
   compression:
      compressors: snappy,zstd,zlib
   serviceExecutor: synchronous

#security:
#   keyFile: <string>
#   clusterAuthMode: keyFile
#   authorization: enabled
#   transitionToAuth: false
#   javascriptEnabled: true
#   redactClientLogData: <boolean>
#   clusterIpSourceWhitelist:
#     - 127.0.0.1
#     - ::1

storage:
   dbPath: /data/mongodb/data/
   indexBuildRetry: true
   journal:
      enabled: true
      commitIntervalMs: 500
   directoryPerDB: true
   syncPeriodSecs: 60
   engine: wiredTiger
   wiredTiger:
      engineConfig:
#         cacheSizeGB: <number>
         journalCompressor: snappy
         directoryForIndexes: false
operationProfiling:
   # 指定應分析哪些操作,默認off:分析器已關閉,並且不收集任何數據;slowOp:收集比slowms的時間長的數據;all:收集所有操作的數據
   mode: slowOp
   slowOpThresholdMs: 100
   slowOpSampleRate: 1

replication:
   # 複製日誌(oplog)的最大大小(以M為單位),僅適用於mongod。操作日誌通常是可用磁碟空間的5%,更改正在運行的副本集成員的操作日誌大小,請使用replSetResizeOplog管理命令。
   oplogSizeMB: 100
   # replSetName: <string>

#sharding:
   # 分片群集中扮演的角色,可選值configsvr:配置伺服器,埠27019開始;shardsvr:分片,埠27018開始;需要將實例部署為副本集成員,使用replSetName設置並指定副本集的名稱,僅適用於mongod。
   # clusterRole: <string>
   # configDB: <string>

View Code

用戶管理

在一個新的實例上,創建的第一個用戶應該是具有管理其他用戶許可權的用戶管理員。在MongoDB部署上啟用訪問控制會強制執行身份驗證,要求用戶認證自己,用戶只能執行根據其角色確定的操作。具體的用戶管理可以看官方文檔或則MongoDB用戶創建,本文做下補充。

1,創建用戶:db.createUser(user, writeConcern)

user:包含有關要創建的用戶的身份驗證和訪問資訊。
writeConcern:可選,創建操作的寫關注級別。 writeConcern文檔的欄位與getLastError命令的欄位相同。

MongoDB4.2開始,可以使用passwordPrompt()方法來提示輸入密碼,不需要在命令中指定密碼。但仍然可以像使用早期版本的MongoDB一樣直接指定密碼。格式:

{
  user: "<name>",   -- 用戶名
  pwd: passwordPrompt(),    -- 提示輸入密碼,也可以直接寫密碼
  customData: { <any information> },  -- 可選,可用於存儲與此特定用戶關聯的任何數據。 例如,這可以是用戶的全名或員工ID。
  roles: [
    { role: "<role>", db: "<database>" } | "<role>",
    ...
  ],     -- 授予用戶的角色。 可以指定一個空數組[]來創建沒有角色的用戶
  authenticationRestrictions: [
     {
       clientSource: ["<IP>" | "<CIDR range>", ...],
       serverAddress: ["<IP>" | "<CIDR range>", ...]
     },
     ...
  ],  -- 可選,伺服器對創建的用戶強制執行的身份驗證限制,指定允許用戶連接到伺服器或伺服器可以接受用戶的IP地址和CIDR(無類別域間路由)範圍的列表
  mechanisms: [ "<SCRAM-SHA-1|SCRAM-SHA-256>", ... ],  --可選,指定特定的SCRAM機制或用於創建SCRAM用戶憑據的機制
  passwordDigestor: "<server|client>"  --可選,指伺服器還是客戶端提取密碼。
}

其中在roles欄位中,可以指定內置角色用戶定義的角色:

Built-In Roles(內置角色):
    1. 資料庫用戶角色:read、readWrite;
    2. 資料庫管理角色:dbAdmin、dbOwner、userAdmin;
    3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
    4. 備份恢復角色:backuprestore5. 所有資料庫角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
    6. 超級用戶角色:root  
    // 這裡還有幾個角色間接或直接提供了系統超級用戶的訪問(dbOwner 、userAdmin、userAdminAnyDatabase)
    7. 內部角色:__system

具體許可權

read:允許用戶讀取指定資料庫
readWrite:允許用戶讀寫指定資料庫
dbAdmin:允許用戶在指定資料庫中執行管理函數,如索引創建、刪除,查看統計或訪問system.profile
userAdmin:允許用戶向system.users集合寫入,可以找指定資料庫里創建、刪除和管理用戶
clusterAdmin:只在admin資料庫中可用,賦予用戶所有分片和複製集相關函數的管理許可權。
readAnyDatabase:只在admin資料庫中可用,賦予用戶所有資料庫的讀許可權
readWriteAnyDatabase:只在admin資料庫中可用,賦予用戶所有資料庫的讀寫許可權
userAdminAnyDatabase:只在admin資料庫中可用,賦予用戶所有資料庫的userAdmin許可權
dbAdminAnyDatabase:只在admin資料庫中可用,賦予用戶所有資料庫的dbAdmin許可權。
root:只在admin資料庫中可用。超級帳號,超級許可權

創建用戶:

db.createUser(
{
user:'dba',
pwd:passwordPrompt(),
roles:[{role:'root',db:'admin'}],
customData:{name:'運維帳號'},
authenticationRestrictions: [ {
        clientSource: ["192.168.163.134"],
        serverAddress: ["192.168.163.134"]
     } ]
}
)

說明創建一個用戶名為dba的管理角色(root)的帳號,且備註為運維帳號,並只能在IP為192.168.163.134的主機上訪問。

use xxx
db.createUser(
{
user:'zjy',
pwd:passwordPrompt(),
roles:[{role:'readAnyDatabase',db:'admin'},
"readWrite"
],
customData:{name:'運維帳號'},
authenticationRestrictions: [ {
        clientSource: ["192.168.163.134"],
        serverAddress: ["192.168.163.134"]
     } ]
},
{ w: "majority" , wtimeout: 5000 }
)

說明創建一個用戶名為zjy的帳號,對當前庫(xxx)有readWrite許可權,對指定庫(admin)有readAnyDatabase許可權;且備註為運維帳號,並只能在IP為192.168.163.134的主機上訪問。

關於帳號許可權的一些說明可以看MongoDB用戶創建

2,修改用戶密碼:db.changeUserPassword(username,password)

db.changeUserPassword('user','pwd')

3,查看指定用戶:db.getUser(username, args)

格式:

db.getUser( "<username>", {
   showCredentials: <Boolean>,
   showPrivileges: <Boolean>,
   showAuthenticationRestrictions: <Boolean>,
   filter: <document>
} )

其中”<username>”表示資料庫用戶,其他的參數都是可選項:

  • showCredentials:查看用戶證書
  • showPrivileges:查看用戶許可權
  • showAuthenticationRestrictions:查看用戶限制條件
> db.getUser('dba')
{
    "_id" : "admin.dba",
    "userId" : UUID("6a1c2217-0c5b-4aa6-a29b-de50ac1d84ae"),
    "user" : "dba",
    "db" : "admin",
    "customData" : {
        "name" : "運維帳號abc"
    },
    "roles" : [
        {
            "role" : "root",
            "db" : "admin"
        }
    ],
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

> db.getUser('dba',{showPrivileges:1})
...
...

4,查看當前庫的所有用戶:db.getUsers(<options>)

> db.getUsers() == show users
[
    {
        "_id" : "xxx.zjy",
        "userId" : UUID("2ad9f4c9-e156-4e71-b8f9-3915beb322e8"),
        "user" : "zjy",
        "db" : "xxx",
        "customData" : {
            "name" : "運維帳號"
        },
        "roles" : [
            {
                "role" : "readAnyDatabase",
                "db" : "admin"
            },
            {
                "role" : "readWrite",
                "db" : "xxx"
            }
        ],
        "mechanisms" : [
            "SCRAM-SHA-1",
            "SCRAM-SHA-256"
        ]
    }
]

5,移除、刪除指定用戶:db.removeUser(username)db.dropUser(username, writeConcern)

> db.removeUser('zjy')
WARNING: db.removeUser has been deprecated, please use db.dropUser instead  --不推薦使用
true

> db.dropUser('accountAdmin01',{w: "majority", wtimeout: 5000})
true

如果在副本集上運行,則默認情況下使用多數寫關注來執行db.dropUser()。

6,刪除當前庫所有用戶:db.dropAllUsers(writeConcern)

> db.dropAllUsers({w: "majority", wtimeout: 5000})
NumberLong(4)

7,更新指定用戶:db.updateUser(username, update, writeConcern)

格式:

db.updateUser(
   "<username>",
   {
     customData : { <any information> },
     roles : [
       { role: "<role>", db: "<database>" } | "<role>",
       ...
     ],
     pwd: passwordPrompt(),      // Or  "<cleartext password>"
     authenticationRestrictions: [
        {
          clientSource: ["<IP>" | "<CIDR range>", ...],
          serverAddress: ["<IP>", | "<CIDR range>", ...]
        },
        ...
     ],
     mechanisms: [ "<SCRAM-SHA-1|SCRAM-SHA-256>", ... ],
     passwordDigestor: "<server|client>"
   },
   writeConcern: { <write concern> }
)

上面各個參數的意思和createUser一致。

> db.updateUser(
... 'user123',
... {
... pwd:passwordPrompt(),
... roles:['read'],
... customData:{name:'運維帳號'},
... authenticationRestrictions: [ {
...         clientSource: ["192.168.163.134"],
...         serverAddress: ["192.168.163.134"]
...      } ]
... },
... { w: "majority" , wtimeout: 5000 }
... )

8,授權role給指定用戶:db.grantRolesToUser(username, roles, writeConcern)

格式:

db.grantRolesToUser( "<username>", [ <roles> ], { <writeConcern> } )
> show users;
{
    "_id" : "products.user123",
    "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"),
    "user" : "user123",
    "db" : "products",
    "roles" : [
        {
            "role" : "read",
            "db" : "products"
        }
    ],
    "customData" : {
        "name" : "運維帳號"
    },
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}
> db.grantRolesToUser('user123',['readWrite',{role:'read',db:'test'}])
> show users; { "_id" : "products.user123", "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"), "user" : "user123", "db" : "products", "roles" : [ { "role" : "readWrite", "db" : "products" }, { "role" : "read", "db" : "test" }, { "role" : "read", "db" : "products" } ], "customData" : { "name" : "運維帳號" }, "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

說明:在已有用戶(user123)的許可權下,新增當前庫(products)的readWrite role和test庫的read role。

9,移除指定用戶的role:db.revokeRolesFromUser()

格式:

db.revokeRolesFromUser( "<username>", [ <roles> ], { <writeConcern> } )
> show users;
{
    "_id" : "products.user123",
    "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"),
    "user" : "user123",
    "db" : "products",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "products"
        },
        {
            "role" : "read",
            "db" : "test"
        },
        {
            "role" : "read",
            "db" : "products"
        }
    ],
    "customData" : {
        "name" : "運維帳號"
    },
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

> db.revokeRolesFrom
db.revokeRolesFromRole(  db.revokeRolesFromUser(

> db.revokeRolesFromUser('user123',[{role:'read',db:'test'},'readWrite'])

> show users; { "_id" : "products.user123", "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"), "user" : "user123", "db" : "products", "roles" : [ { "role" : "read", "db" : "products" } ], "customData" : { "name" : "運維帳號" }, "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

說明:移除已有用戶(user123)在當前庫(products)的readWrite role和test庫的read role許可權。

10,用戶認證:db.auth(username,password)

> db.auth('dba','DBA')
1

以上說明的role都是內置角色,用戶自定義角色可以看官方文檔說明。

角色管理

1,創建角色:db.createRole(role, writeConcern)

格式:

{
  role: "<name>",      --新角色的名稱
  privileges: [
     { resource: { <resource> }, actions: [ "<action>", ... ] },
     ...
  ],      --許可權,由resource和actions組成。空數組表示無許可權
  roles: [
     { role: "<role>", db: "<database>" } | "<role>",
      ...
  ],     --角色的數組,此角色從中繼承特權,空數組表示沒有繼承角色
  authenticationRestrictions: [
    {
      clientSource: ["<IP>" | "<CIDR range>", ...],
      serverAddress: ["<IP>" | "<CIDR range>", ...]
    },
    ...
  ]    --驗證限制
}

resource表示資源,包括database或collection也可以是database和collection的組合:

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

actions表示許可權操作,”actions” 定義了”user”能夠對 “resource document”執行的操作:

find、insert、remove、update

privilege表示許可權,”privilege” 是一組”resource” 和 “actions” 的組,關於privileges的詳細資訊見官方說明,裡面有各個命令的許可權說明

>db.createRole(
   {role:"zhoujy", //角色名稱
       privileges: [ // 許可權集
        {resource: //資源 
         {db:"xxx",     // 創建的zhoujy角色具有對xxx庫的操作許可權,具體許可權見actions
          collection:""   // xxx庫下對應的集合名 如果為"" 表示所有集合
         },
          actions: [ "find", "insert", "remove","update" ]   //角色可進行的操作,注意這裡是一個數組
        }
                   ],
         roles: [{ role: "read", db: "admin" }] // 是否繼承其他的角色,如果指定了其他角色那麼新創建的角色自動繼承對應其他角色的所有許可權,該參數必須顯示指定
   },
  { w: "majority" , wtimeout: 5000 }
)

說明:創建了一個zhoujy的role,可以像內置角色一樣來使用。

2,查看指定角色資訊:db.getRole(rolename, args)

參數:

showBuiltinRoles:可選,在輸出中返回內置角色資訊

showPrivileges:可選,在輸出中返回許可權資訊

> db.getRole('zhoujy')
{
    "role" : "zhoujy",
    "db" : "admin",
    "isBuiltin" : false,
    "roles" : [ ],
    "inheritedRoles" : [ ]
}

> db.getRole('zhoujy',{showPrivileges: true})
{
    "role" : "zhoujy",
    "db" : "admin",
    "isBuiltin" : false,
    "roles" : [ ],
    "inheritedRoles" : [ ],
    "privileges" : [
        {
            "resource" : {
                "db" : "xxx",
                "collection" : ""
            },
            "actions" : [
                "find",
                "insert",
                "remove",
                "update"
            ]
        }
    ],
    "inheritedPrivileges" : [
        {
            "resource" : {
                "db" : "xxx",
                "collection" : ""
            },
            "actions" : [
                "find",
                "insert",
                "remove",
                "update"
            ]
        }
    ]
}

3,查看當前資料庫中所有的角色資訊:db.getRoles()

不帶參數運行,返回資料庫用戶定義角色的繼承資訊,參數:

rolesInfo:1,返回所有用戶定義的角色資訊

showPrivileges:true,返回所有用戶的許可權資訊

showBuiltinRoles:true,返回內置角色資訊

>db.getRoles(
    {
      rolesInfo: 1,
      showPrivileges:true,
      showBuiltinRoles: true
    }
)

4,角色授權:db.grantPrivilegesToRole(rolename, privileges, writeConcern)

> db.grantPrivilegesToRole(
  "zhoujy",
  [
    {
      resource: { db: "products", collection: "" },
      actions: [ "insert","find"  ]
    }
  ],
  { w: "majority" }
)

說明:在已有的role下面,再追加許可權。

5,角色許可權回收:db.revokePrivilegesFromRole(rolename, privileges, writeConcern)

> db.revokePrivilegesFromRole(
  "zhoujy",
  [
    {
      resource: { db: "products", collection: "" },
      actions: [ "insert" ]
    }
  ],
  { w: "majority" }
)

說明:在已有的role下面,刪除許可權。

6,角色繼承:db.grantRolesToRole(rolename, roles, writeConcern)

> db.grantRolesToRole(  
          "zhoujy",
           [ "zhoujy1","zhoujy2" ],
           { w: "majority" , wtimeout: 5000 }
                    )

說明:將 zhoujy1、zhoujy2 兩個角色(假設之前已經創建好)的許可權授予zhoujy角色

7,繼承角色回收: db.revokeRolesFromRole(rolename, roles, writeConcern)

db.revokeRolesFromRole(
          "zhoujy" ,
          [ "zhoujy2" ]
                      ) 

說明:撤銷zhoujy角色從zhoujy2角色所繼承的所有許可權

8,更新角色:db.updateRole(rolename, update, writeConcern)

格式:

    "<rolename>",  --角色名
    {
      privileges:
          [
            { resource: { <resource> }, actions: [ "<action>", ... ] },
            ...
          ],  --許可權
      roles:
          [
            { role: "<role>", db: "<database>" } | "<role>",
            ...
          ],   --角色
      authenticationRestrictions:
          [
            {
              clientSource: ["<IP>" | "<CIDR range>", ...],
              serverAddress: ["<IP>", | "<CIDR range>", ...]
            },
            ...
          ]
    },    -- 限制
    { <writeConcern> } --寫關注
)
> db.updateRole(
   "zhoujy",
   {
      privileges: 
      [
        {
        resource:{db:"xxx", collection:""},
        actions: [ "find", "insert", "remove","update" ]
        }
       ],
      roles: []
   },
  { w: "majority" , wtimeout: 5000 }
)

9,刪除角色:db.dropRole(rolename, writeConcern)

> db.dropRole('myClusterwideAdmin', { w: "majority" })
true

10,刪除當前庫所有用戶自定義的角色:db.dropAllRoles(writeConcern)

db.dropAllRoles( { w: "majority" } )

到此,關於用戶管理的已經介紹完,更多的詳細資訊可以見官網說明,或可以查閱MongoDB 許可權控制系統簡介需要注意的是,需要開啟伺服器參數中的security.authorization:true,才會使用用戶驗證(db.auth())。

總結

本文大致介紹了MongoDB用戶管理的使用說明,後續根據需要會持續更新本文。關於用戶管理的更多說明可以看官方文檔

參考文章:

MongoDB 3.0 用戶創建

MongoDB 許可權控制系統簡介