1. 程式人生 > >Mongo進階--Security安全介紹

Mongo進階--Security安全介紹

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的配置檔案中增加如下配置:

Java程式碼  收藏程式碼
  1. setParameter:  
  2.     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到所有的網絡卡地址)

Java程式碼  收藏程式碼
  1. net:  
  2.     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”,建議使用預設。引數配置參見:【】

    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管理

    為了開啟訪問控制,首先需要在配置檔案中增加如下配置:

Java程式碼  收藏程式碼
  1. security:  
  2.     authorization: enabled  
  3. setParameter:  
  4.     authenticationMechanisms: SCRAM-SHA-1  

    建立使用者和角色:

Java程式碼  收藏程式碼
  1. #mongo shell,首個被建立的user,必須具有“userAdmin”或者“userAdminAnyDatabase”角色  
  2. #一旦user建立,此後訪問資料庫時需要使用auth方法認證,通過後才能繼續執行。  
  3. > ./mongo  
  4. > use admin;  
  5. > db.createUser({user:"admin",pwd:"admin",roles:["userAdminAnyDatabase","dbAdminAnyDatabase"]});  
  6. > db.auth("admin","admin");  
  7. > show databases;  
  8. > ....  
  9. > db.createUser({user:"test",pwd:"test",roles:[  
  10.     {role:"readWrite",db:"db1"},  
  11.     {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

Java程式碼  收藏程式碼
  1. security:  
  2.     authorization: enabled  
  3. setParameter:  
  4.     authenticationMechanisms: SCRAM-SHA-1  
  5.     enableLocalhostAuthBypass: false  

    2)啟動mongod或者叢集

    3)通過mongo shell建立首個使用者:

    此處需要注意,需要遵守“localhost exception”的限制,對於單點部署,需要使用mongod節點的mongo shell,對於replica set需要使用primary節點的shell,對於sharding cluster則需要使用mongos節點的shell。否則將無法新增首個使用者。第一個user必須具有“建立其他user”的許可權。

Java程式碼  收藏程式碼
  1. > use admin;  
  2. > db.createUser({user:"admin",pwd:"admin",roles:["userAdminAnyDatabase","clusterAdmin"]})  

    為了方便,也可以直接建立一個root許可權的最高階使用者:

Java程式碼  收藏程式碼
  1. > db.createUser({user:"root",pwd:"root",roles:["root"]})  

    4)認證:

    建立一個user之後,我們再次訪問其他資料庫或者執行操作時,需要首先認證。

Java程式碼  收藏程式碼
  1. > use admin;  
  2. > db.auth("admin","admin");  

    5)建立application使用者,這種使用者可以通過客戶端程式讀寫資料、建立索引和collection等。

Java程式碼  收藏程式碼
  1. > use common;     ##首先切換到需要建立user的資料庫上,然後才能建立有效的user。  
  2. > db.createUser({user:"common",pwd:"common",roles:["dbAdmin","readWrite"]})  

    上述方法建立了一個“common-user”,限定在資料庫“common”上,它具有讀寫資料和管理資料庫資料的許可權。

    6)管理使用者,參見【user管理

Java程式碼  收藏程式碼
  1. > use admin;  
  2. > db.auth("admin","admin");  ##管理使用者,需要當前認證的使用者是database的userAdmin。  
  3. > db.getUsers();            ##獲取admin資料庫下已經建立的users列表  
  4. > db.dropUser("test")       ##移除“test1”使用者,  
  5. > use common;    ##普通使用者檢視資料,首先在admin資料庫中認證  
  6. > db.getUsers();         ##檢視common資料庫下的user列表  
  7. > db.auth("common","common") ##切換成普通user,然後對資料進行讀寫  
  8. > show collections;  
Java程式碼  收藏程式碼
  1. > use common;  
  2. > db.getUsers();  
  3. [  
  4.     {  
  5.         "_id" : "test.common",  
  6.         "user" : "common",  
  7.         "db" : "test",  
  8.         "roles" : [  
  9.             {  
  10.                 "role" : "readWrite",  
  11.                 "db" : "test"  
  12.             },  
  13.             {  
  14.                 "role" : "dbAdmin",  
  15.                 "db" : "test"  
  16.             }  
  17.         ]  
  18.     }  
  19. ]  

    7)JAVA程式示例:

Java程式碼  收藏程式碼
  1. ##認證機制需要與mongod的配置保持一致,本例項為“SCRAM-SHA-1”  
  2. ##一個mongodb client在建立時可以指定多個database的認證資訊。  
  3. MongoCredential credential = MongoCredential.createScramSha1Credential("common","test","common".toCharArray());  
  4. ServerAddress serverAddress = new ServerAddress("127.0.0.1",27017);  
  5. MongoClient mongoClient = new MongoClient(serverAddress,Arrays.asList(credential));  
  6. MongoDatabase db = mongoClient.getDatabase("test");  
  7. MongoCollection<Document> collection = db.getCollection("products");  
  8. Document document = new Document();  
  9. document.put("categoryId",1);  
  10. document.put("productId",200);  
  11. collection.insertOne(document);  
  12. mongoClient.close();  

    2、內部認證:

    即叢集中memebers之間的認證,在一般情況下其實是不需要認證的,我們一定會將mongod部署在安全可靠的物理環境中,而且在linux平臺的頂層也會增加眾多安全規則以避免不合法入侵的發生。

    Server之間的認證機制,大家或許已經知道太多方式了,官方比較推薦使用x.509方式,不過本文選用比較簡單的方式:keyFiles。

    1)生成keyFile:

Java程式碼  收藏程式碼
  1. openssl rand -base64 741 > /home/mongodb/keyfile  
  2. chmod 600 keyfile  

    此後將此keyFile同步到叢集中所有的mongod節點上。

    2)開啟認證:

Java程式碼  收藏程式碼
  1. security:  
  2.     authorization: enabled  
  3.     clusterAuthMode: keyFile  
  4.     keyFile:/home/mongodb/keyfile  

    3)建立user:這個就跟1、部分一樣,不過我們需要至少指定一個具有“clusterAdmin”等許可權,以方便對叢集使用者進行管理