介绍
[MongoDB] (https://www.mongodb.com/),又名_Mongo_,是许多现代网络应用中使用的开源文件数据库. 它被归类为 [NoSQL database] (https://andsky.com/tech/tutorials/what-is-nosql),因为它不依赖关系数据库模型. 取而代之的是,它使用JSON式的有动态计划的文件. 这意味着,与关系数据库不同,蒙戈 在将数据添加到数据库之前, DB 不需要预先定义的计划 .
当您与多个分布的MongoDB实例合作时,比如一个replica set或一个硬化的数据库架构,必须确保它们之间的通信安全. 其中一个方法是通过keyfile 认证. 这涉及到创建一个特殊文件,基本上作为集群中每个成员的共享密码来发挥作用.
本教程概述了如何更新现有复制件集以使用 keyfile 身份验证。本指南所涉及的程序还将确保复制件集不会经历任何停机时间,因此复制件集中的数据将继续可供任何需要访问的客户端或应用程序使用。
前提条件
要完成本教程,您将需要:
- 联合国 三台服务器,每台运行Ubuntu 20.04. 所有这三台服务器都应有一个非根行政用户和一个配置有UFW的防火墙. 要设置此功能,请遵循我们的Ubuntu 20.04初始服务器设置指南.
- 蒙戈语 在您的 Ubuntu 服务器上安装 DB 。 跟踪我们关于 [如何在 Ubuntu 20. 04 上安装 MongoDB (https://andsky.com/tech/tutorials/how-to-install-mongodb-on-ubuntu-20-04] 的教程, 确保完成您服务器上的每个步骤 。
- 联合国 您的三台MongoDB设备 都配置为复制设备 。 在 [如何配置 Ubuntu 20.04 上的 MongoDB 复制集 (https://andsky.com/tech/tutorials/how-to-configure-a-mongodb-replica-set-on-ubuntu-20-04 ) 上遵循此教程来设置此功能 。
- 为每个服务器生成的SSH 密钥。 此外,您应确保每个服务器在其)。 .
请注意,为了明确起见,本指南将遵循在预先设定的复制套装教程中确立的惯例,并将三个服务器称为:mongo0 、** mongo1** 和** mongo2** 。 它还会假设您已经完成了该指南的第1步,并配置了每个服务器的"hosts"文件,这样以下主机名就可以解决给定服务器的IP地址:
主机名:
在本指南中有一些情况下,您必须在其中一个服务器上运行命令或更新文件,在这种情况下,本指南将默认使用 mongo0 在示例中,并将通过在蓝色背景中显示命令或文件更改来表示这一点:
1[environment second]
任何必须在 多个 服务器上执行的命令或文件更改都将具有标准的灰色背景,如下:
关于 Keyfile 身份验证
在MongoDB中,密钥文件认证依赖于Salted Challenge Reference Authority MAME (SCRAM),即数据库系统的默认认证机制. SCRAM涉及蒙戈 DB根据用户名、密码和认证数据库的组合读取并验证用户提交的证书,这些都为给定的MongoDB实例所了解。 这是用来认证在连接数据库时提供密码的用户的同一种机制.
在 keyfile 身份验证中, keyfile 作为群集中的每个成员的共享密码。 keyfile 必须包含 6 到 1024 个字符. Keyfiles 只能包含 base64 集中的字符,并且请注意,MongoDB 在阅读密钥时使用白空间字符。
< $ > [警告] 警告 :蒙戈的社区版本 DB有两种认证方法,可以帮助您的数据库保持安全,即:keyfile认证_和_x.509认证. 对于使用复制的制作部署,蒙戈DB文档推荐使用x.509认证,它将密钥文件描述为"最适合测试或开发环境的最小安全形式"
获取和配置 x.509 证书的过程伴随着一系列的警告和决定,必须根据个案做出决定,这意味着此过程超出了 DigitalOcean 教程的范围。如果您计划在生产环境中使用复制件,我们 强烈鼓励 您查看 x.509 身份验证的官方 MongoDB 文档。
如果您打算使用您的复制品集用于测试或开发,您可以继续按照本教程来添加一个安全层到您的集群。
第1步:创建用户管理员
当您在 MongoDB 中启用身份验证时,它还将为复制组启用 role-based access control。
MongoDB 使用基于角色的访问控制 (RBAC) 来管理访问 MongoDB 系统. 用户被授予一个或多个角色,决定用户访问数据库资源和操作。
当访问控制在 MongoDB 实例上启用时,这意味着您将无法访问系统上的任何资源,除非您已认证为有效的 MongoDB 用户。
如果你不为你的蒙戈创建用户 启用密钥文件认证前的 DB 系统( 因此, 访问控制) , 您将不会被锁出您的复制集 。 您可以创建 MongoDB 用户, 用于认证该集, 并在必要时通过 Mongo 的 [localhost 例外] (https://docs.mongodb.com/manual/core/security-users/# localhost-exception) 创建其他用户 。 这是MongoDB为已启用访问控制但缺乏用户的配置制作的特殊例外. 此例外只允许您连接到localhost 上的数据库,然后在`admin ' 数据库中创建一个用户.
然而,依靠当地主机的例外来创造出一个蒙戈 DB 用户在启用认证后, 意味着您的复制件集会经历一段停机时间, 因为复制件在创建用户后才能认证它们的连接 。 此步骤概述了如何创建用户_ 在_ 之前允许认证以确保您的复制集仍然可用 。 这个用户将拥有在数据库上创建其他用户的权限,使您可以自由地创建其他用户,并在未来需要任何权限. 在MongoDB中,拥有这种权限的用户被称为_用户管理员_.
要开始,请连接到您的复制组的 主要成员 . 如果您不确定您的成员中哪一个是主要成员,您可以运行 rs.status()
方法来识别它。
运行下列mongo
命令,从 Ubuntu 服务器中的任何一个托管 MongoDB 实例的 bash 提示,该命令的--eval
选项指示mongo
操作不要打开当你单独运行mongo
时出现的壳界面环境,而是运行命令或方法,包装在单个引文中,这跟随--eval
参数:
1mongo --eval 'rs.status()'
rs.status()
返回了大量的信息,但输出的相关部分是 "members" :
数组. 在 MongoDB 的背景下,数组是在一对方块([
和 `])之间持有的文档集合。
在会员
组中,你会发现一系列文件,每个文件都包含你的复制组中的某个成员的信息。在每个会员文件中,找到stateStr
字段。
1[secondary_label Output]
2. . .
3 "members" : [
4 {
5 "_id" : 0,
6 "name" : "mongo0.replset.member:27017",
7 "health" : 1,
8 "state" : 1,
9 "stateStr" : "PRIMARY",
10. . .
11 },
12. . .
一旦您知道您的复制组中的哪个成员是主要的,则将 SSH 输入到服务器托管该实例,为示范目的,本指南将继续使用 mongo0 是主要的示例:
1[environment local]
2ssh sammy@mongo0_ip_address
登录到服务器后,通过打开mongo
壳环境连接到MongoDB:
1[environment second]
2mongo
在 MongoDB 中创建用户时,您必须在特定数据库中创建用户,该数据库将作为其 authentication database。
某些管理操作只适用于用户,其身份验证数据库是admin
数据库 - 每个 MongoDB 安装中包含的特殊特权数据库 - 包括创建新用户的能力. 因为此步骤的目标是创建一个用户管理员,可以在复制集中创建其他用户,请连接到admin
数据库,以便您可以授予该用户相应的特权:
1[environment second]
2use admin
1[secondary_label Output]
2[environment second]
3switched to db admin
MongoDB 配备了 一些基于 JavaScript 的壳方法 您可以使用来管理您的数据库。其中一个,即 db.createUser
方法,用于在该方法运行的数据库中创建新用户。
启动db.createUser
方法:
1[environment second]
2db.createUser(
<$>[注] 注 :在您输入一个关闭组合之前,Mongo 将不会将 db.createUser
方法注册为完整。
请记住,MongoDB 将其数据存储在类似 JSON 的文档中;当您创建新用户时,您所做的就是创建一个文档以将相应的用户数据作为单个字段。
与 JSON 中的对象一样,MongoDB 中的文档始于和结束于弯曲的弯曲({
和 }
)。
1[environment second]
2{
接下来,输入一个用户:
字段,用您想要的用户名作为双引文中的值,然后是句子. 下面的示例指定用户名 UserAdminSammy ,但您可以输入您想要的用户名:
1[environment second]
2user: "UserAdminSammy",
接下来,输入一个)方法. 当您执行)
方法将为您提供一个提示,以便您输入密码。
<$>[注] 注 :《passwordPrompt()》方法仅与MongoDB版本 4.2 及更高版本兼容。如果您使用的是 Mongo 的较旧版本,则需要用清晰文本写出您的密码,类似于您写出用户名的方式:
1[environment second]
2pwd: "password",
美元
请确保跟随这个字段与一个子也:
1[environment second]
2pwd: passwordPrompt(),
然后输入一个角色
字段,随后添加一个细节,说明您希望您的管理用户拥有哪些角色。在 MongoDB 中, roles 定义了用户可以在他们可以访问的资源上执行哪些操作。
因为你正在创建一个用户管理员,至少你应该授予他们内置的 userAdminAnyDatabase
角色在 admin
数据库上. 这将允许用户管理员创建和修改新的用户和角色. 因为管理员用户在 admin
数据库中有这个角色,这也将允许它 超级用户访问整个集群:
1[environment second]
2roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
接下来,输入一个关闭键来表示文档的结束:
1[environment second]
2}
然后输入一个关闭方针以关闭并执行db.createUser
方法:
1[environment second]
2)
总的来说,这里是你的db.createUser
方法应该是什么样子:
1[environment second]
2> db.createUser(
3... {
4... user: "UserAdminSammy",
5... pwd: passwordPrompt(),
6... roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
7... }
8... )
如果每个行的语法正确,该方法将正确执行,您将被要求输入密码:
1[secondary_label Output]
2[environment second]
3Enter password:
输入您选择的强有力的密码,然后,您将收到确认用户已被添加:
1[secondary_label Output]
2[environment second]
3Successfully added user: {
4 "user" : "UserAdminSammy",
5 "roles" : [
6 {
7 "role" : "userAdminAnyDatabase",
8 "db" : "admin"
9 },
10 "readWriteAnyDatabase"
11 ]
12}
有了它,您已添加了一个 MongoDB 用户配置文件,您可以使用它来管理系统上的其他用户和角色,您可以通过创建另一个用户来测试这一点,如本步骤的剩余部分所述。
首先,将身份验证为您刚刚创建的用户管理员:
1[environment second]
2db.auth( "UserAdminSammy", passwordPrompt() )
db.auth()
如果验证成功,则返回1
:
1[secondary_label Output]
2[environment second]
31
注:在未来,如果您想在连接到群集时作为用户管理员进行身份验证,则可以通过以下命令直接从服务器提示处进行身份验证:
1mongo -u "UserAdminSammy" -p --authenticationDatabase "admin"
在此命令中, -u
选项告诉壳,下面的论点是您想要身份验证的用户名。 -p
旗号告诉您请求输入密码,而 --authenticationDatabase
选项先于用户身份验证数据库的名称。
另外,请注意,若要作为用户管理员在复制集中创建新用户,您必须连接到该集的主要成员。
添加另一个用户的程序与用户管理员相同。下面的示例创建了一个具有clusterAdmin
角色的新用户,这意味着他们将能够执行一些与复制和分解相关的操作。
在本教程中稍后启用 keyfile 身份验证后,任何想要执行)或)
方法 - 必须首先作为集群管理员进行身份验证。
也就是说,您可以为此用户提供您想要的任何角色,并为他们提供不同的名称和身份验证数据库,但是,如果您希望新用户作为集群管理员,那么您必须在admin
数据库中授予他们clusterAdmin
的角色。
除了创建用户作为集群管理员外,以下方法还会命名用户 ClusterAdminSammy ,并使用passwordPrompt()
方法提示您输入密码:
1[environment second]
2db.createUser(
3{
4user: "ClusterAdminSammy",
5pwd: passwordPrompt(),
6roles: [ { role: "clusterAdmin", db: "admin" } ]
7}
8)
再次,如果您使用的是版本 4.2 之前的 MongoDB 版本,那么您将不得不用清晰文本写出您的密码,而不是使用passwordPrompt()
方法。
如果每个行的语法正确,该方法将正确执行,您将被要求输入密码:
1[secondary_label Output]
2[environment second]
3Enter password:
输入您选择的强有力的密码,然后,您将收到确认用户已被添加:
1[secondary_label Output]
2[environment second]
3Successfully added user: {
4 "user" : "ClusterAdminSammy",
5 "roles" : [
6 {
7 "role" : "clusterAdmin",
8 "db" : "admin"
9 }
10 ]
11}
此输出确认您的用户管理员能够创建新用户并赋予他们角色,您现在可以关闭 MongoDB 壳:
1[environment second]
2exit
或者,您可以通过按CTRL + C
来关闭壳。
在此时,如果您有连接到 MongoDB 集群的客户端或应用程序,那么创建一个或多个具有相应角色的专用用户是很好的时机,他们可以使用它们来验证数据库。
步骤 2 — 创建和分发身份验证密钥文件
在创建 keyfile 之前,在每个服务器上创建一个目录可以帮助您将 keyfile 存储在那里,以便保持事物的组织。运行以下命令,在管理 Ubuntu 用户的主目录中创建一个名为mongo-security
的目录, **在您的三个服务器中的每一个:
1mkdir ~/mongo-security
然后在您的服务器中的一个上生成 keyfile. 您可以在您的任何服务器上执行此操作,但为了说明,本指南将在 mongo0 上生成 keyfile。
导航到您刚刚创建的mongo-security
目录:
1[environment second]
2cd ~/mongo-security/
在该目录中,使用以下openssl
命令创建 keyfile:
1[environment second]
2openssl rand -base64 768 > keyfile.txt
请注意这个命令的论点:
- `随机 ' :指示 OpenSSL 生成伪随机字节数据
- `-base64':规定命令应使用base64编码来表示可打印的伪随机数据。 这一点很重要,因为如前所述,MongoDB密钥文件只能在基数64集中包含字符
- `768':命令应生成的字节数。 在 Base64 编码中,3个二进制字节的数据以4个字符表示. 因为蒙哥 DB 密钥文件最多可有 1024 个字符, 768 是您可以为有效密钥文件生成的最大字节数
这个命令的768
参数是一个比``>更大的符号。这将命令的输出重定向到一个名为
keyfile.txt`的新文件中,该文件将作为你的 keyfile。如果您想要的话,请随时更改文件名,只要它出现在以后的命令中。
接下来,更改 keyfile 的权限,以便只有所有者才能读取访问:
1[environment second]
2chmod 400 keyfile.txt
接下来,将 keyfile 分发到您复制集中的其他两个托管 MongoDB 实例的服务器上,假设您遵循 [如何设置 SSH 密钥] 的前提指南(https://andsky.com/tech/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04),您可以使用 scp
命令:
1[environment second]
2scp keyfile.txt [email protected]:/home/sammy/mongo-security
3scp keyfile.txt [email protected]:/home/sammy/mongo-security
请注意,这些命令中的每一个都将 keyfile 直接复制到您之前在 mongo1 和** mongo2** 上创建的 ~/mongo-security/
目录中。
接下来,将文件的所有者更改为 mongodb 用户配置文件. 这是您安装 MongoDB 时创建的特殊用户,并且用于运行mongod
服务。
在您的每个服务器上运行以下命令以将 keyfile 的所有者更改为 mongodb 用户帐户:
1sudo chown mongodb:mongodb ~/mongo-security/keyfile.txt
在每个服务器上更改 keyfiles 的所有者后,您可以重新配置您的 MongoDB 实例,以执行 keyfile 身份验证。
步骤 3 – 启用 Keyfile 身份验证
现在你已经生成了一个 keyfile 并将其分发到你的复制片集中的每个服务器中,你可以更新每个服务器上的 MongoDB 配置文件,以执行 keyfile 身份验证。
为了避免在配置您的复制件成员需要认证时出现任何故障,这一步骤涉及首先重新配置设定的次要成员. 然后,你将指示你的首要成员退下,成为次要成员. 这将促使次级成员举行选举,以选择一个新的初级,使任何客户或应用程序都能利用你的集群。 然后重新配置前主节点以启用认证.
在您的每个服务器上托管您的复制片组的次要成员 ,使用您偏好的文本编辑器打开MongoDB的配置文件:
1sudo nano /etc/mongod.conf
在文件中,找到安全
部分,默认情况下将看起来像这样:
1[label /etc/mongod.conf]
2. . .
3#security:
4. . .
然后,在下一行中,添加一个‘KeyFile:’指令,然后是你在上一步创建的 keyfile的完整路径:
1[label /etc/mongod.conf]
2. . .
3security:
4 keyFile: /home/sammy/mongo-security/keyfile.txt
5. . .
请注意,这个新行开始时有两个空格,这些空格是必要的,以便正确读取配置文件。当您在自己的配置文件中输入此行时,请确保您提供的路径反映了每个服务器上的 keyfile 的实际路径。
在keyFile
指令下方,添加一个transitionToAuth
指令,值为true
。当设置为true
,此配置选项允许MongoDB实例接受已验证和未验证的连接。
1[label /etc/mongod.conf]
2. . .
3security:
4 keyFile: /home/sammy/mongo-security/keyfile.txt
5 transitionToAuth: true
6. . .
再次,请确保在transitionToAuth
指令之前包含两个空白空间。
完成这些更改后,保存并关闭文件. 如果您使用nano
来编辑它,则可以通过按CTRL + X
,Y
,然后按ENTER
来进行编辑。
然后,在两个辅助实例的服务器上重新启动mongod
服务,立即实施这些更改:
1sudo systemctl restart mongod
通过此,您已为您的复制组的次要成员配置了 keyfile 身份验证,此时,验证和非验证的用户都可以无限制地访问这些成员。
接下来,您将在主成员中重复此过程. 但是,在这样做之前,您必须退出成员,以便不再是主成员. 要做到这一点,请打开主成员托管服务器上的 MongoDB 壳。
1[environment second]
2mongo
从提示中运行rs.stepDown()
方法,这将指示主要成员成为次要成员,并将导致当前的次要成员举行选举,以确定哪些成员将作为新主要成员:
1[environment second]
2rs.stepDown()
如果该方法在输出中返回OK
:1,则意味着主要成员已成功降级成为次要成员:
1[secondary_label Output]
2[environment second]
3{
4 "ok" : 1,
5 "$clusterTime" : {
6 "clusterTime" : Timestamp(1614795467, 1),
7 "signature" : {
8 "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
9 "keyId" : NumberLong(0)
10 }
11 },
12 "operationTime" : Timestamp(1614795467, 1)
13}
放下初级后,您可以关闭蒙古壳:
1[environment second]
2exit
接下来,在这个服务器上打开 MongoDB 配置文件:
1[environment second]
2sudo nano /etc/mongod.conf
查找安全部分并删除安全
标题,然后添加您在其他 MongoDB 实例中添加的相同的keyFile
和transitionToAuth
指令。
1[label /etc/mongod.conf]
2[environment second]
3. . .
4security:
5 keyFile: /home/sammy/mongo-security/keyfile.txt
6 transitionToAuth: true
7. . .
再次,请确保keyFile
指令之后的文件路径反映了 keyfile 在该服务器上的实际位置。
完成后,保存并关闭文件,然后重新启动mongod
过程:
1[environment second]
2sudo systemctl restart mongod
接下来,您的所有 MongoDB 实例都可以接受已验证和未验证的连接. 在本指南的最后一步中,您将配置您的实例以要求用户在执行特权操作之前进行身份验证。
步骤 4 — 重新启动每个会员而无需过渡到ToAuth
来执行身份验证
在此时,您的每个 MongoDB 实例都配置为transitionToAuth
设置为true
,这意味着即使您已启用每个服务器使用您创建的 keyfile 来内部验证连接,它们仍然可以接受未经验证的连接。
要更改这一点,并要求每个成员执行身份验证,请在每个服务器上重新打开 mongod.conf
文件 **:
1sudo nano /etc/mongod.conf
查找安全
部分并禁用transitionToAuth
指令. 您可以通过用英镑标记来评论线条:
1[label /etc/mongod.conf]
2. . .
3security:
4 keyFile: /home/sammy/mongo-security/keyfile.txt
5 #transitionToAuth: true
6. . .
在每个实例的配置文件中禁用transitionToAuth
指令后,保存并关闭每个文件。
然后,在每个服务器上重新启动mongod
服务 **:
1sudo systemctl restart mongod
之后,您复制集中的每个 MongoDB 实例都将要求您进行身份验证以执行特权操作。
要测试这一点,请尝试运行一个 MongoDB 方法,该方法在具有相应权限的经过验证的用户召唤时起作用。
1mongo --eval 'rs.status()'
即使您在步骤 1 中成功运行了此方法,但rs.status()
方法现在只能由已授予clusterAdmin
或clusterManager
角色的用户运行,因为您已启用了 keyfile 身份验证。
1[secondary_label Output]
2. . .
3MongoDB server version: 4.4.4
4{
5 "operationTime" : Timestamp(1616184183, 1),
6 "ok" : 0,
7 "errmsg" : "command replSetGetStatus requires authentication",
8 "code" : 13,
9 "codeName" : "Unauthorized",
10 "$clusterTime" : {
11 "clusterTime" : Timestamp(1616184183, 1),
12 "signature" : {
13 "hash" : BinData(0,"huJUmB/lrrxpx9YfnONM4mayJwo="),
14 "keyId" : NumberLong("6941116945081040899")
15 }
16 }
17}
请记住,在启用访问控制后,所有集群管理方法(包括 rs.
方法,如 rs.status()
)仅在被授予适当集群管理角色的认证用户召唤时才会工作。
1mongo -u "ClusterAdminSammy" -p --authenticationDatabase "admin" --eval 'rs.status()'
在提示时输入用户密码后,您将看到 rs.status()
方法的输出:
1[secondary_label Output]
2. . .
3MongoDB server version: 4.4.4
4{
5 "set" : "shard2",
6 "date" : ISODate("2021-03-19T20:21:45.528Z"),
7 "myState" : 2,
8 "term" : NumberLong(4),
9 "syncSourceHost" : "mongo1.replset.member:27017",
10 "syncSourceId" : 2,
11 "heartbeatIntervalMillis" : NumberLong(2000),
12 "majorityVoteCount" : 2,
13. . .
这确认复制组正在执行身份验证,并且您可以成功身份验证。
结论
通过完成本教程,您创建了与OpenSSL的 keyfile,然后配置了一个MongoDB复制集以要求其成员使用它进行内部身份验证。您还创建了一个用户管理器,允许您在未来管理用户和角色。
如果您想了解更多关于 MongoDB 的信息,我们鼓励您查看我们的 完整的 MongoDB 内容库。