如何使用 MongoDB 访问控制

作者选择了 开放式互联网 / 自由言论基金作为 写给捐赠计划的一部分接受捐款。

介绍

现代数据库系统能够存储和处理大量数据. 因此,对于任何一个用户来说,只负责处理与管理数据库相关的所有活动是相对罕见的。 通常,不同的数据库用户需要不同级别的访问到数据库的某些部分:有些用户可能只需要在特定数据库中阅读数据,而其他人则必须能够插入新文档或修改现有文档。

MongoDB使用一个强大的机制来控制数据库系统的访问和特权,称为 Role-Based Access Control (RBAC)。

前提条件

要遵循本教程,您将需要:

  • 一个拥有sudo特权和与 UFW 配置的防火墙的常规非 root 用户的服务器. 您可以通过遵循此 初始服务器设置教程来准备您的服务器。
  • MongoDB 安装在您的服务器上。 要设置此功能,请遵循我们在 如何在 Ubuntu 20.04 上安装 MongoDB 的教程上的教程。
  • 您的服务器的 MongoDB 实例通过启用身份验证和创建管理用户进行安全。

<$>[注] 注: 示例教程如何配置您的服务器,安装,然后保护MongoDB安装是指Ubuntu 20.04.本教程专注于MongoDB本身,而不是潜在的操作系统。

MongoDB如何使用基于角色的访问控制来控制访问

Access control - 也称为 authorization - 是一种安全技术,涉及确定谁可以访问哪些资源。

为了更好地了解MongoDB中的访问控制,首先将其区分为不同的但密切相关的概念:身份验证。 Authentication 是确认用户或客户端是否实际上是他们声称的人的过程。

以下子部分扩展到 MongoDB 如何处理身份验证和授权。

MongoDB 中的身份验证

在许多数据库管理系统中,用户只用一个用户名和密码对来识别。当连接到具有有效身份验证的数据库时,用户会被验证并获得与该用户相关的访问级别。

相比之下,MongoDB 使用了一个更复杂的用户目录结构. 在MongoDB 中,用户不仅通过其用户名而被识别,而且还通过其创建的 database 来识别。

这意味着在 MongoDB 中,只要他们在不同的身份验证数据库中创建,就可以有多个用户使用相同的用户名(例如sammy)。

可以假设在特定身份验证数据库中创建的用户只可访问该特定数据库的权限,但并非如此. 每个用户,无论在哪个身份验证数据库中创建,都可以在不同的数据库中分配权限。

MongoDB(基于角色访问控制)

在 MongoDB 中,您可以通过一个名为角色基于访问控制(通常简称为 _RBAC)的机制来控制谁可以访问数据库中的哪些资源,以及在何种程度上。

在基于角色的访问控制中,用户不会被授权直接对资源执行操作,例如将新文档插入数据库或查询特定集合。

例如,管理员可以有意义地阅读和写入公司MongoDB实例中的每个文档,而销售分析师可能只能访问销售记录。

每个特权由一项操作(如创建新文档、从文档中获取数据或创建和删除用户)和可执行该操作的资源(如名为报告的数据库或名为订单的集合)组成。

角色是通过角色名和数据库的组合来识别的,因为每个角色 - 除了在管理员数据库中创建的角色 - 只能包含适用于其自己的数据库的权限。 通过授予用户在其身份验证数据库以外的数据库中定义的角色,用户可以获得在多个数据库上操作的权限。

MongoDB 提供了一套内置角色,描述数据库系统中常用的权限,例如授予只读访问权限,读写授予读写权限,或dbOwner授予给定数据库的完整行政权限。

<$>[注] :您可以在官方 MongoDB 文档中找到 MongoDB 提供的所有内置角色的详细信息。

基于角色的访问控制允许用户仅分配最低、准确的访问权限级别,以便他们能够完成各自的任务,这是一个重要的安全实践,被称为最少特权原则。

通过跟随本指南,您将构建一个示例数据库环境,创建几个示例数据库和用户,每个都有不同的访问级别,展示基于角色的访问控制在行动。

步骤 1 – 概述示例场景并准备示例数据库

为了解释基于角色访问控制(简称RBAC)在实践中是如何工作的,本指南遵循一个使用两个数据库的想象性销售公司Sammy Sales的示例场景。

第一个数据库(称为销售)将存储公司商店的客户订单数据,分为两个单独的集合:客户个人数据的客户和订单细节的订单

第二个数据库(称为报告)将存储每月销售的汇总报告,该数据库将包含一个名为报告的单一集合。

该公司只有两名员工,两人都有遵循最少特权方法的数据库访问级别:

  • 销售代表Sammy需要完全访问销售数据库中的两个集合,但不需要与报告数据库合作。
  • Joe,销售分析师,需要写入报告数据库以构建报告,以及只读的访问销售数据库以获取数据。

要求在下面的图表中呈现:

The levels of access required for Sammy and Joe

要创建这些样本数据库,请在您安装 MongoDB 的服务器上使用以下命令打开 MongoDB 壳,以确保您在过程中作为管理用户进行身份验证。本示例遵循先决条件 How To Secure MongoDB on Ubuntu 20.04 教程中规定的惯例,其中管理 MongoDB 用户名为 AdminSammy

1mongo -u AdminSammy -p --authenticationDatabase admin

当被提示时,输入您在安装过程中设置的密码来访问壳。

您可以通过发出显示 dbs命令来验证您是否可以访问整个 MongoDB 实例:

1show dbs

这将返回当前可用的所有数据库的列表:

1[secondary_label Output]
2admin 0.000GB
3config 0.000GB
4local 0.000GB

确认您可以访问这些数据库后,切换到销售数据库:

1use sales

壳将以简短的确认回复

1[secondary_label Output]
2switched to db sales

在 MongoDB 中,没有明确的操作来创建数据库. 只有当数据库存储至少一个文档时才会创建数据库. 考虑到这一点,您需要插入一些样本文档来准备本指南中的示例中使用的数据库和集合。

你现在在销售数据库中,但直到你插入它之前,它实际上不会存在。

销售中创建一个名为客户的集合,并同时使用以下操作将新文档插入其中:

1db.customers.insert({name: 'Sammy'})

此示例文档仅包含一个名称字段,值为Sammy。 请注意,数据本身对于展示访问权利在实践中如何运作并不重要,因此,本步骤概述了如何创建仅包含示例模仿数据的数据库文档。

MongoDB 将通过:

1[secondary_label Output]
2WriteResult({ "nInserted" : 1 })

重复这个过程,但这一次创建一个名为订单的集合。 要做到这一点,运行以下命令. 此时,文档的唯一字段是总数,值为100:

1db.orders.insert({total: 100})

MongoDB 将再次确认文档已正确插入。

由于您将使用两个数据库,您还需要准备报告数据库,首先切换到报告数据库:

1use reports

然后插入另一个文档,这一次进入报告收藏:

1db.reports.insert({orders: 1})

若要确认两个数据库已正确准备,请再次发出显示 dbs命令。

1show dbs

在第二次运行此命令后,结果将显示新创建的数据库的两个新条目. 这些数据库仅在您在每个数据库中创建了第一个文档后才存在:

1[secondary_label Output]
2admin 0.000GB
3config 0.000GB
4local 0.000GB
5reports 0.000GB
6sales 0.000GB

现在,您可以创建一对用户,他们将有最少的访问权限新创建的数据库需要这个示例场景。

步骤二:创建第一个用户

在此步骤中,您将创建两个 MongoDB 用户中的第一个用户. 这个第一个用户将是公司销售代表 Sammy 的帐户. 此帐户将需要完全访问销售数据库,但没有任何访问报告数据库。

为此,我们将使用内置的readWrite角色授予销售数据库中的阅读和写入资源,因为Sammy是销售代表,我们还将使用销售数据库作为新创建的用户的身份验证数据库。

首先,转到销售数据库:

1use sales

壳将返回您正在使用所选数据库的确认:

1[secondary_label Output]
2switched to db sales

由于Sammy在销售部门工作,所以他们的MongoDB用户帐户将被创建以销售作为身份验证数据库。

运行以下方法来创建 sammy 用户:

1db.createUser(
2  {
3    user: "sammy",
4    pwd: passwordPrompt(),
5    roles: [
6      { role: "readWrite", db: "sales" }
7    ]
8  }
9)

createUser方法包括以下对象:

*用户代表用户名,在本示例中是 sammy

  • pwd代表密码。使用 passwordPrompt() 将确保 MongoDB 壳在执行要输入的命令时会要求密码。
  • roles 是授予的角色列表。本示例分配给 sammyreadWrite 角色,允许他们阅读和写入 sales 数据库。

<$>[注意] 注意:正如《如何保护MongoDB》(https://andsky.com/tech/tutorials/how-to-secure-mongodb-on-ubuntu-20-04)的先决条件教程中提到的,‘passwordPrompt()’方法仅与MongoDB版本 4.2 和更高版本兼容。

1. . .
2    pwd: "password",
3. . .

美元

如果此方法成功,则将返回来自 MongoDB 壳的确认消息,如下:

 1[secondary_label Output]
 2Successfully added user: {
 3    "user" : "sammy",
 4    "roles" : [
 5    	{
 6    		"role" : "readWrite",
 7    		"db" : "sales"
 8    	}
 9    ]
10}

现在,您可以验证新用户是否可以登录数据库,以及您指定的访问权利是否得到适当执行。

您将保留当前的 MongoDB 壳与您的管理员用户登录开放后,所以打开 一个单独的服务器会话

从新的服务器会话中,打开 MongoDB 壳. 此时,指定指定 sammy 作为用户和销售作为身份验证数据库:

1[environment second]
2mongo -u sammy -p --authenticationDatabase sales

输入您创建 sammy 用户时设置的密码. 访问 shell 提示后,执行显示 dbs命令列出可用的数据库:

1[environment second]
2show dbs

与您的管理帐户不同,只会列出一个数据库为 sammy,因为您只授予他们访问销售数据库。

1[environment second]
2[secondary_label Output]
3sales 0.000GB

现在检查 sammy 是否可以从销售数据库中从两个收藏中获取对象。

1[environment second]
2use sales

然后尝试恢复所有客户:

1[environment second]
2db.customers.find()

查找命令将返回您在步骤 1 中创建的文档。

1[environment second]
2[secondary_label Output]
3{ "_id" : ObjectId("60d888946ae8ac2c9120ec40"), "name" : "Sammy" }

您还可以确认第二个收藏,即订单,按预定可用:

1[environment second]
2db.orders.find()
1[environment second]
2[secondary_label Output]
3{ "_id" : ObjectId("60d890730d31cc50dedea6ff"), "total" : 100 }

要确保对销售数据库的访问权限已正确配置,您可以检查 sammy 是否也可以插入新文档。

1[environment second]
2db.customers.insert({name: 'Ellie'})

由于您授予 sammyreadWrite的角色,他们被授权将新文档写入该数据库。

1[secondary_label Output]
2[environment second]
3WriteResult({ "nInserted" : 1 })

最后,检查 sammy是否可以访问报告数据库.他们将无法读取或写入该数据库中的任何数据,因为您没有通过分配的角色授予他们访问权限。

转到报告数据库:

1[environment second]
2use reports

使用命令不会导致任何错误,请尝试通过以下操作访问您在步骤 1 中插入的文档:

1[environment second]
2db.reports.find()

现在,MongoDB 将扔出一个错误消息,而不是返回任何对象:

1[secondary_label Output]
2[environment second]
3Error: error: {
4  "ok" : 0,
5  "errmsg" : "not authorized on reports to execute command { find: \"reports\", filter: {}, lsid: { id: UUID(\"cca9e905-89f8-4903-ae12-46f23b43b967\") }, $db: \"reports\" }",
6  "code" : 13,
7  "codeName" : "Unauthorized"
8}

未经授权错误消息告诉您,sammy没有足够的访问权限来与报告数据库中的数据进行交互。

到目前为止,您已经创建了有限权限的第一个数据库用户,并验证了访问权限得到正确执行,接下来,您将创建具有不同的权限的第二个用户。

步骤 3 – 创建第二个用户

在为销售代表Sammy创建了 sammy MongoDB用户之后,您仍然需要为公司的销售分析员Joe创建帐户。

创建这个新用户帐户的过程类似于您创建 sammy用户的过程。

返回您的管理用户登录到 MongoDB 壳的服务器会话,从那里切换到报告数据库:

1use reports

由于乔在报告部门工作,所以他们的MongoDB用户帐户将被创建以报告作为身份验证数据库。

使用以下命令创建新的 joe用户:

 1db.createUser(
 2  {
 3    user: "joe",
 4    pwd: passwordPrompt(),
 5    roles: [
 6      { role: "readWrite", db: "reports" },
 7      { role: "read", db: "sales" }
 8    ]
 9  }
10)

在上一步中,请注意创建 joe 的方法和创建 sammy 的方法之间的差异。

  • 应用于报告数据库的readWrite意味着 joe将能够读取和写入该数据库的销售报告数据 *应用于销售数据库的readWrite确保 joe可以访问销售数据,但无法将任何文档写入该数据库

这两个角色都是内置的MongoDB角色。

此命令将返回类似于以下的确认消息:

 1[secondary_label Output]
 2Successfully added user: {
 3    "user" : "joe",
 4    "roles" : [
 5    	{
 6    		"role" : "readWrite",
 7    		"db" : "reports"
 8    	},
 9    	{
10    		"role" : "read",
11    		"db" : "sales"
12    	}
13    ]
14}

接下来,检查新用户的权限是否得到正确执行。

再次打开 另一个服务器会话,因为您将在后一步使用管理 MongoDB 用户和 sammy 用户。

打开 MongoDB 壳,此时将 joe指定为用户和报告作为身份验证数据库:

1[environment third]
2mongo -u joe -p --authenticationDatabase reports

当被提示时,输入您在创建 joe用户时设置的密码.一旦您可以访问壳提示,执行显示dbs命令列出可用于 joe的数据库:

1[environment third]
2show dbs

由于 joe可以使用销售报告数据库,这两个数据库将在输出中列出:

1[environment third]
2[secondary_label Output]
3reports 0.000GB
4sales 0.000GB

现在您可以检查 ** joe** 是否可以从销售数据库中获取对象。

转到销售:

1[environment third]
2use sales

运行以下查找命令来尝试检索所有命令:

1[environment third]
2db.orders.find()

假设您正确设置权限,此命令将返回您在步骤 1 中创建的单一文档。

1[environment third]
2[secondary_label Output]
3{ "_id" : ObjectId("60d890730d31cc50dedea6ff"), "total" : 100 }

接下来,尝试将新文档插入订单集合:

1[environment third]
2db.orders.insert({total: 50})

由于您仅为此数据库分配了 joe角色,因此此插入命令会出现错误消息:

1[secondary_label Output]
2[environment third]
3WriteCommandError({
4  "ok" : 0,
5  "errmsg" : "not authorized on sales to execute command { insert: \"orders\", ordered: true, lsid: { id: UUID(\"ebbe853b-e269-463f-a1d4-2c5a5accb966\") }, $db: \"sales\" }",
6  "code" : 13,
7  "codeName" : "Unauthorized"
8})

未经授权消息告诉您失败的原因 - joe 的访问权限不足以插入新文档。

接下来,确认 ** joe** 是否能够读取和写入报告数据库中的数据。

转到报告:

1[environment third]
2use reports

然后,尝试使用找到命令访问其中的数据:

1[environment third]
2db.reports.find()

由于 joe可以从数据库中读取,MongoDB 将用本集合中目前可用的文档列表回复:

1[secondary_label Output]
2[environment third]
3{ "_id" : ObjectId("60d8897d6ae8ac2c9120ec41"), "orders" : 1 }

然后尝试通过运行以下命令插入新报告:

1[environment third]
2db.reports.insert({orders: 2})

此命令还将成功使用类似于此的输出消息:

1[secondary_label Output]
2[environment third]
3WriteResult({ "nInserted" : 1 })

通过此,您创建了具有有限权限的第二个数据库用户,但这一次您向他们授予了两个单独的数据库的角色。

步骤 4 – 为现有用户授予和撤销角色

在步骤 2 和 3 中,您创建了新用户,并在创建过程中分配给角色。在实践中,数据库管理员通常需要撤销或授予已经在系统中创建的用户的新权限。

管理壳,切换到用户 sammy创建的销售数据库:

1use sales

要验证用户 sammy存在,请执行显示用户命令:

1show users

此命令将返回该数据库中的所有用户以及各自的角色列表:

 1[secondary_label Output]
 2{
 3    "_id" : "sales.sammy",
 4    "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"),
 5    "user" : "sammy",
 6    "db" : "sales",
 7    "roles" : [
 8    	{
 9    		"role" : "readWrite",
10    		"db" : "sales"
11    	}
12    ],
13    "mechanisms" : [
14    	"SCRAM-SHA-1",
15    	"SCRAM-SHA-256"
16    ]
17}

要将新角色分配给该用户,您可以使用grantRolesToUser方法. 此方法接受用户的名称和要在创建新用户时使用的语法中分配的角色列表。

本步骤的目标是为报告数据库授予 sammy 只读权限,因此为该数据库分配阅读角色:

1db.grantRolesToUser("sammy", [{role: "read", db: "reports"}])

该命令不会产生任何输出,除非出现错误,因此没有任何消息是预期的行为。

1show users

此命令将返回类似于以下的输出:

 1[secondary_label Output]
 2{
 3    "_id" : "sales.sammy",
 4    "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"),
 5    "user" : "sammy",
 6    "db" : "sales",
 7    "roles" : [
 8    	{
 9    		"role" : "read",
10    		"db" : "reports"
11    	},
12    	{
13    		"role" : "readWrite",
14    		"db" : "sales"
15    	}
16    ],
17    "mechanisms" : [
18    	"SCRAM-SHA-1",
19    	"SCRAM-SHA-256"
20    ]
21}

请注意在角色部分中新增的角色。

现在您可以检查 sammy是否真的能够访问报告数据库,然后切换到终端窗口使用 sammy登录,然后再次尝试访问报告。

转到报告数据库:

1[environment second]
2use reports

然后在报告集合中运行查找命令:

1[environment second]
2db.reports.find()

上次,该命令没有出现错误消息,但这次它会返回报告数据库中的文档列表:

1[secondary_label Output]
2[environment second]
3{ "_id" : ObjectId("60d8897d6ae8ac2c9120ec41"), "orders" : 1 }
4{ "_id" : ObjectId("60d899cafe3d26bf80e947fd"), "orders" : 2 }

过了一段时间后,您可能想要取消 sammy用户访问报告的能力. 为了说明这一点,请返回 管理控制台并执行以下命令,从而取消 sammy用户在报告数据库上的阅读权限:

1db.revokeRolesFromUser("sammy", [{role: "read", db: "reports"}])

revokeRolesFromUser 方法采用与grantRolesToUser 相同的参数集,但取代了指定的角色。

再次,您可以验证该角色不再可用显示用户:

 1[secondary_label Output]
 2{
 3    "_id" : "sales.sammy",
 4    "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"),
 5    "user" : "sammy",
 6    "db" : "sales",
 7    "roles" : [
 8    	{
 9    		"role" : "readWrite",
10    		"db" : "sales"
11    	}
12    ],
13    "mechanisms" : [
14    	"SCRAM-SHA-1",
15    	"SCRAM-SHA-256"
16    ]
17}

若要重复检查 sammy 不能再从报告数据库中读取,请尝试在 sammy 登录的壳中重新运行以前的查找命令:

1[environment second]
2db.reports.find()

这一次,命令将再次出现未经授权错误:

1[secondary_label Output]
2[environment second]
3Error: error: {
4  "ok" : 0,
5  "errmsg" : "not authorized on reports to execute command { find: \"reports\", filter: {}, lsid: { id: UUID(\"2c86fba2-7615-40ae-9c3b-2dfdac2ed288\") }, $db: \"reports\" }",
6  "code" : 13,
7  "codeName" : "Unauthorized"
8}

这表明您取消了 sammy 用户的阅读角色。

结论

在本文中,您了解如何创建有限访问数据库的用户,并使用基于角色访问控制来在数据库中执行最少特权原则,只授予用户最少必要的特权集,您还了解如何授予和撤销现有用户的角色,学习如何在需要的权限随着时间的推移而改变时管理活数据库服务器上的访问权限,并验证更改立即生效。

借助 MongoDB 的基于角色访问控制,您可以使用用户定义角色等功能来定义准确的访问级别,在内置角色不满意时创建自定义角色,以及集合级别访问控制,允许管理员将用户授予特权给特定集合而不是整个数据库。

Published At
Categories with 技术
comments powered by Disqus