使用访问控制列表 (ACL)
KeyDB ACL,即访问控制列表(Access Control List)的缩写,是允许限制某些连接可执行的命令和可访问的键的功能。其工作方式是,客户端连接后需要提供用户名和有效密码进行身份验证:如果身份验证成功,该连接会与指定用户关联,并受该用户的限制。KeyDB 可以配置为新连接默认已通过一个“default”用户进行身份验证(这是默认配置),因此,配置默认用户的一个副作用是,可以为未显式进行身份验证的连接提供一个特定的功能子集。
在默认配置下,KeyDB 6(首个包含 ACL 的版本)的工作方式与旧版 KeyDB 完全相同,即每个新连接都能调用所有可能的命令并访问所有键,因此 ACL 功能与旧客户端和应用程序向后兼容。此外,旧的密码配置方式,即使用 requirepass 配置指令,仍然按预期工作,但现在它的作用只是为默认用户设置密码。
KeyDB 的 AUTH
命令在 KeyDB 6 中得到了扩展,现在可以使用双参数形式:
当按照旧形式使用时,即:
发生的情况是,用于身份验证的用户名是“default”,因此只指定密码意味着我们希望针对默认用户进行身份验证。这提供了与过去完美的向后兼容性。
#
ACL 何时有用在使用 ACL 之前,您可能需要问自己,实现这一层保护的目标是什么。通常,ACL 可以很好地服务于两个主要目标:
- 您希望通过限制对命令和键的访问来提高安全性,以便不受信任的客户端无法访问,而受信任的客户端仅拥有执行所需工作所需的最低数据库访问级别。例如,某些客户端可能只能执行只读命令。
- 您希望提高操作安全性,以便访问 KeyDB 的进程或人员不会因为软件错误或手动失误而破坏数据或配置。例如,一个从 KeyDB 中获取延迟作业的工作进程没有理由能够调用
FLUSHALL
命令。
ACL 的另一个典型用途与托管的 KeyDB 实例有关。KeyDB 通常作为托管服务提供,既可以由公司内部团队为其他内部客户处理 KeyDB 基础设施,也可以由云提供商以软件即服务(SaaS)的形式提供。在这两种设置中,我们都希望确保客户无法使用配置命令。过去通过命令重命名来实现这一点,这种技巧使我们能够在没有 ACL 的情况下长期生存,但并不理想。
#
使用 ACL 命令配置 ACLACL 是使用一种 DSL(领域特定语言)定义的,该语言描述了给定用户能做什么或不能做什么。这些规则总是从头到尾、从左到右地实现,因为有时规则的顺序对于理解用户真正能做什么很重要。
默认情况下,定义了一个名为 *default* 的用户。我们可以使用 ACL LIST
命令来检查当前活动的 ACL,并验证一个刚刚启动、使用默认配置的 KeyDB 实例的配置是怎样的:
上述命令以与 KeyDB 配置文件中使用的相同格式报告用户列表,它将当前为用户设置的 ACL 转换回其描述。
每行的前两个词是“user”和用户名。接下来的词是描述不同内容的 ACL 规则。我们将详细展示规则如何工作,但目前只需知道,默认用户被配置为活动状态(on)、不需要密码(nopass)、可以访问所有可能的键(~*
)和发布/订阅通道(&*
),并且能够调用所有可能的命令(+@all
)。
此外,在默认用户的特殊情况下,拥有 *nopass* 规则意味着新连接会自动以默认用户身份进行验证,无需任何显式的 AUTH
调用。
#
ACL 规则以下是有效的 ACL 规则列表。某些规则只是单个单词,用于激活或移除标志,或对用户 ACL 执行特定更改。其他规则是字符前缀,与命令或类别名称、键模式等连接在一起。
启用和禁用用户
on
:启用用户:可以作为此用户进行身份验证。off
:禁用用户:不能再使用此用户进行身份验证,但已验证的连接仍然有效。请注意,如果默认用户被标记为 *off*,新连接将以未验证状态启动,并且需要用户发送AUTH
或带有 AUTH 选项的HELLO
以某种方式进行身份验证,无论默认用户的配置如何。
允许和禁止命令
+<command>
:将命令添加到用户可以调用的命令列表中。-<command>
:将命令从用户可以调用的命令列表中移除。+@<category>
:添加该类别中的所有命令,供用户调用。有效类别如 @admin、@set、@sortedset 等,完整列表请调用ACL CAT
命令查看。特殊类别 @all 表示所有命令,包括服务器中当前存在的命令和将来通过模块加载的命令。-@<category>
:与+@<category>
类似,但将命令从客户端可以调用的命令列表中移除。+<command>|subcommand
:允许一个原本被禁用的命令的特定子命令。注意,此形式不允许为否定形式,如-DEBUG|SEGFAULT
,只能是“+”开头的加法形式。如果该命令作为一个整体已经激活,此 ACL 将导致错误。allcommands
:+@all 的别名。注意,它意味着能够执行通过模块系统加载的所有未来命令。nocommands
:-@all 的别名。
允许和禁止某些键
~<pattern>
:添加一个键模式,这些键可以在命令中被提及。例如,~*
允许所有键。该模式是类似KEYS
的 glob 风格模式。可以指定多个模式。allkeys
:~*
的别名。resetkeys
:清空允许的键模式列表。例如,ACL~foo:* ~bar:* resetkeys ~objects:*
将导致客户端只能访问匹配objects:*
模式的键。
允许和禁止发布/订阅通道
&<pattern>
:添加一个 glob 风格的 Pub/Sub 通道模式,用户可以访问。可以指定多个通道模式。请注意,模式匹配仅适用于PUBLISH
和SUBSCRIBE
提到的通道,而PSUBSCRIBE
要求其通道模式与用户允许的通道模式进行字面匹配。allchannels
:&*
的别名,允许用户访问所有 Pub/Sub 通道。resetchannels
:清空允许的通道模式列表,如果用户的 Pub/Sub 客户端不再能访问其各自的通道和/或通道模式,则断开其连接。
为用户配置有效密码
><password>
:将此密码添加到用户的有效密码列表中。例如,>mypass
会将“mypass”添加到有效密码列表中。此指令会清除 *nopass* 标志(见后文)。每个用户可以有任意数量的密码。<<password>
:从此用户的有效密码列表中移除此密码。如果您尝试移除的密码实际上不存在,则会产生错误。#<hash>
:将此 SHA-256 哈希值添加到用户的有效密码列表中。此哈希值将与为 ACL 用户输入的密码的哈希进行比较。这允许用户在acl.conf
文件中存储哈希值而不是明文密码。只接受 SHA-256 哈希值,因为密码哈希必须是 64 个字符,并且只包含小写十六进制字符。!<hash>
:从此用户的有效密码列表中移除此哈希值。当您不知道哈希值指定的密码但想从用户中移除该密码时,这很有用。nopass
:移除用户的所有已设密码,并将用户标记为不需要密码:这意味着任何密码都对此用户有效。如果此指令用于默认用户,则每个新连接将立即以默认用户身份进行验证,无需任何显式的 AUTH 命令。请注意,*resetpass* 指令将清除此条件。resetpass
:清空允许的密码列表。此外,还会移除 *nopass* 状态。在 *resetpass* 之后,用户没有任何关联的密码,也无法进行身份验证,除非稍后添加一些密码(或将其设置为 *nopass*)。
注意:一个未标记为 nopass 且没有有效密码列表的用户,实际上是无法使用的,因为没有办法以该用户身份登录。
重置用户
reset
执行以下操作:resetpass, resetkeys, resetchannels, off, -@all。用户返回到其创建后的初始状态。
#
使用 ACL SETUSER 命令创建和编辑用户 ACL创建和修改用户主要有两种方式:
- 使用 ACL 命令及其
ACL SETUSER
子命令。 - 修改服务器配置,可以在其中定义用户,然后重启服务器,或者如果我们正在使用*外部 ACL 文件*,只需执行
ACL LOAD
。
在本节中,我们将学习如何使用 ACL
命令定义用户。有了这些知识,通过配置文件做同样的事情将变得非常简单。在配置中定义用户值得单独一节讨论,将在后面单独讨论。
首先,让我们尝试最简单的 ACL SETUSER
命令调用:
SETUSER
命令接受用户名和一系列要应用于该用户的 ACL 规则。然而,在上面的例子中,我没有指定任何规则。如果用户不存在,这只会使用新用户的默认设置创建用户。如果用户已经存在,上面的命令将不会做任何事情。
让我们检查一下默认用户状态:
刚刚创建的用户 "alice" 是:
- 处于 off 状态,即已禁用。AUTH 将不起作用。
- 该用户也没有设置密码。
- 无法访问任何命令。请注意,用户默认创建时无法访问任何命令,因此上面输出中的
-@all
可以省略,但ACL LIST
尝试做到显式而非隐式。 - 用户没有可以访问的键模式。
- 用户可以访问所有发布/订阅频道。
默认情况下,新用户创建时具有限制性权限。从 KeyDB 6.2 开始,ACL 也提供了发布/订阅通道访问管理。为确保从 6.0 版本升级到 KeyDB 6.2 时的向后兼容性,新用户默认被授予 'allchannels' 权限。可以通过 `acl-pubsub-default` 配置指令将默认值设置为 `resetchannels`。
这样的用户是完全无用的。让我们尝试定义用户,使其处于活动状态,有密码,并且只能使用 GET
命令访问以字符串“cached:”开头的键名。
现在这个用户可以做一些事情,但会拒绝做其他事情:
事情按预期进行。为了检查用户 alice(请记住用户名区分大小写)的配置,可以使用 ACL LIST
的替代方法,该方法更适合计算机读取,而 ACL LIST
更偏向于人类阅读。
ACL GETUSER
返回一个字段-值数组,以更易于解析的术语描述用户。输出包括标志集、键模式列表、密码等。如果我们使用 RESP3,输出可能会更易读,因为它会以映射回复的形式返回:
注意:从现在起,我们将继续使用 KeyDB 的默认协议版本 2,因为社区切换到新协议需要一些时间。
使用另一个 ACL SETUSER
命令(来自另一个用户,因为 alice 无法运行 ACL
命令),我们可以为用户添加多个模式:
内存中的用户表示现在如我们所期望的那样。
#
多次调用 ACL SETUSER 会发生什么理解多次调用 ACL SETUSER 时会发生什么非常重要。关键在于,每次调用 SETUSER
都不会重置用户,而只是将 ACL 规则应用于现有用户。只有在用户之前不存在时,它才会被重置:在这种情况下,会创建一个具有零 ACL 的全新用户,也就是说,该用户什么也做不了,被禁用,没有密码等等:为了安全起见,这是最好的默认设置。
然而,后续的调用只会增量地修改用户,例如,以下序列:
将导致 myuser 能够同时调用 GET
和 SET
#
玩转命令类别逐个指定命令来设置用户 ACL 真的很烦人,所以我们通常这样做:
通过说 +@all 和 -@dangerous,我们包含了所有命令,然后移除了在 KeyDB 命令表中被标记为危险的所有命令。请注意,命令类别**永远不包括模块命令**,除了 +@all。如果您说 +@all,用户可以执行所有命令,甚至包括未来通过模块系统加载的命令。但是,如果您使用 ACL 规则 +@read 或任何其他规则,模块命令总是被排除的。这一点非常重要,因为您应该只信任 KeyDB 内部命令表的健全性。模块可能会暴露危险的东西,在 ACL 只是加法的情况下,即形式为 +@all -...
,您应该绝对确定您不会包含您不打算包含的东西。
以下是命令类别及其含义的列表:
- admin - 管理命令。普通应用程序永远不需要使用这些命令。包括
REPLICAOF
、CONFIG
、DEBUG
、SAVE
、MONITOR
、ACL
、SHUTDOWN
等。 - bitmap - 数据类型:与位图相关。
- blocking - 可能阻塞连接,直到被另一个命令释放。
- connection - 影响连接或其他连接的命令。这包括
AUTH
、SELECT
、COMMAND
、CLIENT
、ECHO
、PING
等。 - dangerous - 潜在危险的命令(出于各种原因,每个都应谨慎考虑)。这包括
FLUSHALL
、MIGRATE
、RESTORE
、SORT
、KEYS
、CLIENT
、DEBUG
、INFO
、CONFIG
、SAVE
、REPLICAOF
等。 - geo - 数据类型:与地理空间索引相关。
- hash - 数据类型:与哈希相关。
- hyperloglog - 数据类型:与 hyperloglog 相关。
- fast - 快速的 O(1) 命令。可能会根据参数数量循环,但不会根据键中元素的数量循环。
- keyspace - 以与类型无关的方式从键、数据库或其元数据中写入或读取。包括
DEL
、RESTORE
、DUMP
、RENAME
、EXISTS
、DBSIZE
、KEYS
、EXPIRE
、TTL
、FLUSHALL
等。可能修改键空间、键或元数据的命令也会有write
类别。只读取键空间、键或元数据的命令将有read
类别。 - list - 数据类型:与列表相关。
- pubsub - 与 PubSub 相关的命令。
- read - 从键(值或元数据)中读取。请注意,不与键交互的命令既不会有
read
也不会有write
。 - scripting - 与脚本相关。
- set - 数据类型:与集合相关。
- sortedset - 数据类型:与有序集合相关。
- slow - 所有不是
fast
的命令。 - stream - 数据类型:与流相关。
- string - 数据类型:与字符串相关。
- transaction - 与
WATCH
/MULTI
/EXEC
相关的命令。 - write - 写入键(值或元数据)。
KeyDB 还可以使用 KeyDB ACL
命令的 CAT
子命令向您显示所有类别的列表,以及每个类别包含的确切命令。该子命令有两种使用形式:
示例:
如您所见,目前有 21 个不同的类别。现在让我们检查一下 *geo* 类别中有哪些命令:
请注意,命令可能属于多个类别,因此像 +@geo -@read
这样的 ACL 规则将导致某些 geo 命令被排除,因为它们是只读命令。
#
添加子命令通常,仅能整体排除或包含一个命令是不够的。许多 KeyDB 命令会根据作为参数传递的子命令执行不同的操作。例如,CLIENT
命令可用于执行危险和非危险的操作。许多部署可能不乐意向非管理员级别的用户提供执行 CLIENT KILL
的能力,但可能仍希望他们能够运行 CLIENT SETNAME
。
注意:新的 RESP3 HELLO
握手命令提供了一个 SETNAME
选项,但这仍然是子命令控制的一个好例子。
在这种情况下,我可以按以下方式更改用户的 ACL:
我首先移除了 CLIENT
命令,然后添加了两个允许的子命令。请注意,**反向操作是不可能的**,子命令只能添加,不能排除,因为未来可能会添加新的子命令:为某个用户指定所有有效的子命令要安全得多。此外,如果您为一个尚未被禁用的命令添加子命令,将会产生错误,因为这只能是 ACL 规则中的一个错误:
请注意,子命令匹配可能会增加一些性能开销,但即使使用综合基准测试也很难衡量这种开销,而且额外的 CPU 成本仅在调用该命令时支付,而不是在调用其他命令时支付。
#
+@all VS -@all在上一节中,我们观察了如何基于添加/移除单个命令来定义命令 ACL。
#
密码在内部是如何存储的KeyDB 内部使用 SHA256 对密码进行哈希存储,如果您设置了密码并查看 ACL LIST
或 GETUSER
的输出,您会看到一个看起来像是伪随机的长十六进制字符串。这里有一个例子,因为在前面的例子中,为了简洁起见,长十六进制字符串被截断了:
此外,从 KeyDB 6 开始,旧命令 CONFIG GET requirepass
将不再返回明文密码,而是返回哈希后的密码。
使用 SHA256 可以在不存储明文密码的情况下,仍然允许非常快速的 AUTH
命令,这是 KeyDB 的一个非常重要的特性,并且与客户端对 KeyDB 的期望一致。
然而,ACL *密码* 并不是真正的密码:它们是服务器和客户端之间的共享秘密,因为在这种情况下,密码不是由人类使用的身份验证令牌。例如:
- 没有长度限制,密码只会被记在某个客户端软件中,在这种情况下没有人需要记住密码。
- ACL 密码不保护任何其他东西:例如,它永远不会是某个电子邮件帐户的密码。
- 通常,当您能够访问哈希密码本身时,通过对给定服务器的 KeyDB 命令拥有完全访问权限,或破坏系统本身,您已经可以访问该密码所保护的内容:KeyDB 实例的稳定性和其包含的数据。
因此,为了使用一种耗费时间和空间的算法来减慢密码验证速度,以使密码破解变得困难,是一个非常糟糕的选择。我们建议的是生成非常强的密码,这样即使拥有哈希值,也没有人能够通过字典或暴力攻击来破解它。为此,有一个特殊的 ACL 命令,使用系统的密码学伪随机生成器来生成密码:
该命令输出一个32字节(256位)的伪随机字符串,转换为一个64字节的字母数字字符串。这足够长以避免攻击,也足够短以便于管理、复制粘贴、存储等。这应该是您用来生成 KeyDB 密码的方法。
#
使用外部 ACL 文件在 KeyDB 配置中存储用户有两种方式。
- 用户可以直接在
keydb.conf
文件中指定。 - 可以指定一个外部 ACL 文件。
这两种方法是*互不兼容*的,KeyDB 会要求您使用其中一种。在 `keydb.conf` 中指定用户是一种非常简单的方法,适用于简单的使用场景。当需要定义多个用户,且环境复杂时,我们强烈建议您使用 ACL 文件。
在 keydb.conf
和外部 ACL 文件中使用的格式完全相同,因此从一种切换到另一种非常简单,格式如下:
例如:
当您想使用外部 ACL 文件时,需要指定名为 aclfile
的配置指令,如下所示:
当您只是直接在 `keydb.conf` 文件中指定几个用户时,您可以使用 `CONFIG REWRITE` 通过重写文件来将新的用户配置存储到文件中。
然而,外部 ACL 文件功能更强大。您可以执行以下操作:
- 如果您手动修改了 ACL 文件并希望 KeyDB 重新加载新配置,请使用
ACL LOAD
。请注意,此命令*仅在所有用户都正确指定的情况下*才能加载文件,否则会向用户报告错误,并且旧配置将保持有效。 - 使用
ACL SAVE
将当前 ACL 配置保存到 ACL 文件。
请注意,CONFIG REWRITE
不会触发 ACL SAVE
:当您使用 ACL 文件时,配置和 ACL 是分开处理的。
#
Sentinel 和副本的 ACL 规则如果您不想为 KeyDB 副本和 KeyDB Sentinel 实例提供对 KeyDB 实例的完全访问权限,以下是必须允许的命令集,以确保一切正常工作。
对于 Sentinel,允许用户在主实例和副本实例中访问以下命令:
- AUTH, CLIENT, SUBSCRIBE, SCRIPT, PUBLISH, PING, INFO, MULTI, SLAVEOF, CONFIG, CLIENT, EXEC。
Sentinel 不需要访问数据库中的任何键,但会使用 Pub/Sub,因此 ACL 规则如下(注意:AUTH 不需要,因为它总是被允许的):
KeyDB 副本需要在主实例上将以下命令列入白名单:
- PSYNC, REPLCONF, PING
不需要访问任何键,所以这转化为以下规则:
请注意,您不需要配置副本以允许主节点能够执行任何命令集:从副本的角度来看,主节点总是以 root 用户身份进行身份验证。