Franz`s blog

mongos权限校验逻辑

背景

经常会发现线上权限的调整好像在mongos当中并没有立即生效,这是一个比较奇怪的问题,下面对这个问题进行一下研究。

mongos 认证逻辑分析

Mongos 的权限逻辑全部存在于 config server 当中。通过分析源码可以发现,当通过 mongos 访问认证以及访问数据库的时候,会经过以下过程:
alt text

  1. 首次请求:mongos 从 config server 获取权限信息,计算后存储到 Session 中
  2. 后续请求:直接从 Session 中读取权限信息
  3. 后台线程:每隔 30秒 刷新权限缓存信息,标记旧信息为过期
  4. 过期检查:后续请求发现 Session 中的权限信息已过期时,才会重新从 config 拉取最新权限

所以我们可以得出一个结论:设置权限后的 30s 内,权限变更在其他 mongos 上是不生效的,除非是在设置权限的那个 mongos 上(该 mongos 上的权限缓存会被立即置为无效)。

举个例子:有两个 mongos 实例,在 mongos1 上设置了权限之后,mongos1 上马上无法执行 dropDatabase(),而 mongos2 还是可以正常执行的。等待 30s 后,mongos2 的缓存刷新,权限才会生效。

alt text

解决方案

如果不想等待 30s 的缓存刷新周期,可以手动刷新 mongos 的用户信息缓存,让权限立即生效:

1
db.getSiblingDB("admin").runCommand({ invalidateUserCache: 1 })

执行该命令后,mongos 会立即清除本地的用户权限缓存,下次请求时会重新从 config server 拉取最新的权限信息,权限变更马上生效。

验证流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 权限变更后,直接执行会报 Unauthorized
mongos> db.dropDatabase()
{
"ok" : 0,
"errmsg" : "not authorized on ttt to execute command { dropDatabase: 1.0, ... }",
"code" : 13,
"codeName" : "Unauthorized"
}

// 2. 手动刷新用户缓存
mongos> db.getSiblingDB("admin").runCommand({ invalidateUserCache: 1 })
{
"ok" : 1
}

// 3. 再次执行,权限立即生效
mongos> db.dropDatabase()
{
"ok" : 1
}

总结