文章轉自:http://shift-alt-ctrl.iteye.com/blog/2262709
Security對任何程序而言都非常重要,mongodb中提供了多種機制來保證數據安全性,集羣中的members只有提供正確的認證信息才能彼此建立鏈接,客戶端訪問數據時,不僅認證信息正確(Authentication),還需要具有一定的訪問權限(privilege)才能操作數據。在production環境中,我們建議所有的mongod都開啓授權驗證。
1、開啓訪問控制(Access Control)、指定認證機制,mongodb提供了多種認證機制,當然也可以選擇現有的第三方框架。最終認證,要求所有的客戶端和Servers必須提供有效的憑證才能加入集羣系統。
2、mongodb和其他存儲引擎一樣,支持多用戶、分權限,可以限定每個用戶所能訪問的database、操作類型等。
3、mongodb支持TLS/SSL,即安全的transport,可以對connection的通訊數據進行加密;在客戶端與mongos、mongod之間,以及集羣中mongod之間,都可以使用TLS/SSL鏈接。
4、我們在mongodb的配置文件中會看到“bindIp”參數,表示mongodb的Socket綁定在本地哪個IP地址,通常一個宿主機器會有多個網卡,我們可以通過此參數,從頂層來限定客戶端訪問,比如只允許局域網內的客戶端訪問;此外也可以藉助系統工具比如iptables等來防範不合法請求。
5、我們還可以通過application對documents加密存儲,或者基於操作系統特性,對底層文件進行加密,避免那些繞過mongod驗證而直接從底層文件獲取數據的非法操作。不過在絕大多數情況下,我們不需要這麼做。
我們會在下文逐步講解如何開啓“認證授權”的操作方式,不過首先來了解一下mongodb中安全的相關機制和原理。
一、Authentication
認證,就是檢測客戶端身份的過程,客戶端必須通過驗證才能訪問數據。
對於客戶端訪問而言,認證之前,首先需要對用戶授權(authorization),比如通過shell方法:db.createUser()指定user的用戶名、密碼、roles,以及他能訪問的資源列表(databases);此後客戶端訪問數據時必須傳遞user的認證信息(用戶名 + 密碼),以及需要訪問的database,只有認證通過後才能訪問實際數據。可以使用db.auth()方法來認證用戶,或者通過命令行方式傳遞認證信息。
mongodb目前支持“SCRAM-SHAR-1”、“MONGODB-CR”、“X.509證書”三種認證方式,我們稍後介紹。
我們知道,mongodb有2種架構模式:replica set和sharding cluster;這兩種模式都需要多個members,它們之間通常需要互相建立鏈接,那麼mongodb提供了“內部授權”方式,來認證集羣中每個member;任何一個member加入集羣,都需要通過其他members的認證,否則將不能參與集羣。
到此爲止,我們已經知道mongodb有2個認證環節:客戶端與mongod或者mongos之間,以及集羣環境中每個mongod之間。
1、用戶
用戶的認證信息均保存在系統內置的admin數據庫中,無論mongodb是何種架構模式;對於單點部署,認證信息則保存在相應的mongod實例的admin數據庫中;對於replica set架構模式,認證信息則會在所有members中同步;對於sharding cluster而言,admin數據庫位於config servers中,即shards節點上本身不負責保存集羣用戶的認證信息(每個shard節點的admin,只是用來保存此shard私有的用戶,這些用戶被允許不通過mongos也可以直接訪問shard節點),在2.6之前的版本中,用戶認證信息保存在每個數據的primary shard上。
使用“use admin”指令切換到admin數據庫,通過db.createUser()創建首個user(下文詳解);也可以切換到其他數據庫,創建此數據庫的user(參見下文);通常爲“user”、“role”、“database”三個參數來限定一個用戶的訪問控制,即表示一個用戶在database中具有role授權。對於同一個database中,不能有相同的user;但是同一個user可以在多個databases中持有不同的roles。認證用戶信息時,進入相應的database(use <database>),然後通過db.auth()方法,指定用戶名和密碼即可。
2、Localhost Exception
“localhost exception”,簡單翻譯爲“本地主機例外”,主要對於開發者(運維人員)而言,通常使用mongo shell來操作mongodb,如下情況均視爲“localhost exception”:
1)在單點部署時,使用mongod節點上的mongo shell操作。
2)在replica set部署模式中,使用primary節點上的mongo shell操作,
3)對於sharding cluster,使用任意一個mongos節點上的mongo shell操作;
新初始化的mongodb或者集羣中沒有任何user,那麼“本機例外”允許開發者創建系統的第一個user,這個user必須具有“創建其他user的權限”(雞生蛋,蛋生雞?),比如它的role爲“userAdmin”、“userAdminAnyDatabase”。(稍後介紹roles)不過,一旦創建了第一個user,“localhost exception”就不再有效,此後即使在localhost訪問“admin”數據庫,仍然需要認證信息,因爲第一個user已經開始執行訪問控制。
對於sharding cluster,我們通常通過mongos訪問集羣中的數據,同時用戶認證也在mongos上完成;不過mongo shell也可以直接訪問任意一個shard,以及在shard上createUser(本地用戶,非集羣用戶),這一點似乎有些違背cluster的使用約定;如果你不希望“魯莽的人”直接訪問shard,可以對每個shard的配置文件中增加如下配置:
- setParameter:
- enableLocalhostAuthBypass: false
此參數默認爲true,對mongod和mongos有效,即表示開啓“本機例外”;對於shard而言(包括任意類型的mongod),如果此值爲false,那麼mongo shell將無法直接在shard創建第一個user;如果mongos配置的此參數爲false,這意味着開發者無法通過此mongos節點上的shell來創建第一個user;在sharding集羣中,其他mongos均設定爲false,保留一個設定爲true,這樣可以限定開發者只能通過此mongos來創建第一個user,便於管理。
爲了避免不必要的麻煩,建議在開啓“locahost exception”時,最好還要調整bindIp的值,將本地地址添加到bindIp列表中(“0.0.0.0”表示會bind到所有的網卡地址)
- net:
- bindIp: 127.0.0.1,192.168.1.101
3、認證機制
mongodb支持三種認證機制:SCRAM-SHA-1,MONGODB-CR,X.509證書。默認使用“SCRAM-SHA-1”,不過在2.6版本之前默認爲“MONGODB-CR”,建議使用默認。參數配置參見:【mongodb配置】
SCRAM-SHA-1與MONGODB-CR認證需要使用用戶名、密碼和訪問的database,所以這兩種方式非常適合客戶端與mongod(mongos)之間的認證。x.509是一種證書認證,需要在TLS/SSL鏈接環境中使用,通常用於“內部認證”,即集羣中members之間的認證。在此不再講述關於這三種認證的具體細節。
4、內部認證
即“replica set”或者“sharding cluster”集羣中members之間的認證,這種認證最終只允許那些持有合法憑證(證書或者密碼)的member才能加入到集羣。mongodb主要支持2種內部認證:keyFile和x.509。
keyFiles是一個祕鑰文件,內容作爲members之間共享的密碼,密文的長度必須爲6~1024個字符且只能包含base64字符集。
x.509是一個基於證書的認證機制,需要在SSL這種安全通道中使用,本文不做介紹。
二、RBAC
Role-Based Access Control,即基於角色的訪問控制;對於指定的database,賦予user一個或者多個roles,基於此來決定用戶所擁有的操作權限,主要針對客戶端與mongod之間的認證。mongodb默認並不開啓訪問控制,我們需要在啓動時指定命令行參數“--auth”或者在配置文件中指定“security.authorization”,當然此參數也用來控制“內部認證”。
每個Role都有相應的權限列表(privileges),每個privilege對應一種類型的操作。mongodb已經提供了大量的內置Roles,基本上可以滿足我們的需要;不過mongodb也支持自定義的role,我們在此不做介紹。每個角色對應的操作權限列表,請參見【內置角色】;關於mongo shell中進行用戶管理,請參見【user管理】
爲了開啓訪問控制,首先需要在配置文件中增加如下配置:
- security:
- authorization: enabled
- setParameter:
- authenticationMechanisms: SCRAM-SHA-1
創建用戶和角色:
- #mongo shell,首個被創建的user,必須具有“userAdmin”或者“userAdminAnyDatabase”角色
- #一旦user創建,此後訪問數據庫時需要使用auth方法認證,通過後才能繼續執行。
- > ./mongo
- > use admin;
- > db.createUser({user:"admin",pwd:"admin",roles:["userAdminAnyDatabase","dbAdminAnyDatabase"]});
- > db.auth("admin","admin");
- > show databases;
- > ....
- > db.createUser({user:"test",pwd:"test",roles:[
- {role:"readWrite",db:"db1"},
- {role:"readWrite",db:"db2"}]});
1、數據庫用戶角色(user):read和readWrite,表示用戶具有指定數據庫的讀或者寫角色。這兩角色比較基本,能對數據庫進行基本的讀寫操作,對於普通的application而言,可以限定用戶爲此角色。
2、數據庫管理員角色(admin):
1)userAdmin:“用戶管理員”,可以對指定的數據庫,創建用戶、修改用戶的roles;這種角色,只能管理用戶,不能訪問數據庫的數據。
2)dbAdmin:“數據庫管理員”,可以對指定的數據庫,進行創建索引、schema調整、統計信息蒐集等,比如“dbStats”、“collStats”、“createCollection”、“createIndex”等;但是它不能創建用戶和role。
3)dbOwner:“數據庫持有者”,繼承“readWrite”、“dbAdmin”、“userAdmin”三種角色。
通常我們必須爲每個數據庫創建至少一個“userAdmin”或者“dbOwner”權限的管理員用戶,以便管理普通的“user”用戶。
3、集羣管理員角色(cluster admin):
用於管理集羣(replica set和sharding cluster)系統和數據庫。如果你的架構是集羣模式,數據庫至少有一個cluster管理員角色的用戶。
1)clusterManager:“集羣管理員”,可以訪問config、local數據庫,這對sharding、replica set都很重要,比如sharding的維護方法“enableSharding”、“addShard”、“dbStats”,以及“replica set”中的維護方法“replSetConfigure”、“replSetGetStatus”等等。我們可以使用此角色的用戶調整集羣節點的部署和查看一些數據庫狀態。
2)hostManager:“host管理員”
3)clusterAdmin:“集羣管理員”,繼承了“clusterManager”、“hostManager”、“clusterMonitor”等角色,是集羣管理的最高角色;通常我們只需要給一個sharding database指定一個此角色的用戶即可。
4、所有數據庫角色(All-Databases):
即此角色不限定在某個數據庫上,它限定在所有的數據庫上;如果一個用戶需要具有所有數據庫的權限,傳統的方式就是在每個數據庫下創建一個user,繁瑣而且易於疏漏,All-Databases提供了一種便捷的手段,創建這樣角色的用戶時不需要指定數據庫。
1)readAnyDatabase、readWriteAnyDatabase:對所有數據庫具有讀、讀寫角色。
2)userAdminAnyDatabase:是所有數據庫的用戶管理員。
3)dbAdminAnyDatabase:是所有數據庫的管理員。
對於這種權限的設定,不需要指定database,mongo shell的操作方式參見上述例子。
5、超級用戶權限:
root角色是一個超級角色,此角色的用戶具有所有的操作權限,是“readWriteAnyDatabase”、“dbAdminAnyDatabase”、“userAdminAnyDatabase”、“clusterAdmin”角色的組合。對於開發人員而言,爲了維護的便捷性,可以給mongodb(或者集羣)創建一個root用戶。
三、實踐與示例
1、客戶端訪問控制
1)在mongod、mongos配置文件中開啓認證控制和“本機例外”,如果是集羣架構,請將如下配置同步到所有的members中,包括mongos
- security:
- authorization: enabled
- setParameter:
- authenticationMechanisms: SCRAM-SHA-1
- enableLocalhostAuthBypass: false
2)啓動mongod或者集羣
3)通過mongo shell創建首個用戶:
此處需要注意,需要遵守“localhost exception”的限制,對於單點部署,需要使用mongod節點的mongo shell,對於replica set需要使用primary節點的shell,對於sharding cluster則需要使用mongos節點的shell。否則將無法添加首個用戶。第一個user必須具有“創建其他user”的權限。
- > use admin;
- > db.createUser({user:"admin",pwd:"admin",roles:["userAdminAnyDatabase","clusterAdmin"]})
爲了方便,也可以直接創建一個root權限的最高級用戶:
- > db.createUser({user:"root",pwd:"root",roles:["root"]})
4)認證:
創建一個user之後,我們再次訪問其他數據庫或者執行操作時,需要首先認證。
- > use admin;
- > db.auth("admin","admin");
5)創建application用戶,這種用戶可以通過客戶端程序讀寫數據、創建索引和collection等。
- > use common; ##首先切換到需要創建user的數據庫上,然後才能創建有效的user。
- > db.createUser({user:"common",pwd:"common",roles:["dbAdmin","readWrite"]})
上述方法創建了一個“common-user”,限定在數據庫“common”上,它具有讀寫數據和管理數據庫數據的權限。
6)管理用戶,參見【user管理】
- > use admin;
- > db.auth("admin","admin"); ##管理用戶,需要當前認證的用戶是database的userAdmin。
- > db.getUsers(); ##獲取admin數據庫下已經創建的users列表
- > db.dropUser("test") ##移除“test1”用戶,
- > use common; ##普通用戶查看數據,首先在admin數據庫中認證
- > db.getUsers(); ##查看common數據庫下的user列表
- > db.auth("common","common") ##切換成普通user,然後對數據進行讀寫
- > show collections;
- > use common;
- > db.getUsers();
- [
- {
- "_id" : "test.common",
- "user" : "common",
- "db" : "test",
- "roles" : [
- {
- "role" : "readWrite",
- "db" : "test"
- },
- {
- "role" : "dbAdmin",
- "db" : "test"
- }
- ]
- }
- ]
7)JAVA程序示例:
- ##認證機制需要與mongod的配置保持一致,本實例爲“SCRAM-SHA-1”
- ##一個mongodb client在創建時可以指定多個database的認證信息。
- MongoCredential credential = MongoCredential.createScramSha1Credential("common","test","common".toCharArray());
- ServerAddress serverAddress = new ServerAddress("127.0.0.1",27017);
- MongoClient mongoClient = new MongoClient(serverAddress,Arrays.asList(credential));
- MongoDatabase db = mongoClient.getDatabase("test");
- MongoCollection<Document> collection = db.getCollection("products");
- Document document = new Document();
- document.put("categoryId",1);
- document.put("productId",200);
- collection.insertOne(document);
- mongoClient.close();
2、內部認證:
即集羣中memebers之間的認證,在一般情況下其實是不需要認證的,我們一定會將mongod部署在安全可靠的物理環境中,而且在linux平臺的頂層也會增加衆多安全規則以避免不合法入侵的發生。
Server之間的認證機制,大家或許已經知道太多方式了,官方比較推薦使用x.509方式,不過本文選用比較簡單的方式:keyFiles。
1)生成keyFile:
- openssl rand -base64 741 > /home/mongodb/keyfile
- chmod 600 keyfile
此後將此keyFile同步到集羣中所有的mongod節點上。
2)開啓認證:
- security:
- authorization: enabled
- clusterAuthMode: keyFile
- keyFile:/home/mongodb/keyfile
3)創建user:這個就跟1、部分一樣,不過我們需要至少指定一個具有“clusterAdmin”等權限,以方便對集羣用戶進行管理。