最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】mongo中给普通数据库gridfs创建root的角色失败:Error couldn’t add user No role named root@gridfs

MongoDB crifan 7727浏览 0评论

折腾:

【已解决】MongoDB开启访问控制后currentOp出错:not authorized on admin to execute command

期间,为了给另外一个普通(而非admin)数据库gridfs,去添加支持currentOp的权限,在还没开启访问控制之前,而尝试直接添加root这个role,结果出错:

<code>&gt; use gridfs
switched to db gridfs
&gt; db.createUser({
...     user: "gridfs",
...     pwd: "pwd",
...     roles: [ { role: "root", db: "gridfs" } ]
... })
2018-04-09T11:37:48.128+0800 E QUERY    [thread1] Error: couldn't add user: No role named root@gridfs :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1267:15
@(shell):1:1
</code>

好像是普通的数据库不存在root的role。

继续去看了:

Built-In Roles — MongoDB Manual 3.6

后才知道:

原来是:

之前的dbAdmin,只是针对于Database Administration Roles这些种类的权限

而root本身来说,貌似?(反正没有完全看懂)只是针对于admin数据库的,或者说只能在admin数据库中授权root这个role 

-》因为root权限太大了:

root = readWriteAnyDatabase + dbAdminAnyDatabase + userAdminAnyDatabase + clusterAdmin + restore + backup

-》而其中的:

clusterAdmin = clusterManager + clusterMonitor +hostManager + dropDatabase

-》而其中的:

clusterMonitor = 。。。 + inprog + 。。。 + serverStatus + 。。。

才有我们要的inprog(和类似的serverStatus)

mongo Error: couldn’t add user: No role named root

security – What MongoDB user privileges do I need to add a user to a new/another mongo database? – Stack Overflow

mongodb – When creating first admin user on mongdb cluster getting error “couldn’t add user: not authorized on admin to execute command” – Stack Overflow

permissions – Creating first user in MongoDB 3.2. – Stack Overflow

mongodb – Error: couldn’t add user: not authorized on test to execute command { createUser: – Stack Overflow

mongodb – Can’t create user – Stack Overflow

mongo 系统权限问题 – CSDN博客

MongoDB3.2 用户权限 – CSDN博客

<code>  Built-In Roles(内置角色):
 .1. 数据库用户角色:read、readWrite;

  .2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;

  .3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;

  .4. 备份恢复角色:backup、restore;

  .5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase

  .6. 超级用户角色:root 

    // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
  .7. 内部角色:__system

</code>

Read:允许用户读取指定数据库

readWrite:允许用户读写指定数据库

backup,retore:在进行备份、恢复时可以单独指定的角色,在db.createUser()方法中roles里面的db必须写成是admin库,要不然会 报错

dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile

userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户

clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。

readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限

readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限

userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限,

dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。

root:只在admin数据库中可用。超级账号,超级权限

->

此处,给之前没有clusterAdmin的话,想要给已有用户增加clusterAdmin的话,可以:

方式1:用db.updateUser去更新用户,加上权限

方式2:用db.grantRolesToUser,额外赋上权限

当然,也可以在新建用户时,直接加上该权限

所以分别去试试:

但是看到了:

“clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。”

难道是:

非admin数据库中,也是无法使用clusterAdmin的?

去试试

<code>&gt; use gridfs
switched to db gridfs
&gt; show users
&gt; db.createUser({
...     user: "gridfs",
...     pwd: "pwd",
...     roles: [
...         { role: "dbOwner", db: "gridfs" },
...         { role: "clusterAdmin", db: "gridfs" }
...     ]
... })
2018-04-09T13:52:28.435+0800 E QUERY    [thread1] Error: couldn't add user: No role named clusterAdmin@gridfs :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1267:15
@(shell):1:1
</code>

果然是的。

那如何才能实现:

对于普通的数据库,gridfs,中,使用db.currentOp()呢?

对了,好像此处的:

刚才新增的admin数据库中的用户:admin,给了root权限了

所以,用刚才新建的具有root的role的admin账号,应该可以去

use gridfs

db.currentOp()

去试试

果然可以的:

<code>&gt; use gridfs
switched to db gridfs
&gt; db.currentOp()
{
        "inprog" : [
                {
                        "desc" : "conn2",
                        "threadId" : "139685965211392",
                        "connectionId" : 2,
                        "client" : "127.0.0.1:38502",
                        "active" : true,
                        "opid" : 19527,
                        "secs_running" : 0,
                        "microsecs_running" : NumberLong(33),
                        "op" : "command",
                        "ns" : "admin.$cmd",
                        "query" : {
                                "currentOp" : 1
                        },
                        "numYields" : 0,
                        "locks" : {

                        },
                        "waitingForLock" : false,
                        "lockStats" : {

                        }
                }
        ],
        "ok" : 1
}
</code>

【总结】

此处,对于root这个超级权限的系统内置角色来说,是只能针对admin数据库(中的用户)去设置的。

不能设置给其他(普通的)数据库,比如此处的gridfs。

而想要实现的效果是:

对于普通数据库gridfs,想要能否有用户可以具有权限,能够执行:db.currentOp()

具体做法是:

可以在admin数据库中,创建一个具有了inprog权限(比如:clusterMonitor -》 或更高权限的clusterAdmin -〉或更更高权限的root)的用户,比如:

<code>&gt; use admin
switched to db admin
&gt; db.runCommand({dropAllUsersFromDatabase: 1})
{ "n" : 1, "ok" : 1 }
&gt; db.createUser({
...     user: "root",
...     pwd: "pwd",
...     roles: [ { role: "root", db: "admin" } ]
... })
Successfully added user: {
        "user" : "root",
        "roles" : [
                {
                        "role" : "root",
                        "db" : "admin"
                }
        ]
}
&gt; show users
{
        "_id" : "admin.root",
        "user" : "root",
        "db" : "admin",
        "roles" : [
                {
                        "role" : "root",
                        "db" : "admin"
                }
        ]
}
</code>

注:

root = readWriteAnyDatabase + dbAdminAnyDatabase + userAdminAnyDatabase + clusterAdmin + restore + backup

-》而其中的:clusterAdmin = clusterManager + clusterMonitor +hostManager + dropDatabase

-》而其中的:clusterMonitor = … + inprog + … + serverStatus + …

然后就可以去到gridfs中去调用db.currentOp()了:

<code>&gt; use gridfs
switched to db gridfs
&gt; db.currentOp()
{
        "inprog" : [
                {
                        "desc" : "conn4",
                        "threadId" : "139685965211392",
                        "connectionId" : 4,
                        "client" : "127.0.0.1:38656",
                        "active" : true,
                        "opid" : 35287,
                        "secs_running" : 0,
                        "microsecs_running" : NumberLong(21),
                        "op" : "command",
                        "ns" : "admin.$cmd",
                        "query" : {
                                "currentOp" : 1
                        },
                        "numYields" : 0,
                        "locks" : {

                        },
                        "waitingForLock" : false,
                        "lockStats" : {

                        }
                }
        ],
        "ok" : 1
}
&gt; 
</code>

另外,再给gridfs创建,具有读写操作的权限的用户:

<code>&gt; use gridfs
switched to db gridfs
&gt; db.createUser({
...     user: "gridfs",
...     pwd: "pwd",
...     roles: [
...         { role: "dbOwner", db: "gridfs" }
...     ]
... })
Successfully added user: {
        "user" : "gridfs",
        "roles" : [
                {
                        "role" : "dbOwner",
                        "db" : "gridfs"
                }
        ]
}
&gt; show users
{
        "_id" : "gridfs.gridfs",
        "user" : "gridfs",
        "db" : "gridfs",
        "roles" : [
                {
                        "role" : "dbOwner",
                        "db" : "gridfs"
                }
        ]
}
</code>

远程(Mac本地电脑中mongo shell)去再多一个client去连接:

<code>➜  ~ mongo 47.96.131.109/gridfs -u gridfs -p pwd --authenticationDatabase gridfs
MongoDB shell version v3.6.3
connecting to: mongodb://47.96.131.109:27017/gridfs
MongoDB server version: 3.2.19
WARNING: shell and server versions do not match
Server has startup warnings:
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten]
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten]
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten]
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 4096 processes, 65535 files. Number of processes should be at least 32767.5 : 0.5 times number of files.
2018-04-09T11:32:34.196+0800 I CONTROL  [initandlisten]
&gt; db.stats()
{
    "db" : "gridfs",
    "collections" : 2,
    "objects" : 21612,
    "avgObjSize" : 238683.47043309273,
    "dataSize" : 5158427163,
    "storageSize" : 4873072640,
    "numExtents" : 0,
    "indexes" : 4,
    "indexSize" : 626688,
    "ok" : 1
}
&gt; db.version()
3.2.19
&gt; db.getName()
gridfs
</code>

后,输出效果是:

<code>&gt; use gridfs
switched to db gridfs
&gt; db.currentOp()
{
        "inprog" : [
                {
                        "desc" : "conn4",
                        "threadId" : "139685965211392",
                        "connectionId" : 4,
                        "client" : "127.0.0.1:38656",
                        "active" : true,
                        "opid" : 36140,
                        "secs_running" : 0,
                        "microsecs_running" : NumberLong(22),
                        "op" : "command",
                        "ns" : "admin.$cmd",
                        "query" : {
                                "currentOp" : 1
                        },
                        "numYields" : 0,
                        "locks" : {

                        },
                        "waitingForLock" : false,
                        "lockStats" : {

                        }
                }
        ],
        "ok" : 1
}
</code>

并没有看到期望的:

多出一个client连接的内容啊

后来发现是:

需要传递参数的:

db.currentOp(true)

== db.currentOp({ “$all”: true })

结果其中就能看到对应的client的IP了:

<code>&gt; db.currentOp(true)
{
        "inprog" : [
                {
                        "desc" : "conn7",
                        "threadId" : "139685965211392",
                        "connectionId" : 7,
                        "client" : "127.0.0.1:38658",
                        "active" : true,
                        "opid" : 36521,
                        "secs_running" : 0,
                        "microsecs_running" : NumberLong(22),
                        "op" : "command",
                        "ns" : "admin.$cmd",
                        "query" : {
                                "currentOp" : 1,
                                "$all" : true
                        },
                        "numYields" : 0,
                        "locks" : {

                        },
                        "waitingForLock" : false,
                        "lockStats" : {

                        }
                },
                {
                        "desc" : "ftdc",
                        "threadId" : "139685668132608",
                        "active" : false
                },
                {
                        "desc" : "clientcursormon",
                        "threadId" : "139685693310720",
                        "active" : false
                },
                {
                        "desc" : "conn6",
                        "threadId" : "139685657564928",
                        "connectionId" : 6,
                        "client" : "112.4.64.141:64029",
                        "active" : false
                },
                {
                        "desc" : "WTJournalFlusher",
                        "threadId" : "139685726881536",
                        "active" : false
                },
                {
                        "desc" : "TTLMonitor",
                        "threadId" : "139685701703424",
                        "active" : false
                },
                {
                        "desc" : "signalProcessingThread",
                        "threadId" : "139685810808576",
                        "active" : false
                },
                {
                        "desc" : "RangeDeleter",
                        "threadId" : "139685710096128",
                        "active" : false
                },
                {
                        "desc" : "initandlisten",
                        "threadId" : "139685965217216",
                        "active" : false
                }
        ],
        "ok" : 1
}
&gt; 
&gt; db.currentOp({ "$all": true })
{
        "inprog" : [
                {
                        "desc" : "conn7",
                        "threadId" : "139685965211392",
                        "connectionId" : 7,
                        "client" : "127.0.0.1:38658",
                        "active" : true,
                        "opid" : 36628,
                        "secs_running" : 0,
                        "microsecs_running" : NumberLong(43),
                        "op" : "command",
                        "ns" : "admin.$cmd",
                        "query" : {
                                "currentOp" : 1,
                                "$all" : true
                        },
                        "numYields" : 0,
                        "locks" : {

                        },
                        "waitingForLock" : false,
                        "lockStats" : {

                        }
                },
                {
                        "desc" : "ftdc",
                        "threadId" : "139685668132608",
                        "active" : false
                },
                {
                        "desc" : "clientcursormon",
                        "threadId" : "139685693310720",
                        "active" : false
                },
                {
                        "desc" : "conn6",
                        "threadId" : "139685657564928",
                        "connectionId" : 6,
                        "client" : "112.4.64.141:64029",
                        "active" : false
                },
                {
                        "desc" : "WTJournalFlusher",
                        "threadId" : "139685726881536",
                        "active" : false
                },
                {
                        "desc" : "TTLMonitor",
                        "threadId" : "139685701703424",
                        "active" : false
                },
                {
                        "desc" : "signalProcessingThread",
                        "threadId" : "139685810808576",
                        "active" : false
                },
                {
                        "desc" : "RangeDeleter",
                        "threadId" : "139685710096128",
                        "active" : false
                },
                {
                        "desc" : "initandlisten",
                        "threadId" : "139685965217216",
                        "active" : false
                }
        ],
        "ok" : 1
}
</code>

其中的:

112.4.64.141

就是我此处网络的IP地址。

另外发现,对于服务端来说的远程的client==Mac的本地的mongo shell中,也是可以执行同样命令,但是看到的结果,稍微不同的:

<code>&gt; db.currentOp(true)
{
    "inprog" : [
        {
            "desc" : "conn2",
            "threadId" : "139637896292096",
            "connectionId" : 2,
            "client" : "112.4.64.141:50438",
            "active" : true,
            "opid" : 369,
            "secs_running" : 0,
            "microsecs_running" : NumberLong(40),
            "op" : "command",
            "ns" : "admin.$cmd",
            "query" : {
                "currentOp" : 1,
                "$all" : true
            },
            "numYields" : 0,
            "locks" : {

            },
            "waitingForLock" : false,
            "lockStats" : {

            }
        },
        {
            "desc" : "ftdc",
            "threadId" : "139637906859776",
            "active" : false
        },
        {
            "desc" : "clientcursormon",
            "threadId" : "139637932037888",
            "active" : false
        },
        {
            "desc" : "TTLMonitor",
            "threadId" : "139637940430592",
            "active" : false
        },
        {
            "desc" : "WTJournalFlusher",
            "threadId" : "139637965608704",
            "active" : false
        },
        {
            "desc" : "conn1",
            "threadId" : "139638203938560",
            "connectionId" : 1,
            "client" : "127.0.0.1:38660",
            "active" : false
        },
        {
            "desc" : "signalProcessingThread",
            "threadId" : "139638049535744",
            "active" : false
        },
        {
            "desc" : "RangeDeleter",
            "threadId" : "139637948823296",
            "active" : false
        },
        {
            "desc" : "initandlisten",
            "threadId" : "139638203944384",
            "active" : false
        }
    ],
    "ok" : 1
}
&gt;
</code>

即:

对于在执行db.currentOp的的一方来说,显示自己的连接的client时,可以显示详细信息(包括额外的opid,secs_running等等字段),否则只能显示简要的信息(client,其中带IP和端口)。

另外,可以用:

Mongodb list current connection – Database Administrators Stack Exchange

database – how to get connected clients in MongoDB – Stack Overflow

中的:

<code>db.currentOp().inprog.forEach(function(x) { print(x.client) })
db.currentOp(true).inprog.forEach(function(x) { print(x.client) })
db.currentOp(true).inprog.forEach(function(d){if (d.client)printjson(d.client)})
</code>

过滤其他信息,只显示出对应的IP(和端口)了:

<code>&gt; db.currentOp().inprog.forEach(function(x) { print(x.client) })
112.4.64.141:55837
&gt; db.currentOp(true).inprog.forEach(function(x) { print(x.client) })
112.4.64.141:55837
[unknown type]
[unknown type]
[unknown type]
[unknown type]
127.0.0.1:53578
[unknown type]
[unknown type]
[unknown type]
&gt; db.currentOp(true).inprog.forEach(function(d){if (d.client)printjson(d.client)})
"112.4.64.141:55837"
"127.0.0.1:53578"
</code>

搞懂了js的函数后,可以写成更清楚的定义:

<code>&gt; db.currentOp(true).inprog.forEach(function(eachConnection){if (eachConnection.client){printjson(eachConnection.client)}})
"112.4.64.141:59012"
"112.4.64.141:59013"
"112.4.64.141:56260"
"127.0.0.1:34168"
</code>

其中:

<code>db.currentOp(true).inprog.forEach(function(eachConnection){if (eachConnection.client){printjson(eachConnection.client)}})
</code>

=

<code>db.currentOp(true).inprog.forEach(
  function(eachConnection) {
    if (eachConnection.client) {
      printjson(eachConnection.client)
    }
  }
)
</code>

而因为:db.currentOp(true)返回的就是一个dict对象,其中就一个inprog的key,值为一个list

所以可以用上述的函数,对于inprog的每个连接,去判断其中是否存在client这个key,

存在的话,就打印出来。

其中再去研究下一:

printjson

mongo printjson

Write Scripts for the mongo Shell — MongoDB Manual 3.6

“In interactive mode, mongo prints the results of operations including the content of all cursors. In scripts, either use the JavaScript print() function or the mongo specific printjson() function which returns formatted JSON.”

可以用js的print(),也可以用mongo的printjson()

所以,则也可以去改编上述函数为:

<code>db.currentOp(true).inprog.forEach(
  function(eachConnection) {
    if (eachConnection.client) {
      printjson(eachConnection)
    }
  }
)
</code>

结果是:

<code>&gt; db.currentOp(true).inprog.forEach(
...   function(eachConnection) {
...     if (eachConnection.client) {
...       printjson(eachConnection)
...     }
...   }
... )
{
    "desc" : "conn12",
    "threadId" : "139761728960256",
    "connectionId" : 12,
    "client" : "112.4.64.141:59012",
    "active" : false
}
{
    "desc" : "conn13",
    "threadId" : "139761730012928",
    "connectionId" : 13,
    "client" : "112.4.64.141:59013",
    "active" : false
}
{
    "desc" : "conn2",
    "threadId" : "139762038712064",
    "connectionId" : 2,
    "client" : "112.4.64.141:56260",
    "active" : true,
    "opid" : 27231,
    "secs_running" : 0,
    "microsecs_running" : NumberLong(33),
    "op" : "command",
    "ns" : "admin.$cmd",
    "query" : {
        "currentOp" : 1,
        "$all" : true
    },
    "numYields" : 0,
    "locks" : {

    },
    "waitingForLock" : false,
    "lockStats" : {

    }
}
{
    "desc" : "conn3",
    "threadId" : "139761731065600",
    "connectionId" : 3,
    "client" : "127.0.0.1:34168",
    "active" : false
}
&gt;
</code>

可以输出更加完整的每个client的连接信息。

转载请注明:在路上 » 【已解决】mongo中给普通数据库gridfs创建root的角色失败:Error couldn’t add user No role named root@gridfs

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.168 seconds, using 22.14MB memory