Redis 访问控制列表(ACL),是一项可以实现限制特定客户端连接可执行命令和键访问的功能,它的工作方式是:客户端连接服务器以后,客户端需要提供用户名和密码用于验证身份:如果验证成功,客户端连接会关联特定用户以及用户相应的权限。Redis 可以配置新的客户端连接自动使用 default 用户进行验证(默认选项),因此配置 default 用户权限 会使没有验证的客户端只能使用一小部分功能。Redis 6 版本(第一个支持 ACL 的版本)的默认配置和之前版本完全相同,即每一个新的客户端连接有权限去访问所用命令和键,因此 ACL 功能对旧版客户端和应用是向后兼容(backward compatible)的,并且对旧版配置用户密码的方式,使用 requirepass 配置选项是完全支持的,但是不同的是 requirepass 配置选项只是设定 default 用户的密码。RedisAUTH 命令在 Redis 6 版本进行扩展,现在可以使用两个参数形式:
如果使用旧版本的使用方式:
会使用 default 用户进行验证,所以用这种形式进行验证意味着我们想使用 default 用户进行身份验证,这种方式可以提供完美的向后兼容旧版本 Redis 的支持。
ACL 的使用场景
在使用 ACL 之前你需要问一下自己使用这层访问控制的目的是什么。正常来说 ACL 可以实现下面两个重要目标:
1.你希望限制用户访问命令和键以提高安全性,因此不在信任列表里的用户没有权限访问,而在信任列表里的用户有可以完成工作的最小访问权限。例如一些客户端只可以执行只读的命令。
2.你希望提高运营安全, 当程序出错或人为原因操作失误, 进程或用户不允许访问 Redis,以避免数据或者配置受到损坏。例如一个用于拿取延迟工作的工作客户端不应该有权限去执行 flushall 命令。
另外一个 ACL 的应用场景是关于管理 Redis 实例。Redis 通常被作为内部托管服务提供给公司内部用户或者作为云服务提供商配置的软件服务。在这两种设置方式中我们必须确保配置命令对用户来说是不可见的。过去版本的 redis 是暂时通过命令重命名(command renaming)这种方式实现的,是旧版本没有 ACL 支持的一种临时措施,但是却不是完美的解决方案。
使用 ACL 命令配置 ACL
ACL 是用 DSL(domainspecific language)语言来定义用户是否有权限访问特定资源或操作的。每一条 ACL 规则都是从第一个到最后一个,从左至右来实现的,因为在一些时候,定义规则的顺序会决定用户是否有权限去访问特定资源和操作。
默认配置下只有一个用户被定义(default 用户)。我们可以使用 ACL LIST 命令来检查当前有效的 ACL 规则。使用 ACL LIST 确认刚启动的,使用默认配置的 Redis 实例 ACL 规则如下:
上面的命令遵循和 Redis 配置文件同样的格式返回当前 ACL 配置的规则。每一行最前面的两个词是”user”和用户名。之后的词是具体 ACL 定义的规则。我们下面将会看到怎样使用这些规则,但是现在,可以简单理解为默认(default)用户的配置是开启的(active(on)),不需要密码(require no password(nopass)),可以访问所有键(to access every possiblekey(~*))和可以执行任何命令(call everypossible command(+@all))的。另外,对于默认(default)用户来说,没有配置密码规则意味着新的客户端连接会自动使用默认(default)进行验证而不用显式的执行 AUTH 命令。
ACL 规则
下面是有效的 ACL 规则,特定规则只是用于启用或删除特定 ACL 标志的词,或者用于改变用户当前的 ACL 规则。其他的规则是 char 类型的前缀和命令或命令类别的名字,或者键的模式。
启用和禁止用户
on:启用用户:可以使用这个用户进行验证。
off:禁止用户:不能使用这个用户进行验证,但已经验证的客户端连接仍然可以继续工作,需要注意的是如果默认(default)用户如果被禁止,默认用户配置将不起作用,新的客户端连接将会不被验证并且需要用户显式发送 AUTH 命令或 HELLO 命令进行验证。
允许和禁止命令
+:添加命令到用户允许执行命令列表。
-:从用户允许执行命令列表删除命令。
+@:允许用户执行所有定义在 category 类别中的命令。有效的类别例如 @admin, @set,@sortedset 等等。你可以通过使用 ACL CAT 命令查看所有预定义的类别,另外一个特殊的类别 +@all 意思是所有在当前系统里的命令,以及将来在 Redis 模块中添加的命令。
-@:类似于 +@,但是是从用户可以执行的命令列表中删除特定的命令。
+|subcommand:允许 用户启用一个被禁止命令的子命令,请注意这个命令不允许使用 - 规则,例如 -DEBUG|SEGFAULT,只能使用 + 规则。如果整个命令已经被启用,单独启用子命令没有意义,Redis 会返回错误。
allcommands: +@all 的别名. 注意这个会使用户允许执行所有从 Redis 模块中添加的命令。
nocommands: -@all 的别名。
允许和禁止特定的键
~:添加一个键模式以被用在用户执行命令里面。例如~允许使用所有的键。键的模式是通配符模式,像 KEYS 命令一样。另外使用多个模式也是被允许的,
allkeys:是 ~* 的别名
resetkeys:在键模式列表里面清空所有的键模式,例如 ACL ~foo:* ~bar:resetkeys ~objects:,将会使用户端只有权限访问 objects:* 的键模式。
配置有效的用户名密码
:添加密码到用户有效密码列表里,例如>mypass 将添加 mypass 到用户有效密码列表,这个配置会使 nopass 失效,每个用户都可以配置任何数量的密码。
<:从用户有效密码列表中删除密码,Redis 会返回错误如果这个密码在之前没有被设置的话。
#:添加 SHA-256 形式哈希值到用户有效密码列表里,这种方式允许用户使用哈希值在 acl.conf 中存储密码,以避免明文形式存储。只有 SHA-256 形式哈希值才能被当做密码的哈希值,并且只能是 64 字符长度,并且使用 16 进制编码的小写字符。
!:从有效的密码列表中删除哈希值密码,这种方式在你不知道密码明文的情况下却又想删除密码的时候非常有用。
nopass:删除所有与用户关联的密码,并且用户会被标记不需要密码验证:这意味着任何密码都会通过用户验证。如果在默认(default)用户使用这个配置,所有客户端连接会立即通过默认(default)用户验证。注意 resetpass 配置会清除这个配置。
resetpass:清除用户所有密码,另外会清除用户 nopass 状态。使用 resetpass 后用户没有与其相关联的密码,这种方式下用户如果不添加新密码的话(或在后面设置 nopass)则无法进行验证。
注意:未使用 nopass 进行标记且没有有效密码列表的用户实际上是无法使用的,因为将无法以该用户身份登录。
重设用户
reset:reset 和以下操作等同:resetpass, resetkeys, off, -@all.用户将返回和它被默认创建时同样的状态。
使用 ACL SETUSER 创建和修改用户当前 ACL 配置
用户可以以下面两种方式被创建和修改:
1.使用 ACL 命令和 ACL SETUSER 子命令。
2.修改 Redis 服务器配置,用户可以在那里被定义,然后重启服务器并生效。或者使用外部 ACL 文件,使用 ACL LOAD 来导入 ACL 信息。
在这部分我们会学到怎样使用 ACL 命令来定义用户。如果我们学会用这种方式定义用户转换为用配置文件配置用户就会很简单。使用配置文件定义用户会在下面的章节单独列出。
开始我们使用最简单的方式定义一个 ACL 用户:
SETUSER 命令需要提供用户名以及与用户相关联的 ACL 规则。但是在上面的例子里我们没有提供任何与用户相关联的规则。这样我们只是创建一个用户,如果用户不存在的话,会使用默认 ACL 配置属性创建。如果用户已经存在,这条命令将不起任何作用。让我们来看一下默认用户的状态:
刚刚创建的 alice 用户状态是:
Off 关闭状态:意思是用户被禁用,AUTH 命令将不会起作用。
不能使用任何命令,注意默认创建的用户没有使用任何命令的权限,所以-@all 在上面的输出可以忽略,但是 ACL LIST 会用更显式的方式来告诉用户。
最后用户不能访问任何键模式。
用户没有任何密码设置。
这样的用户是完全没有用的。让我们来定义一个开启的,设置密码的,只能访问 GET 命令并只能访问 cached:键模式的用户。
现在用户可以做一些事情,但如果做其他的事情会被拒绝:
命令像预期想象的一样可以工作,为了检查用户 alice 的配置属性(记住用户名是区分大小写的),可以使用一个对计算机更友好的替代 ACL LIST 命令,ACL LIST 的输出被认为是对人更友好的。
ACL GETUSER 命令返回的是更容易用电脑进行提取的键值对。输出包括用户标志,键模式列表,密码列表等。如果我们使用 RESP3 这种输出会更友好,如果使用 RESP3 方式,返回值如下:
注意:从现在开始我们会继续使用 Redis 默认协议,版本 2,因为转换到版本 3 对于社区和使用者来说还需要一段时间。使用另一个 ACL SETUSER 命令(在另一个用户下执行,因为 Alice 没有权限运行 ACL 命令)我们可以为用户增加更多的模式:
现在用户的表述和和我们想象的完全一样。
多次调用 ACL SETUSER 命令时的情况
在这里需要特别理解如果多次调用 ACL SETUSER 命令时的情况。在多次调 ACL SETUSER 时,SETUSER 命令不会重新设定用户,而是在之前设定的基础上应用新的 ACL 规则。用户只会在之前不存在的时候被重新设定:在这种情况下,一个新的用户会被创建,没有关联任何 ACL 规则,换句话说,用户不能做任何事情,是被禁用的,并且没有密码相关联:从安全的角度上来说这种默认方式是最好的。但是后面的 ACL SETUSER 调用只会在之前的基础上增加 ACL 规则,例如下面的例子
使用命令类别
设置用户 ACL 规则用逐步添加命令的方式是件很懊恼的事情,所以我们会用下面一种方式:
通过使用+ @ all 和-@dangerous,我们包含了所有命令,然后在 Redis 命令表中删除了所有标记为危险的命令。请注意,除了+ @ all 外,命令类别从不包含模块命令。如果使用+ @ all,则所有命令都可以由用户执行,甚至未来通过 Redis 模块加载的命令也可以执行。但是,如果使用 ACL 规则+@ readonly 或其他任何规则,则始终会排除 Redis 模块命令。这一点非常重要,因为我们只应该信任 redis 原生的命令。Redis 模块有些时候会引起程序执行的风险,并且在 ACL 仅是+的情况下,即+ @ all -…的形式,我们应该绝对确保可执行命令中不会有危险的命令。
但是我们要记住要命令类别,并且命令类别中确切包含的命令是不可能的。因此 Redis ACL 命令导出 CAT 子命令,该命令可以两种形式使用:
例子:
注意命令可能同时属于不同的类别,所以 ACL 规则例如+@geo -@readonly 会导致特定的 geo 命令被排除,因为它们也属于 readonly 类别。
增加子命令
通常来说提供将命令整体加入 ACL 可执行命令列表或者从列表中删除是不够的,很多 Redis 命令通过子命令做很多不同的事。例如 CLIENT 命令可以被用于执行危险或不危险的操作。很多项目部署不会将 CLIENT KILL 交给非管理员用户执行,但是却允许它们使用 CLIENTSETNAME 来设置连接用户属性。注意:新的 RESP3 协议的 HELLO 命令将来会提供一个 SETNAME 的选项,但是这个还是一个好的例子。在这种情况下我们可以用下面的方式改变 ACL 规则:
ACL SETUSER myuser-client +client|setname +client|getname
我们先删除 CLIENT 命令,然后再加上两个可以执行的子命令。注意我们不能改变这个顺序,子命令只可以添加而不能删除,这样设计的原因是也许将来会有更多的子命令被定义,显式的指定哪些子命令用户可以被执行会安全得多。另外,如果添加子命令的父命令不是被禁止的,会产生错误,因为在这种方式下只可能是用户定义的 ACL 规则有错误。
值得注意的是子命令匹配会带来一些服务器性能效率上的问题,但是这类的问题即使使用合成性能测试基准也非常难测量,当这类命令执行的时候只有额外 cpu 被消耗,其他命令则不会被消耗。
+@all 和 -@all 对比
在上一章节我们看到怎样通过添加删除单独命令来定义 acl 命令规则。
密码在内部是怎样存储的
Redis 内部通过 SHA256 哈希算法存储密码,如果你定义一个密码并且查看 ACL LIST 或 GETUSER 的命令输出,你会看到一个看起来像伪随机的 16 进制字符串。下面有个例子,因为在之前的例子为了简单起见 16 进制字符串只截取了前一部分。
并且旧命令 CONFIG GET requirepass 从 redis 6 版本开始不会返回明文密码,而是会返回加密的密码。适用 SHA256 算法可以避免存储明文密码,与此同时,也支持非常快的执行 AUTH 命令,这也是 Redis 重要的特点也是客户希望从 Redis 得到的。
但是 ACL 密码不是真正的密码,而是客户端和服务器端的共享秘钥。因为这个原因密码不是一个客户使用的验证令牌(authentication token)。例如:密码没有长度限制,密码只是存储在客户端的一些软件里,人们不会需要从这里获取密码。ACL 密码不会用于保护其他任何东西,例如永远不会作为一些电子邮箱的密码。经常的如果你可以访问密码的哈希值,并拥有访问特定服务器权限,或者破坏系统本身,你已经可以访问密码所保护的实体,Redis 实例的稳定和它所存储的数据。因为这个原因去减慢密码验证速度而去使用耗时和耗空间的算法去追求密码的安全性,是一个非常不明智的选择。我们建议的方式是生成一个难以破解的密码,因此即使使用哈希算法也没有人可以使用字典或暴力破解去破解密码。因为这个原因,一个特别的 ACL 命令使用系统的伪加密生成算法去生成密码。
这个命令会输出 16 字节(128 位)随机字符串转换为 32 字节字母数字字符串。这个长度密码完全可以避免被攻击并且足够短被管理,复制粘贴,存储等等。这个是你生成 Redis 密码应该被用到的。
使用外部 ACL 文件
通过 Redis 配置方式存储 ACL 用户有以下两种方式:
用户可以直接在 Redis.conf 文件中被指定。
可以通过外部 ACL 文件指定。这两种方式是互相不兼容的,Redis 会让你选择其中的一种方式。在 redis.conf 中配置用户是非常简单的,适用于简易的应用场景。如果需要有很多用户去设定,在复杂的环境中,我们强烈建议使用 ACL 文件。Redis.conf 和外部 ACL 文件是完全一样的。所以从一个转换到另一个是非常简单的。下面的例子:
例如:
当你希望使用外部 ACL 文件,你需要指定配置选项 aclfile,像下面这样:
当你直接在 redis.conf 中直接指定一些用户的时候,你可以使用 CONFIG REWRITE 来覆盖存储文件中新的用户配置。外部 ACL 文件具有更加强大的功能,你可以像下面这样:
使用 ACL LOAD 如果你手动的改变 ACL 文件并且希望 Redis 重新读取新的配置,注意命令能够读取文件的前提是所有用户在文件中都被正确设置。不然的话会返回错误,并且旧的配置会继续生效。适用 ACL SAVE 来存储当前 ACL 配置到 ACL 文件中。注意 CONFIG REWRITE 不会自动调用 ACL SAVE:当你使用 ACL 文件的时候配置和 ACL 是被分开处理的。
本文转载自 中间件小哥 公众号。
原文链接:https://mp.weixin.qq.com/s/mZok4XRg7e9YG-C8zb3dUQ
评论