Redis 7 + ACL 简介

摘要

Redis ACL 简介

  • 从 Redis 6.0 开始,引入了 ACL 功能,用来精细化控制不同用户能做什么,代替过去只有一个全局密码(requirepass)的粗粒度模式。

  • ACL 可以控制的维度包括:

    • 谁能登录(用户、密码)
    • 可以执行哪些命令
    • 可以访问哪些 key(按前缀 / 正则)
    • 可否通过网络访问
    • 是否启用 / 禁用某个用户
  • 一句话:Redis ACL 实现了多用户 + 权限最小化 + 资源隔离

Redis ACL 的使用

  • redis.conf 中添加如下内容:

1
2
3
4
# 指定 ACL 文件
aclfile /etc/redis/users.acl
# 关闭 密码
# requirepass foobared
  • 也可以不配置 aclfile,而是将用户信息直接编写在 redis.conf 中,但不建议这样做。

ACL 的配置方式

直接编辑 aclfile

  • 创建一个 users.acl 文件,并写入如下内容:

1
2
3
4
5
6
# 配置一个管理员
user admin on ~* &* +@all >password
# 配置一个应用用户
user appuser on ~cache:* &app:* +get +set >password
# 配置一个只读用户
user readonly on ~* -@all +@read +ping +info +client +config|get >password
  • 这里要特别注意,aclfile 不支持注释行,所有行都必须以 user 开头,否则会报错。

  • 格式说明

1
2
3
4
5
6
7
user: 固定前缀
username: 用户名
on: 启用 off: 禁用
~pattern: key 匹配模式,~*:匹配所有 key,~cache:*: 匹配 cache: 开头的所有 key
&pattern: 频道匹配模式,&*:匹配所有频道,&app:*:匹配 app:* 开头的所有频道,通道就是 pub/sub 的频道
+command: 允许的命令,可以直接输入命令名称,如 +get +set,子命令 +config|get,也可以是命令的类别,命令类别 通过 acl cat 命令查看,以@开头,比如 @read @write,+ 表示允许命令,- 表示禁止命令
>password: 密码明文,注意 > 与 密码之间不能有空格
  • 为了兼容以前的版本,Redis 提供了一个默认的用户:default,不指定用户名的时候,默认使用的就是 default 用户,其对应的acl权限为

1
2
3
4
# 实际上就是一个超级管理员权限
user default on nopass sanitize-payload ~* &* +@all
# 如果设置了 requirepass 密码
user default on sanitize-payload #5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 ~* &* +@all
  • 开启ACL后,推荐关闭 default 用户。

1
2
# 关闭默认用户,禁止匿名访问
user default off
  • 登录redis

1
2
3
4
5
6
7
8
9
10
11
12
13
# 连接时直接登录, --pass == -a
redis-cli --user username --pass password -h host -p port
# 或者
redis-cli -u redis://admin:123456@127.0.0.1:6379
# 本机登录
redis-cli -u redis://admin:123456@
# 本机登录时端口不是默认的6379时
redis-cli -u redis://admin:123456@:6380


# 先连接后登录
redis-cli -h host -p port
> auth username password
  • Redis ACL 规则中文说明

规则 / 语法 中文说明
on 启用用户:可以使用该用户进行认证登录
off 禁用用户:无法再使用该用户认证,但已认证的连接仍然有效
skip-sanitize-payload 跳过对 RESTORE 命令的 dump 数据载荷过滤(跳过安全检查)
sanitize-payload RESTORE 命令的 dump 数据载荷进行过滤(默认)
+<command> 允许执行指定命令;可以指定子命令,例如:+config | get
-<command> 禁止执行指定命令;可以指定子命令,例如:-config | set
+@<category> 允许一个命令分类的所有命令,如:@admin, @set, @sortedset 等。完整分类在 server.c 的命令表中
@all 特殊分类,表示所有当前已有命令 + 未来模块加载的命令
+<command>|first-arg 只允许使用某命令的第一个参数(已废弃,将可能移除);只支持新增,不支持禁止(如 -SELECT|1 不允许)
allcommands +@all 的别名,允许所有命令(包括将来可能加载的模块命令)
nocommands -@all 的别名,禁止所有命令
~<pattern> 添加允许访问的 key 模式(glob 风格),如:~user:*;可以有多个
%R~<pattern> 添加允许读取的 key 模式
%W~<pattern> 添加允许写入的 key 模式
allkeys ~* 的别名,允许所有 key
resetkeys 清空允许访问的 key 模式
&<pattern> 添加可访问的 Pub/Sub channel 模式(glob 风格),可多个
allchannels &* 的别名,允许所有 channel
resetchannels 清空 Pub/Sub channel 模式列表
><password> 添加密码,例如:>mypass;此指令会清除 nopass 标志
<<password> 移除指定密码
nopass 移除所有密码,且任何密码都可登录。如果用于 default 用户,则新连接无需 AUTH 即自动登录为 default
resetpass 清空所有密码,并移除 nopass 状态。此后没有密码将无法认证
reset 重置所有设置:包括 resetpassresetkeysresetchannelsallchannels(如果开启 acl-pubsub-default)、offclearseletors-@all
(<options>) 创建一个新的 selector(选择器),括号内为该 selector 的独立权限规则
clearselectors 删除所有 selector,但不会影响根权限(直接赋给用户的权限)

通过 ACL 命令

ACL SETUSER
  • 创建/修改用户

1
2
3
4
5
6
7
8
# 关闭 default 用户
ACL SETUSER default off
# 创建一个管理员
ACL SETUSER admin on ~* &* +@all >password
# 创建一个普通用户
ACL SETUSER appuser on >password ~cache:* &app:* +get +set
# 创建一个只读用户
ACL SETUSER readonly on ~* -@all +@read +ping +info +client +config|get >password
  • 可以看的出来,ACL SETUSER 后面的命令格式与直接编辑 users.acl 文件是一样的

  • 这里要注意 ACL SETUSER 即可以创建用户,也可以修改用户,当用户不存在时创建用户,当用户存在时修改用户,修改用户时并不会覆盖旧用户,而是会将权限进行合并,比如:

1
2
3
4
# 第一次执行
ACL SETUSER testuser on ~* -@all +@read +ping >password
# 第二次执行,只添加新的权限
ACL SETUSER testuser +info +client +config|get
  • 运行后的结果

1
"user testuser on sanitize-payload #5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 ~* resetchannels -@all +@read +ping +info +client +config|get"
修改用户权限
  • 重置权限

1
2
3
4
# 重置所有权限
ACL SETUSER testuser reset
## 运行结果:去除所有频道和命令权限,禁止登录
"user testuser off sanitize-payload resetchannels -@all"
  • 取消密码,但保留权限

1
2
3
ACL SETUSER testuser nopass
## 运行结果:取消密码
"user testuser on nopass sanitize-payload ~* resetchannels -@all +@read +ping"
  • 去除所有频道

1
ACL SETUSER testuser resetchannels
  • 去除所有selectors,关于selectors后面会详细介绍

1
ACL SETUSER testuser clearselectors
ACL LIST
  • 可以通过如下命令查看当前 Redis 中所有的用户

1
2
3
4
5
6
ACL LIST
## 输出
1) "user admin on sanitize-payload #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* &* +@all"
2) "user appuser on sanitize-payload #5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 ~cache:* resetchannels &app:* -@all +get +set"
3) "user default off sanitize-payload resetchannels -@all"
4) "user readonly on sanitize-payload #5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 ~* resetchannels -@all +@read +ping +info +client +config|get"
  • 这里显示的并不是设置权限时的命令格式,而是经过翻译后的命令格式

1
2
3
4
5
6
7
8
9
user: 固定前缀
username: 用户名
on: 启用
sanitize-payload: 是在用 RESTORE 命令导入 RDB 数据时,帮你“体检 + 消毒 + 清洗”这份数据,防止恶意内容被导进去。
#xxx: 密码的加密哈希值(通常是 SHA-256), >xxx = 明文密码,两种方式都可以,但推荐使用哈希值,实际上我们可以先通过明文配置,然后通过 ACL SAVE 命令保存就会将密码变成哈希值了
~pattern: key 匹配模式,~*:匹配所有 key,~cache:*: 匹配 cache: 开头的所有 key
resetchannels: 重置频道权限,就是先删除所有频道权限,后面根据 & 的配置再添加新的频道权限。
&pattern: 频道匹配模式,&*:匹配所有频道,&app:*:匹配 app:* 开头的所有频道,通道就是 pub/sub 的频道
+command: 允许的命令,可以直接输入命令名称,如 +get +set,子命令 +config|get,也可以是命令的类别,命令类别 通过 acl cat 命令查看,以@开头,比如 @read @write,+ 表示允许命令,- 表示禁止命令
ACL LOAD
  • 当通过 ACL SETUSER 创建用户时,可以通过如下命令将其保存到 users.acl 文件中

1
ACL SAVE
  • 此时保存的文件内容就是 ACL LIST 命令的输出

ACL SAVE
  • 当修改了 users.acl 文件后,可以通过如下命令将其重新加载到 Redis 中

1
ACL LOAD
ACL USERS
  • 列出所有已创建的用户

1
2
3
4
5
6
ACL USERS
## 输出
1) "admin"
2) "appuser"
3) "default"
4) "readonly"
ACL GETUSER
  • 获取指定用户的权限信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ACL GETUSER appuser
## 输出
1) "flags"
2) 1) "on"
2) "sanitize-payload"
3) "passwords"
4) 1) "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
5) "commands"
6) "-@all +get +set"
7) "keys"
8) "~cache:*"
9) "channels"
10) "&app:*"
11) "selectors"
12) (empty array)
selectors(选择器规则)
  • 这里看到最后有一个selectors,这个是Redis 7.0 引入的一个新能力,它允许一个用户同时拥有多套不同的 ACL 规则,而不是只能有一套规则。

  • 以前 = 一个用户一条规则

1
2
# 根权限(全局规则)
ACL SETUSER selectoruser on ~cache:* +get +set
  • 现在 = 一个用户可以有多个“权限分身”

1
2
3
4
5
# 选择器规则
# 注意命令最后的两个(),这里设置了两个规则,每个()代表一个规则,根据不同的key前缀授予用户不同的权限
ACL SETUSER selectoruser on (~cache:* +get) (~session:* +get +set) >123456
## 翻译后的结果
"user selectoruser on sanitize-payload resetchannels -@all (~cache:* resetchannels -@all +get) (~session:* resetchannels -@all +get +set)" >123456
  • 如果一个用户同时拥有 根权限规则 和 选择器规则,则 选择器规则 优先级更高。

ACL DELUSER
  • 删除一个用户

1
ACL DELUSER 用户名
ACL WHOAMI
  • 获取当前登录的用户名

1
ACL WHOAMI
ACL CAT
  • 获取所有权限类别

  • 前面我们在为用户授权时介绍过,@ 后面跟的是一个权限类别,比如 @all、@dangerous,你可以理解为其是一组权限(命令)的集合。

  • ACL CAT 命令可以获取所有权限类别,然后你可以根据需要选择需要的权限类别。

1
2
3
4
# 获取所有权限类别
ACL CAT
# 获取指定类别中的命令列表
ACL CAT 分类名称
  • 这里有一个特殊的权限类别 @all 并不在列出的权限类别中,其表示所有命令。

ACL DRYRUN
  • ACL DRYRUN 命令可以模拟执行命令,并返回模拟结果,并不是真的执行命令。

  • Redis 7.0.0 新增。

1
2
3
4
5
6
> ACL SETUSER VIRGINIA +SET ~*
"OK"
> ACL DRYRUN VIRGINIA SET foo bar
"OK"
> ACL DRYRUN VIRGINIA GET foo
"User VIRGINIA has no permissions to run the 'get' command"
ACL LOG
  • ACL LOG 命令可以查看 ACL 命令执行日志。

  • 它记录了 ACL(访问控制)相关的事件,也就是用户在操作被拒绝或触发 ACL 规则时的行为记录。

1
2
ACL LOG          # 查看默认最新的 ACL 日志条目
ACL LOG RESET # 清空 ACL 日志
  • 日志格式

1
2
3
4
5
6
7
8
9
10
1) "count" => 1
2) "reason" => "command"
3) "context" => "toplevel"
4) "object" => "acl|log"
5) "username" => "selectoruser"
6) "age-seconds" => 1589.819
7) "client-info" => "id=8 addr=127.0.0.1:56424 laddr=127.0.0.1:6379 fd=11 name= age=1390 idle=0 flags=N db=0 ... cmd=acl|log user=selectoruser ..."
8) "entry-id" => 3
9) "timestamp-created" => 1765089007352
10) "timestamp-last-updated" => 1765089007352
字段 含义
count 触发该日志的次数。比如同一事件触发 1 次就是 1
reason 日志触发的原因,通常是 command 表示某个命令被执行或被 ACL 检查
context 执行命令的上下文,toplevel 表示直接在客户端执行
object 触发事件的对象,例如 acl|log表示执行了ACL LOG 命令,acl|list表示执行了ACL LIST
username 触发事件的用户
age-seconds 事件距离当前的时间(秒),越大表示越久远
client-info 客户端详细信息,包括客户端 ID、IP 地址、端口、本地地址、文件描述符、客户端名称、DB、执行命令等
entry-id 日志条目 ID
timestamp-created 日志创建时间(毫秒)
timestamp-last-updated 日志最后更新时间(毫秒)
ACL GENPASS
  • 生成一个随机的 ACL 密码

  • 生成复杂密码的工具有很多,没必要用这玩意。

  • 该命令的输出是二进制字符串的十六进制表示形式。默认情况下,它会生成 256 位(即 64 个十六进制字符)。用户可以通过提供一个参数来指定生成的位数,范围从 1 到 1024 位,以改变输出长度。需要注意的是,所提供的位数总是会向上取整到 4 的倍数。例如,如果请求生成 1 位密码,实际上会生成 4 位,并以 单个十六进制字符的形式输出。

1
2
3
4
5
6
7
8
9
# 默认64个十六进制字符,相当于 ACL GENPASS 256
> ACL GENPASS
"dd721260bfe1b3d9601e7fbab36de6d04e2e67b0ef1c53de59d45950db0dd3cc"
# 指定长度,32/4 = 8
> ACL GENPASS 32
"355ef3dd"
# 指定长度,5 向上取4的倍数,就是 8,长度就是 8/4 = 2
> ACL GENPASS 5
"90"