1. 前言
在使用 Redis 6.0 之前的版本时,通常会遇到如下问题:
●执行 FLUSHALL 命令,整个 Redis 数据库就清空了
●执行 DEBUG SEGFAULT 命令,导致 Redis 崩溃
●执行 KEYS 命令,引起 Redis 的阻塞甚至宕机
......
针对以上问题,我们解决的办法可能是使用 Redis 的 rename-command 将这些危险命令禁用或者重命名。也就是说,我们是有办法防止上述情况发生的。
但是,对于另外一种情况:多个项目公用一套 Redis 集群,多项目的 KEY 实际上是共享的,这样便存在安全风险。
我们如何解决多项目之间互相操作对方 KEY 的问题呢?
Redis 6.0 提供了一种新的特性 ACL ,可以方便的解决上述的各类问题。
Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。
Debug Segfault 命令执行一个非法的内存访问从而让 Redis 崩溃,仅在开发时用于 BUG 调试。
Keys 命令用于查找所有符合给定模式 pattern 的 key
redis.conf 中加入如下内容,禁用相应的命令
rename-command KEYS ""
rename-command FLUSHALL ""
rename-command DEBUG ""
2. 什么是 ACL
Redis ACL 是 Access Control List(访问控制列表)的缩写,该功能允许对访问 Redis 的连接做一些可执行命令和可访问的 KEY 的限制。
它的工作方式是,在连接之后,要求客户端进行身份验证,以提供用户名和有效密码:如果身份验证成功,该客户端连接与给定用户绑定,并具有该用户的访问权限。
为了保证 Redis 向下兼容,Redis 6.0 中定义了一个默认的 “default” 用户,该用户拥有操作 Redis 的所有权限。也就是说,每个连接都能够调用每个命令并访问每个 KEY。同样,使用 requirepass 设置访问密码的方式与旧版本也保持一致,只不过 Redis 6.0 的这个密码仅对用户 “default” 有效。
Redis AUTH 命令也在 Redis 6.0 中进行了扩展,现在支持两个参数的访问
AUTO <username> <password> |
当按照旧版本的方式访问时(在不输入用户名时,默认使用 default 用户,做到向下兼容)
AUTO <password> 等价于 AUTO default <password> |
2.1 什么时候使用 ACLs
在使用 ACL 前,我们应该考虑一下,使用 ACL 我们想要达到什么目的。
主要有下面两种目的,我们可以通过使用 ACLs 来达成 :
1.希望通过限制对命令和 KEY 的访问来提高安全性。以便不受信任的客户端无法访问,而受信任的客户端仅具有满足要求的最低操作权限。如:某些客户端可能仅能执行只读命令。
2.想提高操作安全性,以防止由于软件或人为错误而导致非授权情况的操作,导致数据或配置的损坏。例如,不应该让所有客户端都能够调用 FLUSHALL 命令。
3. ACL 规则
ACLS 是使用 DSL(Domain specific language,领域专用语言)来定义的。
可以通过两种方式配置 ACL
在命令行通过 ACL 命令配置
开启 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> 将密码添加到用户有效密码列表中。例如:>password 将会把 password 添加到用户的密码列表中。该操作会清除用户的 nopass 标记。每个用户可由拥有多个有效密码
●<<password> 将密码从用户有效密码列表中移除。列表中不存在改密码时,会报错。
●#<hash> 将此 SHA-256 哈希值添加到用户的有效密码列表中。该哈希值将与 ACL 用户输入的密码的哈希值进行比较。这将允许用户将此哈希值存储在 acl 配置文件中,而不是存储明文密码。仅接受 SHA-256 哈希值,因为密码的哈希必须由 64 个字符长度的小写的十六进制字符组成。
●!<hash> 从有效密码列表中删除该哈希值。(适用于不知道哈希值指定的密码但又想从用户中删除密码的情况)
●nopass 删除用户所有密码,并将该用户标记为不需要密码。如果此指令引用于 default 用户,则每个新的连接都将立即通过 default 用户进行连接,而无需任何显示的 AUTH 命令。
●resetpass 清除用户可用密码列表的数据,并清除 nopass 状态。之后该用户将没有任何关联的有效密码,将不允许登录,直到为该用户设置了有效密码或将用户设置成 nopass 状态
reset 重置用户到初始状态。该命令会执行以下操作:resetpass,resetkeys,off,-@ all。
未使用 nopass 标记且没有有效密码列表的用户,实际上是无法使用的。因为无法以该用户的身份登录。 |
4. ACL 常用命令
ACL LIST
检查当前活动的 ACL 列表 |
127.0.0.1:6379> ACL LIST |
1) "user default on nopass ~* +@all" |
首次安装后只有一个默认的 default 用户,其具有如下状态:
●用户名为 default
●用户处于启用状态
●访问不需要密码
●可以访问所有 KEY
●可以执行所有命令
ACL SETUSER
ACL SETUSER <username> 用户不存在,则按默认规则创建用户。用户存在,则该命令不执行任何操作。
ACL SETUSER <username> <rules> 用户不存在,则按默认规则创建用户,并增加 <rules> 。用户存在则在原有规则上增加 <rules>。
<username> 用户名区分大小写
// 创建一个名为 gcp 的用户 |
127.0.0.1:6379> ACL SETUSER gcp |
OK |
// 查询所有活动的 ACL |
127.0.0.1:6379> ACL LIST |
1) "user default on nopass ~* +@all" |
2) "user gcp off -@all" |
我们创建的 gcp 用户默认规则为:
●处于关闭状态
●无法执行任何命令
●没有匹配的访问 KEY 的模式
●没有有效的密码
我们为其配置一些 ACL 规则
// 用户状态设置为启用,密码为 password,允许访问以 cached: 开头的 KEY,允许执行 get 命令 |
127.0.0.1:6379> ACL SETUSER gcp on >password ~cached:* +get |
OK |
127.0.0.1:6379> ACL LIST |
1) "user default on nopass ~* +@all" |
2) "user gcp on #89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8 ~cached:* -@all +get" |
// set 一个以 cached: 开头的 KEY 用来测试 |
127.0.0.1:6379> set cached:test test |
OK |
使用 AUTH 命令切换到 gcp 用户下,进行测试
// 使用 AUTH 切换到 gcp 用户下 |
127.0.0.1:6379> AUTH gcp password |
OK |
// 使用 set 命令设置不符合匹配规则的 KEY |
127.0.0.1:6379> set a 1 |
(error) NOPERM this user has no permissions to run the 'set' command or its subcommand |
// 使用 set 命令设置符合匹配规则的 KEY |
127.0.0.1:6379> set cached:aaa 1 |
(error) NOPERM this user has no permissions to run the 'set' command or its subcommand |
// 使用 get 命令获取不符合匹配的 KEY 值 |
127.0.0.1:6379> get a |
(error) NOPERM this user has no permissions to access one of the keys used as arguments |
// 使用 get 命令获取符合匹配规则的 KEY 值 |
127.0.0.1:6379> get cached:test |
"test" |
测试结果与我们配置的 ACL 规则相符。
ACL GETUSER <username>
同 ACL LIST 作用类似。ACL GETUSER 用来获取指定用户的 ACL 状态信息 |
127.0.0.1:6379> ACL GETUSER default |
1) "flags" // 标记数组(启用、可操作所有 KEY、可执行所有命令、无密码) |
2) 1) "on" |
2) "allkeys" |
3) "allcommands" |
4) "nopass" 3) "passwords" // 密码 |
4) (empty array) |
5) "commands" // 命令匹配规则 |
6) "+@all" |
7) "keys" // KEY 的模式匹配规则 |
8) 1) "*" |
127.0.0.1:6379> ACL GETUSER gcp |
1) "flags" |
2) 1) "on" |
3) "passwords" |
4) 1) "89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8" |
5) "commands" |
6) "-@all +get" |
7) "keys" |
8) 1) "cached:*" |
ACL DELUSER
删除用户 ACL DELUSER <username> |
// 创建一个测试用户 |
127.0.0.1:6379> ACL SETUSER testuser |
OK |
127.0.0.1:6379> ACL LIST |
1) "user default on nopass ~* +@all" |
2) "user gcp on +@all" |
3) "user testuser off -@all" |
// 删除用户 |
127.0.0.1:6379> ACL DELUSER testuser |
(integer) 1 |
// 查看结果 |
127.0.0.1:6379> ACL LIST |
1) "user default on nopass ~* +@all" |
2) "user gcp on +@all" |
ACL USERS
查看 ACL 的所有用户 |
127.0.0.1:6379> ACL USERS |
1) "default" |
2) "gcp" |
ACL WHOAMI
查看当前操作的用户 |
127.0.0.1:6379> ACL WHOAMI |
"default" |
ACL CAT
ACL CAT 显示所有的 ACL 类别 |
ACL CAT <类别> 显示指定类别中所有的 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" |
以 dangerous 为例,查看该类别包含的所有命令:
127.0.0.1:6379> ACL CAT dangerous 1) "bgsave" 2) "module" 3) "replconf" 4) "info" 5) "role" 6) "debug" 7) "slaveof" 8) "keys" 9) "flushdb" 10) "bgrewriteaof" 11) "lastsave" 12) "acl" 13) "psync" 14) "client" 15) "latency" 16) "save" 17) "migrate" 18) "pfselftest" 19) "swapdb" 20) "restore-asking" 21) "sync" 22) "shutdown" 23) "monitor" 24) "slowlog" 25) "pfdebug" 26) "flushall" 27) "sort" 28) "config" 29) "replicaof" 30) "cluster" 31) "restore" |
ACL SAVE
前提:redis 配置中启用了 aclfile 选项 |
将当前所有的 ACL 存入 aclfile,覆盖 aclfile 内容 |
ACL LOAD
前提:redis 配置中启用了 aclfile 选项 |
从 acl 文件中加载定义的 acl 规则 |
该命令保证所有的规则都有效时才能执行成功:
●如果 aclfile 文件中每一行都有效,则将所有内容加载到内存替换内存中现有的 ACL 规则
●如果文件中有一行或多行无效,则不会加载任何内容,继续使用现有内存中的规则
ACL GENPASS
可以使用该命令来生成 Redis 密码 |
该命令默认创建一个 256 bit 的 32 字节的伪随机字符串,并将其转换为 64 字节的字母+数字的字符串。如有有参数,则使用指定的位数长度 |
127.0.0.1:6379> ACL GENPASS |
"e2ceb52e866908c2706e521b7d145eb78ec4b5806ef1fd0bcb4482687d2c82f7" |
127.0.0.1:6379> ACL GENPASS 32 |
"fbcc163c" |
127.0.0.1:6379> ACL GENPASS 64 |
"3ab469e7c659c19a" |
ACL LOG
查看安全事件日志 |
该命令记录如下 ACL 安全事件
●无法通过 AUTH 身份验证的
●违背当前 ACL 规则,执行命令被拒绝
●访问当前 ACL 规则中不允许的键,被拒绝访问
127.0.0.1:6379> ACL LOG 1) 1) "count" 2) (integer) 1 3) "reason" 4) "auth" 5) "context" 6) "toplevel" 7) "object" 8) "AUTH" 9) "username" 10) "gcp" 11) "age-seconds" 12) "3076.3829999999998" 13) "client-info" 14) "id=4 addr=127.0.0.1:53462 fd=7 name= age=12138 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=40 qbuf-free=32728 obl=0 oll=0 omem=0 events=r cmd=auth user=default" |
使用 RESET 命令清空 LOG
127.0.0.1:6379> ACL LOG RESET |
OK |
127.0.0.1:6379> ACL LOG (empty array) |
ACL HELP
ACL 命令帮助 |
127.0.0.1:6379> ACL HELP 1) ACL <subcommand> arg arg ... arg. Subcommands are: 2) LOAD -- Reload users from the ACL file. 3) SAVE -- Save the current config to the ACL file. 4) LIST -- Show user details in config file format. 5) USERS -- List all the registered usernames. 6) SETUSER <username> [attribs ...] -- Create or modify a user. 7) GETUSER <username> -- Get the user details. 8) DELUSER <username> [...] -- Delete a list of users. 9) CAT -- List available categories. 10) CAT <category> -- List commands inside category. 11) GENPASS [<bits>] -- Generate a secure user password. 12) WHOAMI -- Return the current connection username. 13) LOG [<count> | RESET] -- Show the ACL log entries. |