redis6 - 权限控制

最后更新:2020-04-18

1. ACL

在使用 Redis 6.0 之前的版本时,通常会遇到如下问题:

  • 执行 FLUSHALL 命令,整个 Redis 数据库就清空了
  • 执行 DEBUG SEGFAULT 命令,导致 Redis 崩溃
  • 执行 KEYS 命令,引起 Redis 的阻塞甚至宕机
  • ……

针对以上问题,我们解决的办法可能是使用 Redis 的 rename-command 将这些危险命令禁用或者重命名。也就是说,我们是有办法防止上述情况发生的。

Redis ACL 是 Access Control List(访问控制列表)的缩写,该功能允许对访问 Redis 的连接做一些可执行命令和可访问的 KEY 的限制。

它的工作方式是,在连接之后,要求客户端进行身份验证,以提供用户名和有效密码:如果身份验证成功,该客户端连接与给定用户绑定,并具有该用户的访问权限。

为了保证 Redis 向下兼容,Redis 6.0 中定义了一个默认的 “default” 用户,该用户拥有操作 Redis 的所有权限。也就是说,每个连接都能够调用每个命令并访问每个 KEY。同样,使用 requirepass 设置访问密码的方式与旧版本也保持一致,只不过 Redis 6.0 的这个密码仅对用户 “default” 有效。

Redis AUTH 命令也在 Redis 6.0 中进行了扩展,现在支持两个参数的访问

AUTH <username> <password>

在使用 ACL 前,我们应该考虑一下,使用 ACL 我们想要达到什么目的。

主要有下面两种目的,我们可以通过使用 ACLs 来达成 :

  1. 希望通过限制对命令和 KEY 的访问来提高安全性。以便不受信任的客户端无法访问,而受信任的客户端仅具有满足要求的最低操作权限。如:某些客户端可能仅能执行只读命令。
  2. 想提高操作安全性,以防止由于软件或人为错误而导致非授权情况的操作,导致数据或配置的损坏。例如,不应该让所有客户端都能够调用 FLUSHALL 命令。

2. 使用

ACLS 是使用 DSL(Domain specific language,领域专用语言)来定义的。

可以通过两种方式配置 ACL

  1. 在命令行通过 ACL 命令配置
  2. 开启 Redis 配置文件中开启 aclfile 配置项,通过配置文件的方式加载

redis.conf 默认配置中, 使用外部 aclfile 是不开启的

我们通过配置来启用:aclfile /usr/local/redis6/users.acl

/usr/local/redis6/users.acl 即 acl 的配置文件

启动或禁用用户

  • on 启用。可以使用该用户进行身份认证
  • off 禁用。不能使用该用户进行身份认证,但已通过身份认证的连接仍然可以使用。

启用或禁用命令

  • +<command><command> 命令添加到用户可调用的命令列表中
  • -<command> 从可调用的命令列表中移除 <command> 命令
  • +@<category> 允许用户调用 <category> 分类中的所有命令(可通过 ACL CAT 命令查看完整分类列表)
  • -@<category> 禁止用户调用 <category> 分类中的所有命令
  • +<command>|subcommand 允许使用原本禁用的特定类别下的特定子命令
  • +@all 允许调用所有命令,与使用 allcommands 效果相同。包括当前存在的命令以及将来通过模块加载的命令
  • -@all 禁止调用所有命令

允许或禁止访问某些 KEY

  • ~<pattern> 添加符合条件的模式。如: ~* 允许所有 KEY,使用 ~*allkeys 效果相同
  • resetkeys 使用当前模式覆盖所有允许的模式。如: ~foo:* ~bar:* resetkeys ~objects:* ,最终客户端只允许访问匹配 ~object:* 模式的 KEY

为用户配置有效密码

  • ><password> 将密码添加到用户有效密码列表中。例如:>mypassword 将会把 mypassword 添加到用户的密码列表中。该操作会清除用户的 nopass 标记。每个用户可由拥有多个有效密码
  • <<password> 将密码从用户有效密码列表中移除。列表中不存在改密码时,会报错。
  • **#** 将此 SHA-256 哈希值添加到用户的有效密码列表中。该哈希值将与 ACL 用户输入的密码的哈希值进行比较。这将允许用户将此哈希值存储在 acl 配置文件中,而不是存储明文密码。仅接受 SHA-256 哈希值,因为密码的哈希必须由 64 个字符长度的小写的十六进制字符组成。
  • **!** 从有效密码列表中删除该哈希值。(适用于不知道哈希值指定的密码但又想从用户中删除密码的情况)
  • nopass 删除用户所有密码,并将该用户标记为不需要密码。如果此指令引用于 default 用户,则每个新的连接都将立即通过 default 用户进行连接,而无需任何显示的 AUTH 命令。
  • resetpass 清除用户可用密码列表的数据,并清除 nopass 状态。之后该用户将没有任何关联的有效密码,将不允许登录,直到为该用户设置了有效密码或将用户设置成 nopass 状态
  • reset 重置用户到初始状态。该命令会执行以下操作:resetpass,resetkeys,off,-@ all。

示例

user worker +@list +@connection ~jobs:* on >ffa9203c493aa99

检查当前活动的 ACL 列表

127.0.0.1:6379> acl list
1) "user default on nopass ~* +@all"

ACL SETUSER

ACL SETUSER <username> 用户不存在,则按默认规则创建用户。用户存在,则该命令不执行任何操作。

ACL SETUSER <username> <rules> 用户不存在,则按默认规则创建用户,并增加 <rules> 。用户存在则在原有规则上增加 <rules>

127.0.0.1:6379> acl setuser test
OK
127.0.0.1:6379> acl list
1) "user default on nopass ~* +@all"
2) "user test off -@all"

我们创建的新用户默认规则为:

  • 处于关闭状态
  • 无法执行任何命令
  • 没有匹配的访问 KEY 的模式
  • 没有有效的密码
127.0.0.1:6379> ACL SETUSER test on >123456 ~cached:* +get
OK
127.0.0.1:6379> acl list
1) "user default on nopass ~* +@all"
2) "user test on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~cached:* -@all +get"

**ACL GETUSER **

127.0.0.1:6379> acl getuser test
1) "flags"
2) 1) "on"
3) "passwords"
4) 1) "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"
5) "commands"
6) "-@all +get"
7) "keys"
8) 1) "cached:*"

ACL DELUSER

127.0.0.1:6379> acl deluser foo
(integer) 1

ACL USERS

127.0.0.1:6379> acl users
1) "default"
2) "test"

ACL WHOAMI

127.0.0.1:6379> acl whoami
"default"

ACL CAT

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"
127.0.0.1:6379> acl cat string
 1) "setnx"
 2) "getrange"
 3) "incrbyfloat"
 4) "decr"
 5) "get"
 6) "incrby"
 7) "substr"
 8) "setrange"
 9) "psetex"
10) "set"
11) "stralgo"
12) "append"
13) "incr"
14) "msetnx"
15) "setex"
16) "strlen"
17) "mset"
18) "decrby"
19) "getset"
20) "mget"

ACL SAVE

将当前所有的 ACL 存入 aclfile,覆盖 aclfile 内容

ACL LOAD

从 acl 文件中加载定义的 acl 规则

该命令保证所有的规则都有效时才能执行成功:

  • 如果 aclfile 文件中每一行都有效,则将所有内容加载到内存替换内存中现有的 ACL 规则
  • 如果文件中有一行或多行无效,则不会加载任何内容,继续使用现有内存中的规则

ACL GENPASS

该命令默认创建一个 256 bit 的 32 字节的伪随机字符串,并将其转换为 64 字节的字母+数字的字符串。如有有参数,则使用指定的位数长度

127.0.0.1:6379> acl genpass
"4c61d8d0b6e10c41b232cd899efbed75237863143a9980bbb24ae78bda031f6b"
127.0.0.1:6379> acl genpass 16
"06dc"
127.0.0.1:6379> acl genpass 32
"6813b2f9"

ACL LOG

该命令记录如下 ACL 安全事件

  • 无法通过 AUTH 身份验证的
  • 违背当前 ACL 规则,执行命令被拒绝
  • 访问当前 ACL 规则中不允许的键,被拒绝访问
127.0.0.1:6379> acl log
1)  1) "count"
    2) (integer) 1
    3) "reason"
    4) "command"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "acl"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "150.03899999999999"
   13) "client-info"
   14) "id=11 addr=127.0.0.1:44994 fd=8 name= age=176 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=24 qbuf-free=32744 argv-mem=8 obl=0 oll=0 omem=0 tot-mem=61464 events=r cmd=acl user=test"
2)  1) "count"
    2) (integer) 1
    3) "reason"
    4) "command"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "keys"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "317.10700000000003"
   13) "client-info"
   14) "id=11 addr=127.0.0.1:44994 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=keys user=test"
3)  1) "count"
    2) (integer) 2
    3) "reason"
    4) "command"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "set"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "323.94799999999998"
   13) "client-info"
   14) "id=11 addr=127.0.0.1:44994 fd=8 name= age=2 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=31 qbuf-free=32737 argv-mem=9 obl=0 oll=0 omem=0 tot-mem=61473 events=r cmd=set user=test"
4)  1) "count"
    2) (integer) 1
    3) "reason"
    4) "command"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "command"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "326.26100000000002"
   13) "client-info"
   14) "id=11 addr=127.0.0.1:44994 fd=8 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=17 qbuf-free=32751 argv-mem=7 obl=0 oll=0 omem=0 tot-mem=61455 events=r cmd=command user=test"
5)  1) "count"
    2) (integer) 1
    3) "reason"
    4) "auth"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "AUTH"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "341.19799999999998"
   13) "client-info"
   14) "id=10 addr=127.0.0.1:44992 fd=8 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=33 qbuf-free=32735 argv-mem=11 obl=0 oll=0 omem=0 tot-mem=61475 events=r cmd=auth user=default"
6)  1) "count"
    2) (integer) 1
    3) "reason"
    4) "auth"
    5) "context"
    6) "toplevel"
    7) "object"
    8) "auth"
    9) "username"
   10) "test"
   11) "age-seconds"
   12) "371.55599999999998"
   13) "client-info"
   14) "id=9 addr=127.0.0.1:44990 fd=8 name= age=4 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=33 qbuf-free=32735 argv-mem=11 obl=0 oll=0 omem=0 tot-mem=61475 events=r cmd=auth user=default"

ACL LOG RESET

127.0.0.1:6379> acl log reset
OK
127.0.0.1:6379> acl log
(empty array)
Edgar

Edgar
一个略懂Java的小菜比