TrumanWong

Redis ACL权限控制

TrumanWong
5/17/2024

Redis6.0之前版本只提供了一个轻量级的访问控制功能,服务端可通过requirepass命令设置密码,客户端只需通过auth命令输入密码,认证成功后,就拥有全量命令执行权限。

这种方式十分不安全,如:

  • 场景一:一个普通用户,执行KEYS命令,引起Redis的短时间阻塞,严重的甚至可能触发主从切换。
  • 场景二:一个普通用户,执行FLUSHALL命令,导致Redis数据库被清空。

针对场景1和场景2,一般可以通过rename-command命令重命名或禁用这些危险函数。但是还有另外一个问题,即多个线上业务通常共用一个Redis实例,可能会,当然也存在数据泄露风险。用户只通过rename-command命令是防止不了自己的key被其他用户访问的。

为了解决上述问题,Redis 6.0开始引入了ACL(Access Control Lists)权限控制机制,可以方便地。基于ACL权限控制,Redis 6.0可以设置多个用户,允许你以细粒度的方式管理用户对Redis的访问权限。当然,ACL是向上兼容的,默认在不做任何配置的情况下,所有客户端都拥有全量命令执行权限,且继续支持以requirepass配置密码的方式进行鉴权。

ACL概述

ACL可以对不同的用户设置命令权限或数据权限,这样就能避免有些用户因误操作而导致数据丢失或降低数据泄露的风险。ACL相关的命令配置分为3部分:ACLS规则(语法)、用户关联命令配置、用户关联数据配置。

配置ACL

ACL是使用DSL(Domain Specific Language,领域专用语言)来定义的。ACL的配置方式有两种:

  • redis.conf文件中通过user配置项指定ACL配置。注意:Redis为保持兼容性,会默认创建一个default用户。示例如下:

    user trumanwong on nopass ~* +@all
    

    在上述配置中:

    • user为关键词,ACL配置必须以user关键字开始。
    • trumanwong为用户设置的用户名,其后面的内容为ACL规则描述。

    查看生效规则,可使用acl list。可以看到,ACL规则配置有两条,其中default用户是默认的用户,是为了向上兼容老版本的Redis

    127.0.0.1:6379> acl list
    1) "user default on nopass sanitize-payload ~* &* +@all"
    2) "user trumanwong on nopass sanitize-payload ~* resetchannels +@all"
    
  • redis.conf文件中通过aclfile配置项把ACL相关配置生成.acl文件引入加载的配置文件。在配置文件中,aclfile默认是不开启的。通过aclfile引入配置项的示例如下:

    $ cat redis.conf
    ...
    aclfile /usr/local/redis/users.acl
    ...
    
    $ cat users.acl
    user default on nopass sanitize-payload ~* &* +@all
    user test on sanitize-payload #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* resetchannels +@all -@dangerous
    

注意:这两种方法互不兼容,因此Redis要求使用其中一种。官方更推荐使用aclfile模式,因为redis.conf中配置了权限之后需要重启Redis服务,才能将配置的权限加载至Redis服务;使用aclfile模式,可以调用acl load命令将aclfile中配置的ACL权限热加载进环境中

启用和禁用用户

参数说明如下:

  • on:启用用户,可以以该用户身份进行认证。
  • off:将用户设置为非活动状态,将无法以此用户身份登录。请注意,如果用户在连接已通过身份验证后被禁用(设置为off),则连接将继续按预期工作。要同时终止旧连接,可以CLIENT KILL [USER username]命令断开连接,或者使用 ACL DELUSER命令删除用户,这将导致所有经过身份验证的已删除用户的连接都被断开。

为用户配置有效密码

nopass表示无须密码访问,即客户端不需要用auth认证即可使用Redis,如果需要配置密码,就需要解除nopass状态,详解如下:

  • ><password>:将此密码添加到用户的有效密码列表中。例如,>mypassmypass添加到有效密码列表中。该命令会清除用户的nopass标记。每个用户可以有任意数量的有效密码。

  • <<password>:从有效密码列表中删除此密码。若该用户的有效密码列表中没有此密码,则返回错误信息。

  • #<hash>:将SHA-256 Hash值添加到用户的有效密码列表中。将该Hash值与为用户输入的密码的Hash值进行比较。该命令允许用户将Hash值存储在users.acl文件中,而不是存储明文密码;仅接受SHA-256 Hash值,因为密码Hash值必须为64个字符且为小写的十六进制字符。

  • !<hash>:从有效密码列表中删除该Hash值。当不知道Hash值对应的明文是什么时,此命令很有用。

  • nopass:移除该用户已设置的所有密码,并将该用户标记为nopass(无密码)状态,即使用任何密码都可以登录。resetpass命令可以解除nopass状态。

  • resetpass:刷新允许的密码列表并删除*nopass状态。在resetpass后,用户没有关联的密码,并且如果不添加一些密码(或稍后将其设置为nopass* ) ,则无法进行身份验证。

重置用户

执行以下操作:resetpassresetkeysresetchannelsallchannels(如果设置了 acl-pubsub-default)、offclearselectors-@all。即可重置用户状态为初始状态。

允许和禁止用户调用命令(用户关联命令配置)

其中,@all为禁止或允许所有命令。其中@符号不是所有命令都有。+@all表示允许用户使用所有命令,-@all表示禁止这个用户使用所有命令。具体命令如下:

  • +<command>:将命令添加到用户可以调用的命令列表中。
  • -<command>:将命令从用户可以调用的命令列表中移除。
  • +@<category>:允许用户调用<category>类别中的所有命令,有效类别为@admin@set@sortedset等,通过调用命令ACL CAT查看完整列表。特殊类别@all表示所有命令,包括当前和未来版本中存在的所有命令。
  • -@<category>:禁止用户调用<category>类别中的所有命令。
  • +<command>|first-arg:允许禁用命令的特定第一个参数。它仅在没有子命令的命令上受支持,并且不允许像 -SELECT|1 这样的负数形式,只能以+开头的加法。此功能已被弃用,并且将来可能会被删除。
  • allcommands+@all的别名,包括当前存在的命令及将来通过模块加载的所有命令。
  • nocommands-@all的别名,禁止用户调用所有命令。

允许或禁止用户访问某些键

  • ~<pattern>:添加可以在命令中提及的键模式,支持正则匹配模式,比如~*允许所有键,~foo*~bar*表示允许这个用户访问以foo以及bar开头的键。
  • %R~<pattern>:(Redis 7.0及以上版本可用)添加指定的读取key模式。其行为类似于常规密钥模式,但仅授予读取与给定模式匹配的密钥的权限。有关详细信息,请参阅关键权限。
  • %W~<pattern>:(Redis 7.0及以上版本可用)添加指定的写入键模式。其行为类似于常规密钥模式,但仅授予写入与给定模式匹配的密钥的权限。有关详细信息,请参阅关键权限。
  • %RW~<pattern>:(在 Redis 7.0 及更高版本中可用) 的别名~<pattern>
  • allkeys: 的别名~*
  • resetkeys:清空之前允许的所有键模式。比如resetkeys ~objects*,客户端只能访问以objects开头的键,而之前配置的所有键模式都将被resetkeys命令清空。

了解这些后,我们基本就能配置出一个较完整的ACL规则。

接下来看一下客户端操作ACL配置的常用命令:

  • ACL CAT [category]:查看类别,如果不带参数调用,该命令将显示可用的 ACL 类别。如果给出类别名称,该命令将显示指定类别中的所有 Redis 命令。

    127.0.0.1:6379> acl cat
     1) "keyspace"
     2) "read"
     3) "write"
     4) "set"
     5) "sortedset"
     6) "list"
     7) "hash"
     8) "string"
     9) "bitmap"
    10) "hyperloglog"
    11) "geo"
    12) "stream"
    13) "pubsub"
    14) "admin"
    15) "fast"
    16) "slow"
    17) "blocking"
    18) "dangerous"
    19) "connection"
    20) "transaction"
    21) "scripting"
    
  • ACL DELUSER username [username ...]:删除所有指定的ACL用户,并终止所有通过该用户认证的连接。注意:特殊default用户无法从系统中删除,这是每个新连接进行身份验证的默认用户。用户列表中可能包含不存在的用户名,此时不对不存在的用户执行任何操作

    127.0.0.1:6379> ACL DELUSER trumanwong
    (integer) 1
    
  • ACL GENPASS:创建密码

    127.0.0.1:6379> ACL GENPASS
    "46faf0cf16a5f032dde1dbb200517fb6346798440f98d97f6fb55c757e49f411"
    
  • GETUSER:该命令返回指定用户定义的所有规则。

    127.0.0.1:6379> ACL GETUSER trumanwong
     1) "flags"
     2) 1) "on"
        2) "sanitize-payload"
     3) "passwords"
     4) 1) "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"
     5) "commands"
     6) "+@all"
     7) "keys"
     8) "~*"
     9) "channels"
    10) ""
    11) "selectors"
    12) (empty array)
    
  • HELP:查看帮助

  • ACL LIST:显示Redis当前活动的 ACL 规则。返回的数组中每一行都定义了一个不同的用户,并且格式与 redis.con 文件或外部 ACL 文件中使用的格式相同,因此可以直接剪切并粘贴 ACL LIST 命令返回的内容到外部ACL文件中。

    1) "user default on nopass sanitize-payload ~* &* +@all"
    2) "user test on sanitize-payload #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* resetchannels +@all -@dangerous"
    3) "user trumanwong on sanitize-payload #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* resetchannels +@all"
    
  • ACL LOAD:加载aclfile,在aclfile中修改活新增ACL权限之后,ACL权限不会立即生效。用户可以在Redis命令行中执行acl load命令,将该aclfile中的权限加载至Redis服务中。

  • ACL SAVE:该命令会将当前定义的ACLRedis内存保存到ACL文件中。。

  • ACL SETUSER username [rule [rule ...]]:设置用户访问规则,每次设置之后一定要使用ACL SAVE命令将ACL权限持久化到aclfile中,避免丢失。使用方式如下:

    • ACL SETUSER <username>:用户不存在,则按默认规则创建用户,如果用户存在,则不执行任何操作。
    • ACL SETUSER <username> <rules>:用户不存在,则按默认规则创建用户,并增加<rules>;用户存在,则在原有规则上增加<rules>
  • USERS:显示 Redis ACL 系统中当前配置的所有用户名列表。

    127.0.0.1:6379> ACL USERS
    1) "default"
    2) "test"
    3) "trumanwong"
    
  • ACL WHOAMI:返回当前连接进行身份验证所用的用户名。

    127.0.0.1:6379> ACL WHOAMI
    "default"
    
  • ACL LOG [count | RESET]:该命令显示最近 ACL 安全事件的列表。可选参数count指定要显示的日志数。默认情况下返回最近十条失败事件。指定RESET参数会清除日志。

允许和禁止 Pub/Sub 频道:

  • &<pattern>:(在Redis 6.2 及更高版本中可用)添加用户可以访问的 Pub/Sub 通道的 glob 样式模式。可以指定多个通道模式。请注意,模式匹配仅针对PUBLISH和提到的通道进行SUBSCRIBE,而PSUBSCRIBE需要其通道模式与用户允许的通道模式之间进行字面匹配。

  • allchannels&*允许用户访问所有 Pub/Sub 频道的别名。

  • resetchannels:刷新允许的通道模式列表,并断开用户的 Pub/Sub 客户端(如果这些客户端不再能够访问其各自的通道和/或通道模式)。

    最后,AUTH命令在Redis 6中进行了扩展,新加了一个username参数,示例如下:

    AUTH <username> <password>
    

    为兼容Redis老版本,Redis 6.0会默认创建一个default用户,其密码是通过require-pass配置项配置的。在客户端通过AUTH进行鉴权认证时,Redis的服务端会根据auth传入参数分情况处理:

    • 传入1个参数时,该参数会作为密码使用,用户名为默认生成的default用户;
    • 传入两个参数时,第1个参数表示用户名,第2个参数表示用户密码。

总结

通过Redis ACL,我们可以轻松地实现对Redis数据库的访问控制,保护重要数据不受未经授权的访问,根据实际需求,精细地管理用户的权限,从而提高系统的安全性和稳定性。在设计和配置ACL时,请确保平衡安全性和便利性,以确保系统的正常运行和良好的用户体验。