對於一個完善的數據庫系統,必然是需要權限控制,當然mongodb也不例外.沒有認證的數據庫已經被證明是******的一個突破口,所以我們無論是出於什麼原因,數據庫認證對於一個生產系統而言,至關重要.
在MongoDB 3.0 以後,用戶登錄的密碼認證機制有:SCRAM-SHA-1(默認,基於加鹽的應答式認證),MONGODB-CR(普通應答式認證,3.6廢棄,4.0刪除),x.509 Certificate(基於證書的SSL/TLS加密認證),LDAP Proxy(基於LDAP系統的鑑權認證,僅企業版支持),Kerberos(基於Kerberos鑑權,僅企業版支持),一般而言,默認的就已經很不錯了.
然後mongodb還有一個內部鑑權策略,主要是防止數據被攔截,有:keyfile(基於SCRAM的應答式認證),x.509 Certificate(基於證書的SSL/TLS加密),對於內部來說,如非特殊情況,使用keyfile就足夠了.
對單臺數據庫增加mongodb認證信息(基本認證知識)
首先要講一講的是,mongodb本身並沒有要求一定要認證登錄,也就是免認證直接登錄是允許的,不過出於安全考慮的話,我們應該更加嚴謹一些,增加認證登錄來使用,但是mongodb又不同於mysql和oracle的授權方式,比較奇怪.下面來看.
首先,也是要打開參數
#打開配置文件 vim /usr/local/mongodb/mongod_data_40001.conf . . . #是否開啓認證模式,現在開啓來使用 auth = true #關閉認證模式,和上面的衝突,之前是開啓的,現在關閉了 #noauth = true #指定集羣認證文件,沒開認證就不用指定,註釋關閉即可,需要用openssl來生成,暫時不研究 #keyFile = /data/mongodb/data/keyfile #指定訪問地址,3.4之前默認全網,之後則默認是127.0.0.1,但是在3.6之後必須制定這個參數才能啓動.配置0.0.0.0代表全網匹配 #bind_ip=0.0.0.0
參數打開了,需要重啓一下登錄
#登錄進去操作 mongo -port=40001 MongoDB shell version v3.4.16-rc0 connecting to: mongodb://127.0.0.1:40001/ MongoDB server version: 3.4.16-rc0 #操作一下 > show dbs 2018-07-17×××5:04:34.009+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }", "code" : 13, "codeName" : "Unauthorized" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1 shellHelper.show@src/mongo/shell/utils.js:788:19 shellHelper@src/mongo/shell/utils.js:678:15 @(shellhelp2):1:1 >
好像報錯了,但是請淡定,這是正常現象,因爲之前默認是免認證,現在開啓了認證,你又沒創建用戶,那肯定是報錯的,不過不用擔心,因爲你原本是沒用戶密碼的,所以是可以新建的.
但是要注意,在初始無賬戶密碼狀態下,只能通過登錄127.0.0.1這個地址你才能創建用戶和密碼,所以就需要注意參數bind_ip的值了.
#進入admin數據庫,也是當前用戶認證庫 > use admin switched to db admin #授權 > db.createUser({user:"adminuser",pwd:"admin123",roles:[{role:"userAdminAnyDatabase",db:"admin"}]}) Successfully added user: { "user" : "adminuser", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] } #再創建多一個試試 > db.createUser({user:"root",pwd:"root123",roles:[{role:"root",db:"admin"}]}) 2018-07-17×××5:05:04.167+0800 E QUERY [thread1] Error: couldn't add user: not authorized on admin to execute command { createUser: "root", pwd: "xxx", roles: [ { role: "root", db: "admin" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 600000.0 } } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype.createUser@src/mongo/shell/db.js:1292:15 @(shell):1:1 >
又報錯了?沒錯,這個時候你已經有用戶了,所以就拒絕你的操作了,你要先認證一下你自己剛建好的用戶了
#認證一下你剛纔創建的用戶 > db.auth("adminuser","admin123") 1 > use admin switched to db admin #再次創建用戶 > db.createUser({user:"root",pwd:"root123",roles:[{role:"root",db:"admin"}]}) Successfully added user: { "user" : "root", "roles" : [ { "role" : "root", "db" : "admin" } ] } > db.auth("root","root123") 1 > show dbs admin 0.000GB local 0.000GB >
這次沒問題了,解決了問題,要想創建更多用戶,甚至細化到個別數據庫來創建用戶,你可以這樣:
use foo; db.createUser({user: 'foo', pwd: 'bar', roles: [{role: 'readWrite', db: 'foo'}]}) db.auth('foo', 'bar')
這裏就涉及一個mongodb特別的知識點---認證庫的概念.
mongodb和別的數據庫不同,認證的信息是細化到庫的,而並不是像mysql,oracle,sql server那樣集中管理,都存放到一個user表.
不同數據庫的用戶可以存放到不同的數據庫,例如:foo庫的用戶foo只存放在foo庫裏面,他不能登錄admin庫,在admin庫裏面是沒有foo用戶信息.同理,admin庫的用戶root是不存在於foo庫的,但是root用戶是能登陸foo的,原因是root用戶的權限更大一些(廢話).但是,你如果把foo庫刪除了,那麼foo用戶也會"順便"被一起刪除了,也就是這個用戶和這個庫也徹底不再了.
所以我們有兩種不同的理念,基於方便管理的概念來說,我們應該集中管理,對於認證庫這個概念,我們的思路應該把它們都建立在admin庫更佳,然後再細分權限,刪除庫有時候可以只是刪除登錄用戶就可以了.基於安全考慮問題,我們不能讓一些廢棄用戶存在,因爲mongodb的特性是沒有create動作,只要有權限,就可以自動創建庫和表的.
然後還有第二個mongodb的知識點---角色權限管理
mongodb創建的用戶是按role角色來區分權限的,跟我們熟悉的oracle和sql server相似(mysql8.0後才支持角色管理),這點倒不是特別了.至於有什麼role選擇呢,你可以show roles看看.我們主要常用的有全局超級管理員userAdminAnyDatabase(也是我們第一個需要創建的用戶權限),readWriteAnyDatabase,readWrite,readAnyDatabase,read,root等.
每一個角色代表着有不同的權限集合,可以細化到每個用戶的權限範圍,例如read就只能find查詢,readWrite就是典型的增刪查改了,還有我們熟悉的root就是完全控制權限了.
mongos> show roles { "role" : "__system", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "backup", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { . . . } { "role" : "read", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "readAnyDatabase", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "readWrite", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "readWriteAnyDatabase", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "restore", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "root", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "userAdmin", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "userAdminAnyDatabase", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] }
這裏就不詳細解析了,大多數和字面意思差不多,非常贊.
既然創建完成了,我們嘗試下帶用戶名的登陸方式:
[root@localhost ~]# mongo 10.21.14.16:40001/admin -u root -p "root123" MongoDB shell version v3.4.16-rc0 connecting to: mongodb://10.21.14.16:40001/admin MongoDB server version: 3.4.16-rc0 > show dbs admin 0.000GB local 0.000GB >
可以正常使用了.
當然了,用戶就必須是可以修改和刪除信息的,這裏不詳細講,附上命令參考
#假設我們新建了一個用戶ttt,這裏就不解析了 >use admin >db.createUser({user:"ttt",pwd:"123",roles:[{role:"root",db:"admin"}]}) >db.auth("ttt","123") #查看現在所有的用戶信息,也就多了個ttt了. > db.system.users.find() { "_id" : "admin.adminuser", "user" : "adminuser", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "jLubKc6ODHMxCnjFeeJuog==", "storedKey" : "l1cL39bnvkw2fecw1DHdKM0TM7s=", "serverKey" : "ZD+EZymr8OhlwMZ/0h25Qn8QHE4=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] } { "_id" : "admin.root", "user" : "root", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "Fx2BRB2ZIJeD2X+bvM0ZIg==", "storedKey" : "Yyo+vcxhv40vNXZwUNfnBTz×××30=", "serverKey" : "th3UbT+/aJcgOXvKx4TFDhX242k=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] } { "_id" : "admin.ttt", "user" : "ttt", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "X58qg8jiiM3ju8v8Cywu8A==", "storedKey" : "MO4lwHhFUn50Ja0/QCHeN4hObRQ=", "serverKey" : "N9iUXgshDP9beTD8pNKfrmtl0V0=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] } #另一個命令也能看到 > db.getUsers() [ { "_id" : "admin.adminuser", "user" : "adminuser", "db" : "admin", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }, { "_id" : "admin.root", "user" : "root", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] }, { "_id" : "admin.ttt", "user" : "ttt", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] } ] #再看看新建的那個用戶 > db.getUser("ttt") { "_id" : "admin.ttt", "user" : "ttt", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] } #我們修改用戶的所有信息,都可以通過下面這條命令實現, #db.updateUser("UESR",{修改信息}) #現在我們需要修改的是密碼 > db.updateUser("ttt",{pwd:"789"}) #修改完成,舊密碼登錄就會報錯 > db.auth("ttt","123") Error: Authentication failed. 0 #新密碼就沒事了 > db.auth("ttt","789") 1 #不過,修改密碼的方式並不是只有一種,下面這種也行 > db.changeUserPassword("ttt","456") #同理,舊密碼登錄就會報錯了 > db.auth("ttt","123") Error: Authentication failed. 0 #新密碼就沒事了 > db.auth("ttt","456") 1 #刪除用戶就很簡單了 > db.dropUser("ttt") true #現在你還想登錄,當然是不行的 > db.getUser("ttt") 2018-07-18T09:38:38.504+0800 E QUERY [thread1] Error: not authorized on admin to execute command { usersInfo: "ttt" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype.getUser@src/mongo/shell/db.js:1518:1 @(shell):1:1 #登錄到其他用戶就行了 > db.auth("root","root123") 1 > db.dropUser("ttt") false > db.getUser("ttt") null > db.getUsers() [ { "_id" : "admin.adminuser", "user" : "adminuser", "db" : "admin", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }, { "_id" : "admin.root", "user" : "root", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] } ] >
關於單臺服務器的認證方式就說到這裏了.
對集羣數據庫增加mongodb認證信息
正如我之前說的,使用mongodb,大部分人不會只用單臺,因爲這就是源生的分佈式數據庫.而使用集羣的認證,比起單臺,則需要增加一個keyfile,當然你也能用另一種,這裏不展開講太多.
我這裏前置條件說明一下,這個時候集羣是已經搭建好了,但是集羣內的用戶還沒有創建起來,因爲你沒有創建keyfile也是走不起來.
首先,要在所有節點配置都打開keyfile,並配上路徑,並確保mongodb有這個文件的權限,無論你是分片集羣,還是單純副本集,在mongos端,config端,share端,都要一起啓用,要不然沒有配置的就會報權限錯誤,如果是副本集而又授權了,你還需要重新做一遍才行.
#打開配置文件 vim /usr/local/mongodb/mongod_data_40001.conf . . . #是否開啓認證模式,現在開啓來使用 auth = true #關閉認證模式,和上面的衝突,之前是開啓的,現在關閉了 #noauth = true #指定集羣認證文件,沒開認證就不用指定,註釋關閉即可,需要用openssl來生成,暫時不研究 keyFile = /data/mongodb/data/keyfile #指定訪問地址,3.4之前默認全網,之後則默認是127.0.0.1,但是在3.6之後必須制定這個參數才能啓動.配置0.0.0.0代表全網匹配 #bind_ip=0.0.0.0
改完之後,沒啓動就先不要急着啓動,而已經啓動了的,也不要急着重啓,稍等再重啓.
然後,我們要生成這個keyfile文件
#用openssl命令生成一個64位的祕鑰文件 openssl rand -base64 64 > keyfile #把它權限設成600,不然就報錯權限過高的錯誤 chmod 600 keyfile #把他移到目的目錄 mv keyfile /data/mongodb/data/ #記得把權限搞一搞 chown mongodb:mongodb /data/mongodb/data/keyfile
有些人好奇,爲什麼是64位的祕鑰文件?沒錯,真的可以更多,或者更少,例如:
openssl rand -base64 741 >keyfile openssl rand -base64 16 >keyfile
這些都可以用,但是,要考慮系統性能問題和數據安全的問題.祕鑰文件越複雜,那麼你內部系統的加密與解密就需要更耗費資源,尤其在高併發環境,可以講比較悲催.但是太簡單,那麼你的數據被破解的機率就更高,安全性就談不上了.我這裏只是折衷,所以是64,各位有興趣可以根據實際情況來設置.
再然後,重新啓動各節點,或者說你沒啓動的話,就現在啓動了,按以下順序重啓所有服務
config副本集:先主庫,再從庫,讓他慢慢切換
router服務:隨便重啓
shard副本集:先主庫,再從庫,讓他慢慢切換
最後,就開始創建用戶授權了,就和上面一樣操作就可以了
#如果我們沒有做好副本集或集羣,那就還需要先做,詳細解析我就不做了. > config = {_id:"sljd_shard1",members:[{_id:0, host:"172.25.40.80:40001" },{_id:1, host:"172.25.40.81:40001" },{_id:2, host:"172.25.40.82:40001" }]} > rs.initiate(config) > rs.status() #然後進入admin數據庫,也是當前用戶認證庫 shard1:PRIMARY> use admin switched to db admin #授權 shard1:PRIMARY> db.createUser({user:"root",pwd:"admin123",roles:[{role:"userAdminAnyDatabase",db:"admin"}]}) Successfully added user: { "user" : "adminuser", "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] } #然後登陸 shard1:PRIMARY> db.auth("root","admin123") #或者 mongo 172.25.40.80:40001/admin -u root -p "admin123"
然後,我們看到用戶了
#登陸上主庫,注意認證庫 mongo 172.25.40.80:40001/admin -u root -p "admin123" #在主庫上查看一下用戶情況 shard1:PRIMARY> db.getUsers() [ { "_id" : "admin.root", "user" : "root", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] } ] #登陸上從庫.注意認證庫 mongo 172.25.40.81:40001/admin -u root -p "admin123" #要多做一步,允許在從庫執行查詢,不然會報錯 rs.slaveOk() #再在從庫執行一下 shard1:SECONDARY> db.getUsers() [ { "_id" : "admin.root", "user" : "root", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] } ]
好了,那就沒問題了.