天天看点

MongoDB--安全认证

MongoDB的用户和角色权限简介

默认情况下,​

​MongoDB​

​​实例启动运行时是没有启用用户访问权限控制的,也就是说,在实例本机服务器上都可以随意连接到实例进行各种操作,​

​MongoDB​

​不会对连接客户端进行用户验证。

为了强制开启用户访问控制(用户验证),则需要在​

​MongoDB​

​​实例启动时使用选项​

​--auth​

​​或在指定启动配置文件中添加​

​auth=true​

​。
  • 启用访问控制:​

    ​MongoDB​

    ​​使用的是基于角色的访问控制​

    ​(Role-Based Access Control,RBAC)​

    ​来管理用户对实例的访问。通过对用户授予一个或多个角色来控制用户访问数据库资源的权限和数据库操作的权限,在对用户分配角色之前,用户无法访问实例。
  • 角色:在​

    ​MongoDB​

    ​中通过角色对用户授予相应数据库资源的操作权限,每个角色当中的权限可以显示指定,也可以通过集成其他角色的权限,或者两者都存在的权限。
  • 权限:权限由指定的数据库(resource)以及允许在运行资源上进行的操作(action)组成。
  • 资源(resource)包括:数据库、集合、部分集合和集群;
  • 操作(action)包括:对资源的增、删、改、查(CRUD)操作。
在角色定义时可以包含一个或多个已存在的角色,新创建的角色会继承包含的角色所有的权限。在同一个数据库中,新创建角色可以继承其他角色的权限,在​

​admin​

​数据库中创建的角色可以继承在其它任意数据库中的角色的权限。

角色权限的查看,可以通过如下的命令进行查看:

# 查询所有角色权限(仅用户自定义角色) 
> db.runCommand({ rolesInfo: 1 })

# 查询所有角色权限(包含内置角色)
> db.runCommand({ rolesInfo: 1, showBuiltinRoles: true })

# 查询当前数据库中的某角色的权限 
> db.runCommand({ rolesInfo: "<rolename>" })

# 查询其它数据库中指定的角色权限
> db.runCommand({ rolesInfo: { role: "<rolename>", db: "<database>" } }

# 查询多个角色权限
> db.runCommand({ 
  rolesInfo: [ 
    "<rolename>", { role: "<rolename>", db: "<database>" },
    ...
  ] 
})      

示例:

查看所有内置角色:

> db.runCommand({rolesInfo:1, showBuiltinRoles:true})
{
  "roles" : [
    {
      "role" : "__queryableBackup",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "__system",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "backup",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "clusterAdmin",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "clusterManager",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "clusterMonitor",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "dbAdmin",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "dbAdminAnyDatabase",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "dbOwner",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "enableSharding",
      "db" : "admin",
      "isBuiltin" : true,
      "roles" : [ ],
      "inheritedRoles" : [ ]
    },
    {
      "role" : "hostManager",
      "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" : [ ]
    }
  ],
  "ok" : 1
}      

常见的内置角色

  • 数据库用户角色:​

    ​read​

    ​​、​

    ​readWrite​

  • 数据库管理角色:​

    ​dbAdmin​

    ​​、​

    ​dbOwner​

    ​​、​

    ​userAdmin​

  • 集群管理角色:​

    ​clusterAdmin​

    ​​、​

    ​clusterManager​

    ​​、​

    ​clusterMonitor​

  • 所有数据库用户角色:​

    ​readAnyDatabase​

    ​​、​

    ​readWriteAnyDatabase​

    ​​、​

    ​userAdminAnyDatabase​

    ​​、​

    ​dbAdminAnyDatabase​

  • 备份恢复角色:​

    ​backup​

    ​​、​

    ​restore​

  • 超级用户角色:​

    ​root​

  • 内部角色:​

    ​system​

???? 角色说明:

角色 权限描述

​read​

可以读取指定数据库中任何数据。

​readWrite​

可以读写指定数据库中任何数据,包括创建、重命名、删除集合

​readAnyDatabase​

可以读取所有数据库中任何数据(除了数据库​

​config​

​​和​

​local​

​之外)

​readWriteAnyDatabase​

可以读写所有数据库中任何数据(除了数据库​

​config​

​​和​

​local​

​之外)

​userAdminAnyDatabase​

可以在指定数据库创建和修改用户(除了数据库​

​config​

​​和​

​local​

​之外)

​dbAdminAnyDatabase​

可以读取任何数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作(除了数据库​

​config​

​​和​

​local​

​之外)

​dbAdmin​

可以读取指定数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作

​userAdmin​

可以在指定数据库创建和修改用户

​clusterAdmin​

可以对整个集群或数据库系统进行管理操作

​backup​

备份​

​MongoDB​

​数据最小的权限

​restore​

从备份文件中还原恢复​

​MongoDB​

​​数据(除了​

​system.profile​

​集合)的权限

​root​

超级账户,超级权限

单实例环境

创建用户

✏️ 创建用户格式如下:

db.createUser({
  user:"<name>",
  pwd:"<password>",
  customData:{<any Object Data},
  roles:[
    {role:"<role>",db:"database"},
    ...
  ]
})

备注:
1)user:新建用户名
2)pwd:新建用户密码
3)customData:存放一些用户相关的自定义数据,可忽略
4)roles:数据类型,配置用户的权限      

示例:创建一个管理员账户

# mongo --host=10.10.10.11 --port=27017
> show dbs;
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.000GB
> 
> use admin
switched to db admin
> 
> db.createUser({"user":"root","pwd":"123456","roles":[{"role":"root","db":"admin"}]});
Successfully added user: {
  "user" : "root",
  "roles" : [
    {
      "role" : "root",
      "db" : "admin"
    }
  ]
}      

✏️ 查看用户

# 方法一
> use admin;
switched to db admin
> 
> show users;
{
  "_id" : "admin.root",
  "userId" : UUID("d7280144-2886-45eb-95f3-1965be4eb7fe"),
  "user" : "root",
  "db" : "admin",
  "roles" : [
    {
      "role" : "root",
      "db" : "admin"
    }
  ],
  "mechanisms" : [
    "SCRAM-SHA-1",
    "SCRAM-SHA-256"
  ]
}

# 方法二
> use admin;
switched to db admin
> 
> show tables;
system.users
system.version
> 
> db.system.users.find()
{ "_id" : "admin.root", "userId" : UUID("d7280144-2886-45eb-95f3-1965be4eb7fe"), "user" : "root", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "u2nQBHuC2P367Qo5mLhcRw==", "storedKey" : "0ccLsqhpWUyCdAoQKZQNNHwvj9g=", "serverKey" : "9Qxfsw+GCQMwUuyz9AlKRAX6zx0=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "rxajlRAv8zteEMm6r2Wx8GFY1ShBeLjHUqJ7iA==", "storedKey" : "T9z/0GJ21cxJ4CvnNL+PBGkTYwdPu9YxmRo1CQ2tGp0=", "serverKey" : "UBVuzHOxfKADUdpxIhRBab2HwPdLhNL2yUZzyTXFRt0=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ]      

服务端开启认证

✏️ 修改配置文件

# vim /usr/local/mongodb/conf/mongodb.conf
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongodb.log
bind_ip=0.0.0.0
port=27017
logappend=1
fork=1
auth=true    #开启认证      

✏️ 重启服务

# ps -ef |grep mongod
root       6991      1  0 19:46 ?        00:00:10 mongod -f /usr/local/mongodb/conf/mongodb.conf
root       7438   6395  0 20:14 pts/1    00:00:00 grep --color=auto mongod
# kill -2 6991 
# 
# mongod -f /usr/local/mongodb/conf/mongodb.conf      

✏️ 登陆测试

# mongo --host=10.10.10.11 --port=27017
MongoDB shell version v4.4.1
connecting to: mongodb://10.10.10.11:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("cb337048-a9c8-45ae-99aa-b0cb8cc7a163") }
MongoDB server version: 4.4.1
> show dbs   #查看
> 
> use admin   #切换到admin库
switched to db admin
> db.auth("root","123456")  #进行认证
1
> show dbs;
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.000GB      

✏️ 添加一个普通用户

创建普通用户可以在没有开启认证的时候添加,也可以在开启认证之后添加,但开启认证之后,必须使用有操作​

​admin​

​​库的用户登录认证后才能操作。底层是将用户信息保存在了​

​admin​

​​数据库的集合​

​system.users​

​中。
# 创建(切换)将来要操作的数据库articledb
> use articledb
switched to db articledb
> 
> db.createUser({user:"user1",pwd:"1234",roles:[{role:"readWrite",db:"articledb"}]})
Successfully added user: {
  "user" : "user1",
  "roles" : [
    {
      "role" : "readWrite",
      "db" : "articledb"
    }
  ]
}
# 测试是否可用
> db.auth("user1","1234")
1      

客户端登录认证

✏️ 方法1

先连接,在认证

# mongo --host 10.10.10.11 --port 27017
MongoDB shell version v4.4.1
connecting to: mongodb://10.10.10.11:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("4efbbc03-31b7-45ff-8b3a-5ce6f76ff1ca") }
MongoDB server version: 4.4.1
> use admin
switched to db admin
> db.auth("root","123456")
1
> show dbs
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.000GB      

✏️ 方法2

连接时进行认证

# mongo --host 10.10.10.11 --port 27017 --authenticationDatabase admin -u root -p 123456
MongoDB shell version v4.4.1
connecting to: mongodb://10.10.10.11:27017/?authSource=admin&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0848b5d0-fb56-40eb-9b37-542682fd6ada") }
MongoDB server version: 4.4.1
......
> 
> show dbs
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.000GB      

参数说明:

  • ​--host​

    ​:连接的主机地址
  • ​--port​

    ​:连接的端口
  • ​--authenticationDatabase​

    ​:指定连接到哪个库。当登录是指定用户密码时,必须指定对应的数据库!
  • ​-u​

    ​:用户名
  • ​-p​

    ​:密码

副本集环境

对于搭建好的​

​mongodb​

​副本集,为了安全,启动安全认证,使用账户密码登录。

对副本集执行访问控制需要配置两个方面:

1)副本集和共享集群的各个节点成员之间使用内部身份验证,可以使用秘钥文件或​

​x.509​

​证书。秘钥文件比较简单。

2)使用客户端连接到​

​mongodb​

​集群时,可开启访问授权。对于集群外部的访问。如通过可视化客户端、或者通过代码连接的时候,需要开启授权。

添加管理账户

在主节点添加管理账户,副本集会自动同步。
myrs:PRIMARY> use admin
switched to db admin
myrs:PRIMARY> db.createUser({user:"root",pwd:"123456",roles:[{role:"root",db:"admin"}]})
Successfully added user: {
  "user" : "root",
  "roles" : [
    {
      "role" : "root",
      "db" : "admin"
    }
  ]
}      

备注:

该步骤也可以在开启认证之后,但需要通过​

​localhost​

​登录才允许添加用户,用户数据也会自动同步到副本集。后续再创建其他用户,都已使用该超管用户创建。

创建副本集认证的key文件

✏️ 生成一个key文件到当前文件夹中。

可以使用任何方法生成秘钥文件,例如,一下使用​

​openssl​

​​生成密码文件,然后使用​

​chmod​

​来更改文件权限,仅为文件所有这提供读取权限。

# openssl rand -base64 90 -out ./mongo.keyfile
# chmod 400 ./mongo.keyfile 
# ll mongo.keyfile 
-r--------. 1 root root 122 Nov 17 16:27 mongo.keyfile      

备注:

所有副本集节点都必须使用同一份​

​keyfile​

​​,一般在一台机器上生成,然后拷贝到其他机器上,且必须有读的权限,否则将来会报错:​

​permissions on /mongodb/replica_sets/myrs_27017/mongo.keyfile are too open​

一般情况下和配置文件放置在一起,方便查找。这里都​

​copy​

​到各个节点的配置文件目录

# cp mongo.keyfile /data/replica_sets/myrs_27017/
# cp mongo.keyfile /data/replica_sets/myrs_27018/
# cp mongo.keyfile /data/replica_sets/myrs_27019/      

修改配置文件指定keyfile

分别编辑几个服务的​

​mongod.conf​

​文件,添加相关内容
# vim /data/replica_sets/myrs_27017/mongod.conf
security:
    #keyfile鉴权文件
    keyFile: /data/replica_sets/myrs_27017/mongo.keyfile
    #开启认证方式运行
    authorization: enabled

# vim /data/replica_sets/myrs_27018/mongod.conf
security:
    #keyfile鉴权文件
    keyFile: /data/replica_sets/myrs_27018/mongo.keyfile
    #开启认证方式运行
    authorization: enabled

# vim /data/replica_sets/myrs_27019/mongod.conf
security:
    #keyfile鉴权文件
    keyFile: /data/replica_sets/myrs_27019/mongo.keyfile
    #开启认证方式运行
    authorization: enabled      

重启副本集

如果副本集是开启状态,则先分别关闭副本集中的每个​

​mongod​

​,从次节点开始。直到副本集的所有成员都离线,包括任何仲裁者。主节点必须是最后一个成员关闭,以避免潜在的回滚。
# ps -ef |grep mongod
root       2649      1  1 11:20 ?        00:04:44 mongod -f /data/replica_sets/myrs_27017/mongod.conf
root       2728      1  1 11:20 ?        00:05:21 mongod -f /data/replica_sets/myrs_27018/mongod.conf
root       4534      1  0 14:13 ?        00:01:07 mongod -f /data/replica_sets/myrs_27019/mongod.conf

# kill -2 2649 4534 2728      

分别启动副本集

# mongod -f /data/replica_sets/myrs_27017/mongod.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 6287
child process started successfully, parent exiting
# 
# mongod -f /data/replica_sets/myrs_27018/mongod.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 6365
child process started successfully, parent exiting
#
# mongod -f /data/replica_sets/myrs_27019/mongod.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 6456
child process started successfully, parent exiting      

查看进程情况

# ps -ef |grep mongo
root       6287      1  3 16:40 ?        00:00:02 mongod -f /data/replica_sets/myrs_27017/mongod.conf
root       6365      1  3 16:41 ?        00:00:02 mongod -f /data/replica_sets/myrs_27018/mongod.conf
root       6456      1  2 16:41 ?        00:00:01 mongod -f /data/replica_sets/myrs_27019/mongod.conf      

连接测试

连接主节点测试

✏️ 先连接,再认证

# mongo --host 10.10.10.11 --port 27017
MongoDB shell version v4.4.1
connecting to: mongodb://10.10.10.11:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("e1218d07-5094-468d-bdfe-f1d3e3815281") }
MongoDB server version: 4.4.1
myrs:PRIMARY> show dbs
myrs:PRIMARY> db.auth("root","123456")
1
myrs:PRIMARY> show dbs
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.001GB
test        0.000GB      

✏️ 连接时进行认证

# mongo --host 10.10.10.11 --port 27017 --authenticationDatabase admin -u root -p 123456
---
myrs:PRIMARY> show dbs
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.001GB
test        0.000GB      

连接副本节点测试

# mongo --host 10.10.10.11 --port 27018 --authenticationDatabase admin -u root -p 123456
myrs:SECONDARY> show dbs  #查看数据库,这里不能查看是因为默认副本节点不能查询数据,需要开启查询权限
uncaught exception: Error: listDatabases failed:{
  "topologyVersion" : {
    "processId" : ObjectId("5fb38ca9ae9c791f8008eb9a"),
    "counter" : NumberLong(4)
  },
  "operationTime" : Timestamp(1605603187, 1),
  "ok" : 0,
  "errmsg" : "not master and slaveOk=false",
  "code" : 13435,
  "codeName" : "NotMasterNoSlaveOk",
  "$clusterTime" : {
    "clusterTime" : Timestamp(1605603187, 1),
    "signature" : {
      "hash" : BinData(0,"hCt+wCrLo2uwZucZsLN0id+Rnh0="),
      "keyId" : NumberLong("6893051287466672133")
    }
  }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
myrs:SECONDARY> 
myrs:SECONDARY> rs.secondaryOk()  #开启副本集读取权限
myrs:SECONDARY> 
myrs:SECONDARY> show dbs  #再次查看
admin       0.000GB
collectest  0.000GB
config      0.000GB
local       0.001GB
test        0.000GB      

添加普通用户

✏️ 添加普通用户对指定库有读写权限

//连接主节点
# mongo --host 10.10.10.11 --port 27017 --authenticationDatabase admin -u root -p 123456
myrs:PRIMARY> use collectest  #切换到collectest库
switched to db collectest
myrs:PRIMARY> 
myrs:PRIMARY> db.createUser({user:"u_test",pwd:"123",roles:[{role:"readWrite", db:"collectest"}]})  #创建用户u_test对collectest库有读写权限

Successfully added user: {
  "user" : "u_test",
  "roles" : [
    {
      "role" : "readWrite",
      "db" : "collectest"
    }
  ]
}
myrs:PRIMARY>      
# mongo --host 10.10.10.11 --port 27017 --authenticationDatabase collectest -u u_test -p 123
...
myrs:PRIMARY> 
myrs:PRIMARY> show dbs
collectest  0.000GB
myrs:PRIMARY> 
myrs:PRIMARY> use collectest
switched to db collectest
myrs:PRIMARY> 
myrs:PRIMARY> show collections
collectest
myrs:PRIMARY> 
myrs:PRIMARY> db.collectest.find()
{ "_id" : ObjectId("5faa3432f6e79c62c00e4d72"), "name" : "张三", "sex" : "男", "age" : 22, "userid" : 1001, "createdatetime" : ISODate("2020-11-10T06:33:22.459Z") }
{ "_id" : ObjectId("5facec10cca53c48154d261c"), "name" : "小白", "sex" : "女", "age" : 20, "userid" : 1002, "createdatetime" : ISODate("2020-11-12T08:02:24.915Z") }
{ "_id" : ObjectId("5facf32dfb5fe16aaf699d7d"), "name" : "小嘿", "sex" : "女", "age" : 21, "userid" : 1002, "createdatetime" : ISODate("2020-11-12T08:32:45.919Z") }
myrs:PRIMARY>

#查询一个普通用户没有权限的库,则会提示响应警告,并无法查看成功。
myrs:PRIMARY> use admin
switched to db admin
myrs:PRIMARY> show collections
Warning: unable to run listCollections, attempting to approximate collection names by parsing connectionStatus