meface/docs/article/db/mongodb_advance2.md

20 KiB
Raw Blame History

title date author tags categories
MongoDB 安全认证 2021-01-10 ac
MongoDB
Database

MongoDB的安全认证

安全认证

1.用户和角色权限简介

默认情况下,MongoDB实例启动运行时是没有启用用户访问权限控制的,也就是说,在实例本机服务器上都可以随意连接到实例进行各种操作,MongoDB不会对连接客户端进行用户验证,这是非常危险的。

MongoDB官网上说,为了能保障MongoDB的安全可以做以下几个步骤:

  1. 使用新的端口,默认的27017端口。
  2. 设置MongoDB的网络环境最好将mongodb部署到公司服务器内网。
  3. 开启安全认证。认证要同时设置服务器之间的内部认证方式,同时要设置客户端连接到集群的账号密码认证方式。

为了强制开启用户访问控制(用户验证),则需要在MongoDB实例启动时使用选项--auth或在指定的启动文件中添加选项auth=true

在开始之前需要了解的概念

  1. 启用访问控制

    MongoDB使用的是基于角色的访问控制(Role-Based Access ControlRBAC)来管理用户对实例的访问。通过对用户授予一个或多个角色来控制用户访问数据库资源的权限和数据库操作的权限,在对用户分配角色之前,用户无法访问实例。

  2. 角色

    MongoDB中通过角色对用户授予相应数据库资源的操作权限,每个角色当中的权限可以显示指定,也可以通过继承其他角色的权限,或者两者都存在的权限。

  3. 权限

    权限由指定的数据库资源resource以及允许在指定资源上进行的操作action组成。

    • 资源resource数据库、集合、部分集合和集群
    • 操作action对资源进行的增删改查CRUD操作

在角色定义时可以包含一个或多个已经存在的角色,新创建的角色会继承包含的角色所有的权限。在同一个数据库中新创建角色可以继承其他角色的权限,在admin数据库中创建的角色可以继承在其它任意数据库中角色的权限。

常用的内置角色:

  • 数据库用户角色:readreadWrite
  • 所有数据库用户角色:readAnyDatabasereadWriteAnyDatabaseuserAdminAnyDatabasedbAdminAnyDatabase
  • 数据库管理角色:dbAdmindbOwneruserAdmin
  • 集群管理角色:clusterAdminclusterManagerclusterMonitorhostManager;
  • 备份恢复角色:backuprestore
  • 超级用户角色:root
  • 内部角色:system

关于角色权限的查看:

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

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

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

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

角色说明

角色 权限描述
read 可以读取指定数据库中任何数据。
readWrite 可以读写指定数据库中任何数据,包括创建、重命名、删除集合
readAnyDatabase 可以读取所有数据库中任何数据(除了数据库configlocal之外)
userAdminAnyDatabase 可以在指定数据库创建和修改用户(除了数据库configlocal之外)
readWriteAnyDatabase 可以读写所有数据库中任何数据(除了数据库configlocal之外)
dbAdminAnyDatabase 可以读取任何数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作(除了数据库configlocal之外)
dbAdmin 可以读取指定数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作。
userAdmin 可以在指定数据库创建和修改用户。
clusterAdmin 可以对整个集群或数据库系统进行管理操作。
backup 备份MongoDB数据最小的权限。
restore 从备份文件中还原恢复MongoDB数据(除了system.profile集合)的权限。
root 超级账号,超级权限

2.单实例环境

对单实例的MongoDB服务开启安全认证,这里的单实例指的是未开启副本集或分片的MongoDB实例

关闭已开启的服务(可选)

增加mongod的单实例的安全认证功能,可以在服务搭建的时候直接添加,也可以在之前搭建好的服务上添加。

这里使用之前搭建好的服务,因此,先停止之前的服务。

关闭服务有两种方式:

  • 快速关闭:使用kill命令直接杀死进程

    #通过进程编号关闭节点
    kill -2 进程号
    
  • 标准关闭:通过mongo个客户端中的shutdownServer命令来关闭服务

    #客户端登录服务注意这里通过localhost登录如果需要远程登录必须先登录认证才行。 
    mongo --port 27017 
    
    #切换到admin库 
    use admin 
    
    #如果不是admin数据库则会报如下错误
    shutdown command only works with the admin database; try 'use admin'
    
    #关闭服务 
    db.shutdownServer()
    

    必须是在admin库下执行该关闭命令;

    如果没有开启认证,必须是从localhost登陆才能执行关闭服务命令;

    localhost的、通过远程登陆的,必须登陆且必须登陆用户有对admin操作权限才可以。

添加用户权限

  1. 先按照普通无授权认证的配置,来配置服务端的配置文件/mongodb/single/mongod.conf

    systemLog:
       destination: file
       path: "/home/qiusj/.local/mongodb/singlesite/log/mongod.log"
       logAppend: true
    storage:
       dbPath: "/home/qiusj/.local/mongodb/singlesite/data/db"
       journal:
          enabled: true
    net:
       #添加本机在局域网内的ip
       bindIp: 127.0.0.1,192.168.0.152
       port: 27017
    setParameter:
       enableLocalhostAuthBypass: false
    processManagement:
       fork: true
       pidFilePath: "/home/qiusj/.local/mongodb/singlesite/log/mongod.pid"
    
    
  2. 按照之前未开启认证的方式(不添加--auth参数)来启动MongoDB服务:

    qiusj@u20:~$ /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/singlesite/mongod.conf 
    
  3. 使用Mongo客户端登陆

    qiusj@u20:~$ /usr/local/mongodb/bin/mongo --port=27017
    
  4. 创建两个管理员用户,一个系统的超级管理员myroot,一个是admin库的管理员用户myadmin:

    #切换到admin库
    > use admin
    switched to db admin
    #创建系统超级用户myroot,密码123456角色root
    #> db.createUser({user:"myroot",pwd:"123456",roles:[{"role":"root","db":"admin"}]})
    > db.createUser({user:"myroot",pwd:"123456",roles:["root"]})
    Successfully added user: { "user" : "myroot", "roles" : [ "root" ] }
    
    #创建专门用来管理admin库的账号myadmin只用来作为用户权限的管理
    > db.createUser({user:"myadmin",pwd:"123456",roles:[{role:"userAdminAnyDatabase",db:"admin"}]})
    Successfully added user: {
            "user" : "myadmin",
            "roles" : [
                    {
                            "role" : "userAdminAnyDatabase",
                            "db" : "admin"
                    }
            ]
    }
    #查看已经创建了的用户的情况
    > db.system.users.find()
    { "_id" : "admin.myroot", "userId" : UUID("9ae2bb4d-d289-4135-884f-2082cb6b20aa"), "user" : "myroot", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "I2oNFoKRK0ktT9pM4to5fw==", "storedKey" : "Nxuoez/RCQ1AeyQxF2FKEyTsopE=", "serverKey" : "L740F4turO7raGtNr92H1GoRErk=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "iZ7PX1mFDZdCKwyOIrvyvLW7SywqR8sJANp+SA==", "storedKey" : "ngkeL6kE94IKXuuHjjSZjo87fo4yZ3ivxGeFsWK0/V4=", "serverKey" : "SpcYUVj+J3ZO3Iv3SU4AOx9POmWc7bc2WLreAxFtypI=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
    { "_id" : "admin.myadmin", "userId" : UUID("4a901dd2-75ce-4ea4-ab2e-5c06de29ba20"), "user" : "myadmin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "eCn7OmqaJHgX4kxGij2eew==", "storedKey" : "qXWbRh5vgKQJQ4Ri4in1W7KyTq0=", "serverKey" : "tattdTXuWm2wsozuaWgtZgn2I4M=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "z3gBxcUUIgV4BjZk1RPWkjWHlDZF6r5ZDcSV6A==", "storedKey" : "2oQpybEAs0mdvNTZnIuKA3hVRHJxKcR6UZXq6At/fkg=", "serverKey" : "cMuUfnaDScuIJOQ4Y/g2y/1s/1W7FZNrM8T9H/pMbAI=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }
    
    
  5. 用户相关操作

    #删除用户
    > db.dropuser("myadmin")
    
    #修改密码
    > db.changeUserPassword("myroot","123456")
    
    #认证测试
    > db.auth("myroot","123456")
    1
    
    

和其他数据库一样,权限的管理都差不多一样,也是将用户和权限信息保存在数据库对应的表中。MongoDB存储所有用户信息在admin数据库中的system.users集合中,保存着用户名、密码和数据库信息。

如果不指定数据库,则创建的指定的权限的用户在所有的数据库上有效。如{role:"userAdmin",db:""}

  1. 创建普通用户

    创建普通用户可以在没有开启认证的时候添加,也可以在开启认证后添加,但开启之后,必须使用有操作admin库的用户登录认证后才能操作。底层都是将用户信息保存在admin数据库的system.users集合中

    > use meface
    > db.createUser({user:"star",pwd:"star",roles:[{role:"readWrite",db:"meface"}]})
    Successfully added user: {
            "user" : "star",
            "roles" : [
                    {
                            "role" : "readWrite",
                            "db" : "meface"
                    }
            ]
    }
    > db.auth("star","star")
    1
    

    如果开启了认证,登陆的客户端的用户必须使用admin库的角色,如果拥有root劫色的myadmin用户,再通过myadmin用户去创建其他角色的用户。

服务端开启认证和客户端连接登陆

有两种方式开启权限认证启动服务:参数方式、配置文件方式

  1. 参数方式

    在启动时指定参数--auth,示例:

    /usr/local/mongodb/bin/mongd -f /mongodb/single/mongod.conf --auth
    
  2. 配置文件方式

    mongod.conf配置文件中加入:

    security:
    	#开启授权认证
    	authorization: enabled
    

    启动时可以不加--auth参数

开启了认证的情况下的客户端登陆,有两种方式:先连接再登陆、登陆时直接认证

  1. 开启认证后再登陆,如查询admin库中的system.user集合的用户:

    > use admin
    switched to db admin
    > db.auth("myadmin","123456")
    1
    > db.system.users.find()
    

    查询meface库中的 article集合的内容:

    > use meface
    switched to db meface
    > db.article.find()
    Error: error: {
            "ok" : 0,
            "errmsg" : "not authorized on meface to execute command { find: \"article\", filter: {}, lsid: { id: UUID(\"a2215a94-d86a-4bdc-bdef-1e62033aee89\") }, $db: \"meface\" }",
            "code" : 13,
            "codeName" : "Unauthorized"
    }
    

    需要退出来,再使用star用户去操作

    > exit
    bye
    qiusj@u20:~$ /usr/local/mongodb/bin/mongo --port=27017
    
    > db.auth("star","star")
    Error: Authentication failed.
    0
    > use meface
    switched to db meface
    
    > db.auth("star","star")
    1
    > db.article.find()
    
  2. 登陆时直接认证:

    /usr/local/mongodb/bin/mongo --port=27017 --username=star --password=star --authenticationDatabase=meface

    qiusj@u20:~$ /usr/local/mongodb/bin/mongo --port=27017 --username=star --password=star --authenticationDatabase=meface
    MongoDB shell version v4.4.2
    connecting to: mongodb://127.0.0.1:27017/?authSource=meface&compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("0cabba54-185e-4332-a8f8-2a1b1b8753a3") }
    MongoDB server version: 4.4.2
    > db
    test
    > use meface
    switched to db meface
    > db.article.find()
    { "_id" : ObjectId("5fed2b1b232584d8ba357db0"), "articleid" : "100000", "content" : "今天天气真好,阳光明 媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2020-12-31T01:36:27.784Z"), "likenum" : 10, "state" : null }
    { "_id" : "1", "likenum" : 1001 }
    { "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 666, "state" : "1" }
    { "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船 长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
    { "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "小李", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
    { "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "小李", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
    > 
    

Compass连接认证

mongodb://star:star@192.168.0.152:27017/?authSource=meface

image-20210121161359189

SpringData连接认证

使用用户名和密码连接到MongoDB服务器,必须使用username:password@hostname/dbname格式。

application.yml

spring:
  #数据源配置
  data:
    mongodb:
      #连接分片集群的路由节点多个路由用逗号分隔mongodb会有其负载均衡的策略
#      uri: mongodb://192.168.0.152:27017,192.168.0.152:27117/meface
#      host: 192.168.0.152
#      database: meface
#      port: 27017
      #使用uri的方式
#      uri:mongodb://192.168.0.152:27017/meface
      uri: mongodb://star:star@192.168.0.152:27017/meface

3.副本集环境

对于搭建好的mongodb副本集,为了安全,启动安全认证,使用账号密码登陆。

副本集环境使用之前搭建好的,架构如下:

image-20210121164640605

先通过主节点增加一个管理员账号

在开启认证之前,创建超管用户:myroot/123456

只需要在主节点上添加用户,副本集会自动同步。

myrs:PRIMARY> use admin
switched to db admin
myrs:PRIMARY> db.createUser({user:"myroot",pwd:"123456",roles:[{role:"root",db:"admin"}]})
Successfully added user: {
        "user" : "myroot",
        "roles" : [
                {
                        "role" : "root",
                        "db" : "admin"
                }
        ]
}

关闭副本集

主节点必须最后一个成员关闭义避免潜在的回滚。

$ ps -ef|grep mongo
qiusj    4004546       1  0 10:38 ?        00:00:02 /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27017/mongod.conf
qiusj    4004640       1  0 10:38 ?        00:00:02 /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27018/mongod.conf
qiusj    4004739       1  0 10:38 ?        00:00:01 /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27019/mongod.conf
qiusj    4006158 3989038  0 10:45 pts/1    00:00:00 grep --color=auto mongo

$ kill -2 4004640 4004739 4004546

创建副本集认证的key文件

第一步生成一个key文件到当前文件夹中。

可以使用任何方法生成密钥文件。例如,以下操作使用openssl生成加密文件,然后使用chmod来更改文件权限,仅为文件所有者提供读取权限。

# 查看安装的ssl版本
qiusj@u20:~$ openssl version -a
OpenSSL 1.1.1f  31 Mar 2020

qiusj@u20:~$ openssl rand -out /home/qiusj/.local/mongodb/mongo.keyfile -base64 90

所有副本集节点都必须要用同一份keyfile,一般是在一台机器上生成,然后拷贝到其他机器上,且必须有读取权限,否则将来会报错。

一定要保证密钥文件一致,文件位置随便。

第二步:将生成的加密文件拷贝到节点的目录下

$ cp mongo.keyfile /home/qiusj/.local/mongodb/replica_sets/myrs_27017
$ cp mongo.keyfile /home/qiusj/.local/mongodb/replica_sets/myrs_27018
$ cp mongo.keyfile /home/qiusj/.local/mongodb/replica_sets/myrs_27019

第三步:修改加密文件的权限

$ ll
-rw-r--r-- 1 qiusj intplanet  122 1月  21 17:44 mongo.keyfile
#如果这种权限启动服务会报错“permissions on ../mongo.keyfile are too open”当前加密文件的权限过大

#修改成只对当前用户只读
$ chmod 400 ./mongo.keyfile 
-r-------- 1 qiusj intplanet  122 1月  21 17:44 mongo.keyfile

修改配置文件指定keyfile

分别编辑几个服务的mongod.conf文件,添加相关内容:

$ vi /home/qiusj/.local/mongodb/replica_sets/myrs_27017/mongod.conf

security:
   # KeyFile鉴权文件
   keyFile: /home/qiusj/.local/mongodb/replica_sets/myrs_27017/mongo.keyfile
   # 开启认证方式
   authorization: enabled
security:
   # KeyFile鉴权文件
   keyFile: /home/qiusj/.local/mongodb/replica_sets/myrs_27018/mongo.keyfile
   # 开启认证方式
   authorization: enabled
security:
   # KeyFile鉴权文件
   keyFile: /home/qiusj/.local/mongodb/replica_sets/myrs_27019/mongo.keyfile
   # 开启认证方式
   authorization: enabled

启动副本集

如果副本集是开启状态,则分别关闭副本集中的每个mongod进程,从次节点开始,直到副本集的所有成员都离线,包括仲裁者。主节点必须最后一个成员关闭义避免潜在的回滚。

分别启动副本集节点:

$ /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27017/mongod.conf 
$ /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27018/mongod.conf 
$ /usr/local/mongodb/bin/mongod -f /home/qiusj/.local/mongodb/replica_sets/myrs_27019/mongod.conf 

$ /usr/local/mongodb/bin/mongo --port=27017

在主节点上添加普通账号

myrs:PRIMARY> use admin
switched to db admin
myrs:PRIMARY> db.auth("myroot","123456")
1
myrs:PRIMARY> show dbs
admin    0.000GB
article  0.000GB
config   0.000GB
local    0.002GB

#切换到article库
myrs:PRIMARY> use article
switched to db article
#创建操作article库的普通用户moon
myrs:PRIMARY> db.createUser({user:"moon",pwd:"moon",roles:[{role:"readWrite",db:"article"}]})
Successfully added user: {
        "user" : "moon",
        "roles" : [
                {
                        "role" : "readWrite",
                        "db" : "article"
                }
        ]
}

SpringData连接副本集

使用用户名和密码连接到MongoDB服务器必须使用username:password@hostname/dbname格式

application.yml

spring:
  #数据源配置
  data:
    mongodb:
      #连接分片集群的路由节点多个路由用逗号分隔mongodb会有其负载均衡的策略
#      uri: mongodb://192.168.0.152:27017,192.168.0.152:27117/meface
#      host: 192.168.0.152
#      database: meface
#      port: 27017
      #使用uri的方式
#      uri:mongodb://192.168.0.152:27017/meface

      #开启安全认证后的单机连接uri
#      uri: mongodb://star:star@192.168.0.152:27017/meface
      #开启安全认证后的副本集连接uri
      uri: mongodb://moon:moon@192.168.0.152:27017,192.168.0.152:27018,192.168.0.152:27019/article?connect=replicaSet&slaveOk=true&replicaSet=myrs

参考文章

[1] Replication https://docs.mongodb.com/manual/replication/

[2] Sharding https://docs.mongodb.com/manual/sharding/

[3] Sharding reference https://docs.mongodb.com/manual/reference/sharding/

[4] Security Reference https://docs.mongodb.com/manual/reference/security/