建立mongodb的登錄認證功能

對於一個完善的數據庫系統,必然是需要權限控制,當然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"
            }
        ]
    }
]

好了,那就沒問題了.







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章