summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMadelyn Olson <34459052+madolson@users.noreply.github.com>2022-05-29 23:42:56 -0700
committerGitHub <noreply@github.com>2022-05-29 23:42:56 -0700
commited29d634b3844d2c6af099e22f3bd6bf316edab9 (patch)
treebe47eb25b78dd02ce76837bece5d4413acd3b0be
parent7a550c8bbbf55bdc56a45b9c728b45408d620bc5 (diff)
downloadredis-ed29d634b3844d2c6af099e22f3bd6bf316edab9.tar.gz
Add readonly flag to EVAL_RO, EVALSHA_RO and FCALL_RO (#10728)
* Add readonly flag to EVAL_RO, EVALSHA_RO and FCALL_RO * Require users to explicitly declare @scripting to get access to lua scripting.
-rw-r--r--src/commands.c6
-rw-r--r--src/commands/eval_ro.json3
-rw-r--r--src/commands/evalsha_ro.json3
-rw-r--r--src/commands/fcall_ro.json3
-rw-r--r--src/server.c11
-rw-r--r--tests/unit/acl.tcl7
-rw-r--r--tests/unit/tracking.tcl27
7 files changed, 51 insertions, 9 deletions
diff --git a/src/commands.c b/src/commands.c
index 425b64351..2e694076c 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -7279,10 +7279,10 @@ struct redisCommand redisCommandTable[] = {
/* scripting */
{"eval","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_History,EVAL_tips,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_Args},
{"evalsha","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_History,EVALSHA_tips,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_Args},
-{"evalsha_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,EVALSHA_RO_tips,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_RO_Args},
-{"eval_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_RO_History,EVAL_RO_tips,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_RO_Args},
+{"evalsha_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,EVALSHA_RO_tips,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_RO_Args},
+{"eval_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_RO_History,EVAL_RO_tips,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_RO_Args},
{"fcall","Invoke a function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_History,FCALL_tips,fcallCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_Args},
-{"fcall_ro","Invoke a read-only function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_RO_History,FCALL_RO_tips,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_RO_Args},
+{"fcall_ro","Invoke a read-only function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_RO_History,FCALL_RO_tips,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_RO_Args},
{"function","A container for function commands","Depends on subcommand.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_History,FUNCTION_tips,NULL,-2,0,0,.subcommands=FUNCTION_Subcommands},
{"script","A container for Lua scripts management commands","Depends on subcommand.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_History,SCRIPT_tips,NULL,-2,0,0,.subcommands=SCRIPT_Subcommands},
/* sentinel */
diff --git a/src/commands/eval_ro.json b/src/commands/eval_ro.json
index 952303d39..65fd866a0 100644
--- a/src/commands/eval_ro.json
+++ b/src/commands/eval_ro.json
@@ -11,7 +11,8 @@
"NOSCRIPT",
"SKIP_MONITOR",
"NO_MANDATORY_KEYS",
- "STALE"
+ "STALE",
+ "READONLY"
],
"acl_categories": [
"SCRIPTING"
diff --git a/src/commands/evalsha_ro.json b/src/commands/evalsha_ro.json
index d876c5d76..d76313540 100644
--- a/src/commands/evalsha_ro.json
+++ b/src/commands/evalsha_ro.json
@@ -11,7 +11,8 @@
"NOSCRIPT",
"SKIP_MONITOR",
"NO_MANDATORY_KEYS",
- "STALE"
+ "STALE",
+ "READONLY"
],
"acl_categories": [
"SCRIPTING"
diff --git a/src/commands/fcall_ro.json b/src/commands/fcall_ro.json
index 092915700..46085ebb0 100644
--- a/src/commands/fcall_ro.json
+++ b/src/commands/fcall_ro.json
@@ -11,7 +11,8 @@
"NOSCRIPT",
"SKIP_MONITOR",
"NO_MANDATORY_KEYS",
- "STALE"
+ "STALE",
+ "READONLY"
],
"acl_categories": [
"SCRIPTING"
diff --git a/src/server.c b/src/server.c
index c031716eb..8f4654ad8 100644
--- a/src/server.c
+++ b/src/server.c
@@ -2716,7 +2716,8 @@ void commandAddSubcommand(struct redisCommand *parent, struct redisCommand *subc
void setImplicitACLCategories(struct redisCommand *c) {
if (c->flags & CMD_WRITE)
c->acl_categories |= ACL_CATEGORY_WRITE;
- if (c->flags & CMD_READONLY)
+ /* Exclude scripting commands from the RO category. */
+ if (c->flags & CMD_READONLY && !(c->acl_categories & ACL_CATEGORY_SCRIPTING))
c->acl_categories |= ACL_CATEGORY_READ;
if (c->flags & CMD_ADMIN)
c->acl_categories |= ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS;
@@ -3392,8 +3393,12 @@ void call(client *c, int flags) {
(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);
/* If the client has keys tracking enabled for client side caching,
- * make sure to remember the keys it fetched via this command. */
- if (c->cmd->flags & CMD_READONLY) {
+ * make sure to remember the keys it fetched via this command. Scripting
+ * works a bit differently, where if the scripts executes any read command, it
+ * remembers all of the declared keys from the script. */
+ if ((c->cmd->flags & CMD_READONLY) && (c->cmd->proc != evalRoCommand)
+ && (c->cmd->proc != evalShaRoCommand) && (c->cmd->proc != fcallroCommand))
+ {
client *caller = (c->flags & CLIENT_SCRIPT && server.script_caller) ?
server.script_caller : c;
if (caller->flags & CLIENT_TRACKING &&
diff --git a/tests/unit/acl.tcl b/tests/unit/acl.tcl
index c252de031..11c871bb6 100644
--- a/tests/unit/acl.tcl
+++ b/tests/unit/acl.tcl
@@ -529,6 +529,13 @@ start_server {tags {"acl external:skip"}} {
assert_equal [lsearch [r acl cat stream] "get"] -1
}
+ test "ACL requires explicit permission for scripting for EVAL_RO, EVALSHA_RO and FCALL_RO" {
+ r ACL SETUSER scripter on nopass +readonly
+ assert_equal "This user has no permissions to run the 'eval_ro' command" [r ACL DRYRUN scripter EVAL_RO "" 0]
+ assert_equal "This user has no permissions to run the 'evalsha_ro' command" [r ACL DRYRUN scripter EVALSHA_RO "" 0]
+ assert_equal "This user has no permissions to run the 'fcall_ro' command" [r ACL DRYRUN scripter FCALL_RO "" 0]
+ }
+
test {ACL #5998 regression: memory leaks adding / removing subcommands} {
r AUTH default ""
r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c
diff --git a/tests/unit/tracking.tcl b/tests/unit/tracking.tcl
index 140656acf..6270ce472 100644
--- a/tests/unit/tracking.tcl
+++ b/tests/unit/tracking.tcl
@@ -208,6 +208,33 @@ start_server {tags {"tracking network"}} {
assert {$res eq {key1}}
}
+ test {Tracking only occurs for scripts when a command calls a read-only command} {
+ r CLIENT TRACKING off
+ r CLIENT TRACKING on
+ $rd_sg MSET key2{t} 1 key2{t} 1
+
+ # If a script doesn't call any read command, don't track any keys
+ r EVAL "redis.call('set', 'key3{t}', 'bar')" 2 key1{t} key2{t}
+ $rd_sg MSET key2{t} 2 key1{t} 2
+
+ # If a script calls a read command, track all declared keys
+ r EVAL "redis.call('get', 'key3{t}')" 2 key1{t} key2{t}
+ $rd_sg MSET key2{t} 2 key1{t} 2
+ assert_equal {invalidate key2{t}} [r read]
+ assert_equal {invalidate key1{t}} [r read]
+
+ # RO variants work like the normal variants
+ r EVAL_RO "redis.call('ping')" 2 key1{t} key2{t}
+ $rd_sg MSET key2{t} 2 key1{t} 2
+
+ r EVAL_RO "redis.call('get', 'key1{t}')" 2 key1{t} key2{t}
+ $rd_sg MSET key2{t} 3 key1{t} 3
+ assert_equal {invalidate key2{t}} [r read]
+ assert_equal {invalidate key1{t}} [r read]
+
+ assert_equal "PONG" [r ping]
+ }
+
test {RESP3 Client gets tracking-redir-broken push message after cached key changed when rediretion client is terminated} {
r CLIENT TRACKING on REDIRECT $redir_id
$rd_sg SET key1 1