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”等权限,以方便对集群用户进行管理。
到此为止,我们已经基本了解了mongodb安全有关的原理和使用方式,具体更多选择,比如SSL配置、x.509等方式,请参考相关文档。
参考:
1、https://docs.mongodb.org/manual/reference/method/#user-management
2、https://docs.mongodb.org/manual/reference/built-in-roles
3、https://docs.mongodb.org/manual/reference/configuration-options/
4、https://docs.mongodb.org/manual/reference/parameters/