From: Bill Erickson Date: Mon, 1 May 2023 15:01:51 +0000 (-0400) Subject: LP2017941 Generate Redis passwords at build time X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=3997099a89251460a99e8c3ef525f40f8fb722d6;p=working%2FOpenSRF.git LP2017941 Generate Redis passwords at build time Generate passwords as UUIDs at build time. Apply a password the the Redis 'default' account for security. Make Perl %connections cache per-Client, not global. Signed-off-by: Bill Erickson --- diff --git a/.gitignore b/.gitignore index 181b1d1..7203d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ config.sub configure depcomp doc/dokuwiki-doc-stubber.pl +examples/redis-accounts.example.txt examples/math_bench.pl examples/math_client.py examples/multisession-test.pl diff --git a/README b/README index f3e7d9e..e7fbf6e 100644 --- a/README +++ b/README @@ -226,7 +226,7 @@ save "" + 3. Restart the Redis server to make the changes take effect: + -.Starting ejabberd +.Starting Redis [source, bash] --------------------------------------------------------------------------- systemctl start redis-server @@ -288,14 +288,25 @@ cp redis-accounts.example.txt redis-accounts.txt Creating Redis Accounts -------------------------------------- - Before starting services, it's necessary to create Redis accounts. Issue the following command as the *opensrf* Linux account: - ++ [source, bash] --------------------------------------------------------------------------- osrf_control --reset-message-bus --------------------------------------------------------------------------- ++ +Accessing the Redis Command Line +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The script which creates Redis OpenSRF accounts also applies a password +to the 'default' Redis account for security. To access the Redis +command line with full privileges, use the password for the 'default' +user from the SYSCONFDIR/redis-accounts.txt file. For example: ++ +[source,bash] +--------------------------------------------------------------------------- +REDISCLI_AUTH=f0d2ebcc-5a52-49e4-a910-a515144b4141 redis-cli +--------------------------------------------------------------------------- Starting and stopping OpenSRF services -------------------------------------- diff --git a/bin/opensrf-perl.pl.in b/bin/opensrf-perl.pl.in index 9103b8c..8e0115b 100755 --- a/bin/opensrf-perl.pl.in +++ b/bin/opensrf-perl.pl.in @@ -621,16 +621,47 @@ sub do_reset_message_bus { my $routers = $conf->bootstrap->routers; - # TODO pull logins for all clients in the conf, including - # gateway and router. - for my $router (@{$conf->bootstrap->routers}) { + my $bus_pass = `grep 'ACL SETUSER default on >' $opt_bus_accounts | cut -d'>' -f2`; + + chomp($bus_pass); + + die "No password for Redis 'default' account found in $opt_bus_accounts\n" + unless $bus_pass; + # Redis prefers the password be passed via ENV. + $ENV{REDISCLI_AUTH} = $bus_pass; + + # Apply the bus accounts to all of our domains. + for my $router (@{$conf->bootstrap->routers}) { my $domain = ref $router ? $router->{domain} : $router; my $port = $conf->bootstrap->port; + # The first time this script runs after installing / rebooting Redis, + # the 'default' account will have no password. Subsequent logins + # will use the password defined in our redis-accounts file. See if + # we can figure where we are... + my $login = `echo "exit" | redis-cli -h $domain -p $port 2>&1`; + + if ($login =~ /AUTH failed/) { + # Login failed. Clear the password. + delete $ENV{REDISCLI_AUTH}; + } else { + # Multiple OpenSRF domains may run on the same Redis instance. + # If so, make sure subsequent runs on the same redis instance + # use the just-applied password. In this case, our $login + # var above will be empty, becuase Redis will think we are + # trying to login with no authentication, and will later fail + # when we try to perform actions that are not allowed. + $ENV{REDISCLI_AUTH} = $bus_pass; + } + msg("Resetting bus accounts for domain $domain"); - system("cat $opt_bus_accounts | redis-cli -h $domain -p $port > /dev/null"); + # Grep out some noise. Avoid piping to /dev/null so we can + # see failures. + my $command = "redis-cli -h $domain -p $port | grep -v OK | grep -v ^1"; + + system("cat $opt_bus_accounts | $command"); } } diff --git a/configure.ac b/configure.ac index 9bd31b4..542ea75 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,21 @@ AC_SUBST([PID_DIR]) AC_SUBST(prefix) AC_SUBST(bindir) +OPENSRF_BUS_PASS=$(cat /proc/sys/kernel/random/uuid) +GATEWAY_BUS_PASS=$(cat /proc/sys/kernel/random/uuid) +ROUTER_BUS_PASS=$(cat /proc/sys/kernel/random/uuid) +DEFAULT_BUS_PASS=$(cat /proc/sys/kernel/random/uuid) + +AC_DEFINE_UNQUOTED([OPENSRF_BUS_PASS], ["$OPENSRF_BUS_PASS"], [opensrf bus password]) +AC_DEFINE_UNQUOTED([GATEWAY_BUS_PASS], ["$GATEWAY_BUS_PASS"], [gateway bus password]) +AC_DEFINE_UNQUOTED([ROUTER_BUS_PASS], ["$ROUTER_BUS_PASS"], [router bus password]) +AC_DEFINE_UNQUOTED([DEFAULT_BUS_PASS], ["$DEFAULT_BUS_PASS"], [admin bus password]) + +AC_SUBST([OPENSRF_BUS_PASS]) +AC_SUBST([GATEWAY_BUS_PASS]) +AC_SUBST([ROUTER_BUS_PASS]) +AC_SUBST([DEFAULT_BUS_PASS]) + #------------------------------- # Installation options #------------------------------- @@ -321,6 +336,7 @@ if test "x$OSRF_INSTALL_CORE" = "xtrue"; then #------------------------------------ AC_CONFIG_FILES([doc/dokuwiki-doc-stubber.pl + examples/redis-accounts.example.txt examples/math_bench.pl examples/multisession-test.pl src/c-apps/Makefile diff --git a/examples/redis-accounts.example.txt b/examples/redis-accounts.example.txt deleted file mode 100644 index e2ceeaa..0000000 --- a/examples/redis-accounts.example.txt +++ /dev/null @@ -1,34 +0,0 @@ - -SET comment "opensrf clients can perform all opensrf-level actions" -SET COMMENT "opensrf accounts send requets to opensrf:router:* queues" -SET COMMENT "opensrf accounts send replies to opensrf:client:* queues" -SET COMMENT "opensrf accounts lpop requests from their opensrf:servivce: queue." -SET COMMENT "TODO: separate Listener vs Drone accounts to prevent Drones / standalone clients from accessing opensrf:service:*" - -ACL SETUSER opensrf reset -ACL SETUSER opensrf on >password -ACL SETUSER opensrf -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:service:* ~opensrf:client:* - -SET comment "routers lpop requests from their own opensrf:router:* queues" -SET comment "routers send requests to opensrf:service:* queues" -SET comment "routers send replies to opensrf:client:* queues" - -ACL SETUSER router reset -ACL SETUSER router on >password -ACL SETUSER router -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:service:* ~opensrf:client:* - -SET comment "gateway accounts send request to opensrf:router:* queues" -SET comment "gateway accounts send subsequent, stateful requests to opensrf:client:* queues" - -ACL SETUSER gateway reset -ACL SETUSER gateway on >password -ACL SETUSER gateway -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:client:* - -SET comment "admin can do anything" - -ACL SETUSER admin reset -ACL SETUSER admin on >password -ACL SETUSER admin +@all ~* - -DEL comment - diff --git a/examples/redis-accounts.example.txt.in b/examples/redis-accounts.example.txt.in new file mode 100644 index 0000000..4d5a6e9 --- /dev/null +++ b/examples/redis-accounts.example.txt.in @@ -0,0 +1,31 @@ + +SET comment "opensrf clients can perform all opensrf-level actions" +SET COMMENT "opensrf accounts send requets to opensrf:router:* queues" +SET COMMENT "opensrf accounts send replies to opensrf:client:* queues" +SET COMMENT "opensrf accounts lpop requests from their opensrf:servivce: queue." +SET COMMENT "TODO: separate Listener vs Drone accounts to prevent Drones / standalone clients from accessing opensrf:service:*" + +ACL SETUSER opensrf reset +ACL SETUSER opensrf on >@OPENSRF_BUS_PASS@ +ACL SETUSER opensrf -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:service:* ~opensrf:client:* + +SET comment "routers lpop requests from their own opensrf:router:* queues" +SET comment "routers send requests to opensrf:service:* queues" +SET comment "routers send replies to opensrf:client:* queues" + +ACL SETUSER router reset +ACL SETUSER router on >@ROUTER_BUS_PASS@ +ACL SETUSER router -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:service:* ~opensrf:client:* + +SET comment "gateway accounts send request to opensrf:router:* queues" +SET comment "gateway accounts send subsequent, stateful requests to opensrf:client:* queues" + +ACL SETUSER gateway reset +ACL SETUSER gateway on >@GATEWAY_BUS_PASS@ +ACL SETUSER gateway -@all +lpop +blpop +rpush +del ~opensrf:router:* ~opensrf:client:* + +SET comment "default can do anything" +SET comment "set default password last so our logged-in account does not break mid-script" + +ACL SETUSER default resetpass +ACL SETUSER default on >@DEFAULT_BUS_PASS@ diff --git a/src/perl/lib/OpenSRF/Transport/Redis/Client.pm b/src/perl/lib/OpenSRF/Transport/Redis/Client.pm index 53ac681..7220083 100644 --- a/src/perl/lib/OpenSRF/Transport/Redis/Client.pm +++ b/src/perl/lib/OpenSRF/Transport/Redis/Client.pm @@ -9,9 +9,6 @@ use OpenSRF::Transport; use OpenSRF::Transport::Redis::Message; use OpenSRF::Transport::Redis::BusConnection; -# Map of bus domain names to bus connections. -my %connections; - # There will only be one Client per process, but each client may # have multiple connections. my $_singleton; @@ -22,7 +19,10 @@ sub new { return $_singleton if $_singleton && !$force; - my $self = {service => $service}; + my $self = { + service => $service, + connections => {}, + }; bless($self, $class); @@ -70,7 +70,7 @@ sub add_connection { ); $connection->set_address(); - $connections{$domain} = $connection; + $self->{connections}->{$domain} = $connection; $connection->connect; @@ -80,7 +80,7 @@ sub add_connection { sub get_connection { my ($self, $domain) = @_; - my $con = $connections{$domain}; + my $con = $self->{connections}->{$domain}; return $con if $con; @@ -115,18 +115,18 @@ sub primary_domain { sub primary_connection { my $self = shift; - return $connections{$self->primary_domain}; + return $self->{connections}->{$self->primary_domain}; } sub disconnect { my ($self, $domain) = @_; - for my $domain (keys %connections) { - my $con = $connections{$domain}; + for my $domain (keys %{$self->{connections}}) { + my $con = $self->{connections}->{$domain}; $con->disconnect($self->primary_domain eq $domain); - delete $connections{$domain}; } + $self->{connections} = {}; $_singleton = undef; }