tags {modules external:skip cluster} { set testmodule [file normalize tests/modules/internalsecret.so] set modules [list loadmodule $testmodule] start_cluster 1 0 [list config_lines $modules] { set r [srv 0 client] test {Internal command without internal connection fails as an unknown command} { assert_error {*unknown command*} {r internalauth.internalcommand} } test {Wrong internalsecret fails authentication} { assert_error {*WRONGPASS invalid internal password*} {r auth "internal connection" 123} } test {Internal connection basic flow} { # A non-internal connection cannot execute internal commands, and they # seem non-existent to it. assert_error {*unknown command*} {r internalauth.internalcommand} # Authenticate as an internal connection assert_equal {OK} [r debug mark-internal-client] # Now, internal commands are available. assert_equal {OK} [r internalauth.internalcommand] } } start_server {} { r module load $testmodule test {Internal secret is not available in non-cluster mode} { # On non-cluster mode, the internal secret does not exist, nor is the # internal auth command available assert_error {*unknown command*} {r internalauth.internalcommand} assert_error {*ERR no internal secret available*} {r internalauth.getinternalsecret} assert_error {*Cannot authenticate as an internal connection on non-cluster instances*} {r auth "internal connection" somepassword} } test {marking and un-marking a connection as internal via a debug command} { # After marking the connection to an internal one via a debug command, # internal commands succeed. r debug mark-internal-client assert_equal {OK} [r internalauth.internalcommand] # After unmarking the connection, internal commands fail. r debug mark-internal-client unmark assert_error {*unknown command*} {r internalauth.internalcommand} } } start_server {} { r module load $testmodule test {Test `COMMAND *` commands with\without internal connections} { # ------------------ Non-internal connection ------------------ # `COMMAND DOCS ` returns empty response. assert_equal {} [r command docs internalauth.internalcommand] # `COMMAND INFO ` should reply with null for the internal command assert_equal {{}} [r command info internalauth.internalcommand] # `COMMAND GETKEYS/GETKEYSANDFLAGS ` returns an invalid command error assert_error {*Invalid command specified*} {r command getkeys internalauth.internalcommand} assert_error {*Invalid command specified*} {r command getkeysandflags internalauth.internalcommand} # -------------------- Internal connection -------------------- # Non-empty response for non-internal connections. assert_equal {OK} [r debug mark-internal-client] # `COMMAND DOCS ` returns a correct response. assert_match {*internalauth.internalcommand*} [r command docs internalauth.internalcommand] # `COMMAND INFO ` should reply with a full response for the internal command assert_match {*internalauth.internalcommand*} [r command info internalauth.internalcommand] # `COMMAND GETKEYS/GETKEYSANDFLAGS ` returns a key error (not related to the internal connection) assert_error {*ERR The command has no key arguments*} {r command getkeys internalauth.internalcommand} assert_error {*ERR The command has no key arguments*} {r command getkeysandflags internalauth.internalcommand} } } start_server {} { r module load $testmodule test {No authentication needed for internal connections} { # Authenticate with a user that does not have permissions to any command r acl setuser David on >123 &* ~* -@all +auth +internalauth.getinternalsecret +debug +internalauth.internalcommand assert_equal {OK} [r auth David 123] assert_equal {OK} [r debug mark-internal-client] # Execute a command for which David does not have permission assert_equal {OK} [r internalauth.internalcommand] } } start_server {} { r module load $testmodule test {RM_Call of internal commands without user-flag succeeds only for all connections} { # Fail before authenticating as an internal connection. assert_equal {OK} [r internalauth.noninternal_rmcall internalauth.internalcommand] } test {Internal commands via RM_Call succeeds for non-internal connections depending on the user flag} { # A non-internal connection that calls rm_call of an internal command assert_equal {OK} [r internalauth.noninternal_rmcall internalauth.internalcommand] # A non-internal connection that calls rm_call of an internal command # with a user flag should fail. assert_error {*unknown command*} {r internalauth.noninternal_rmcall_withuser internalauth.internalcommand} } test {Internal connections override the user flag} { # Authenticate as an internal connection assert_equal {OK} [r debug mark-internal-client] assert_equal {OK} [r internalauth.noninternal_rmcall internalauth.internalcommand] assert_equal {OK} [r internalauth.noninternal_rmcall_withuser internalauth.internalcommand] } } start_server {} { r module load $testmodule test {RM_Call with the user-flag after setting thread-safe-context from an internal connection should fail} { # Authenticate as an internal connection assert_equal {OK} [r debug mark-internal-client] # New threadSafeContexts do not inherit the internal flag. assert_error {*unknown command*} {r internalauth.noninternal_rmcall_detachedcontext_withuser internalauth.internalcommand} } } start_server {} { r module load $testmodule r config set appendonly yes r config set appendfsync always waitForBgrewriteaof r test {AOF executes internal commands successfully} { # Authenticate as an internal connection assert_equal {OK} [r debug mark-internal-client] # Call an internal writing command assert_equal {OK} [r internalauth.internal_rmcall_replicated set x 5] # Reload the server from the AOF r debug loadaof # Check if the internal command was executed successfully assert_equal {5} [r get x] } } start_server {} { r module load $testmodule test {Internal commands are not allowed from scripts} { # Internal commands are not allowed from scripts assert_error {*not allowed from script*} {r eval {redis.call('internalauth.internalcommand')} 0} # Even after authenticating as an internal connection assert_equal {OK} [r debug mark-internal-client] assert_error {*not allowed from script*} {r eval {redis.call('internalauth.internalcommand')} 0} } } start_cluster 1 1 [list config_lines $modules] { set master [srv 0 client] set slave [srv -1 client] test {Setup master} { # Authenticate as an internal connection set reply [$master internalauth.getinternalsecret] assert_equal {OK} [$master auth "internal connection" $reply] } test {Slaves successfully execute internal commands from the replication link} { assert {[s -1 role] eq {slave}} wait_for_condition 1000 50 { [s -1 master_link_status] eq {up} } else { fail "Master link status is not up" } # Execute internal command in master, that will set `x` to `5`. assert_equal {OK} [$master internalauth.internal_rmcall_replicated set x 5] # Wait for replica to have the key $slave readonly wait_for_condition 1000 50 { [$slave exists x] eq "1" } else { fail "Test key was not replicated" } # See that the slave has the same value for `x`. assert_equal {5} [$slave get x] } } start_server {} { r module load $testmodule test {Internal commands are not reported in the monitor output for non-internal connections when unsuccessful} { set rd [redis_deferring_client] $rd monitor $rd read ; # Discard the OK assert_error {*unknown command*} {r internalauth.internalcommand} # Assert that the monitor output does not contain the internal command r ping assert_match {*ping*} [$rd read] $rd close } test {Internal commands are not reported in the monitor output for non-internal connections when successful} { # Authenticate as an internal connection assert_equal {OK} [r debug mark-internal-client] set rd [redis_deferring_client] $rd monitor $rd read ; # Discard the OK assert_equal {OK} [r internalauth.internalcommand] # Assert that the monitor output does not contain the internal command r ping assert_match {*ping*} [$rd read] $rd close } test {Internal commands are reported in the monitor output for internal connections} { set rd [redis_deferring_client] $rd debug mark-internal-client assert_equal {OK} [$rd read] $rd monitor $rd read ; # Discard the OK assert_equal {OK} [r internalauth.internalcommand] # Assert that the monitor output contains the internal command assert_match {*internalauth.internalcommand*} [$rd read] $rd close } test {Internal commands are reported in the slowlog} { # Set up slowlog to log all commands r config set slowlog-log-slower-than 0 # Execute an internal command r slowlog reset r internalauth.internalcommand # The slow-log should contain the internal command set log [r slowlog get 1] assert_match {*internalauth.internalcommand*} $log } test {Internal commands are reported in the latency report} { # The latency report should contain the internal command set report [r latency histogram internalauth.internalcommand] assert_match {*internalauth.internalcommand*} $report } test {Internal commands are reported in the command stats report} { # The INFO report should contain the internal command for both the internal # and non-internal connections. set report [r info commandstats] assert_match {*internalauth.internalcommand*} $report set report [r info latencystats] assert_match {*internalauth.internalcommand*} $report # Un-mark the connection as internal r debug mark-internal-client unmark assert_error {*unknown command*} {r internalauth.internalcommand} # We still expect to see the internal command in the report set report [r info commandstats] assert_match {*internalauth.internalcommand*} $report set report [r info latencystats] assert_match {*internalauth.internalcommand*} $report } } }