From 2439d7471afb2f588d88e0dd06a6e41be8645fec Mon Sep 17 00:00:00 2001 From: Louis Laporte Date: Tue, 2 Apr 2024 12:17:15 +0000 Subject: [PATCH 1/2] feat: add option to wait for a specific port open --- bin/shell/osh.pl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bin/shell/osh.pl b/bin/shell/osh.pl index 7d7da2b1f..71b8fb44b 100755 --- a/bin/shell/osh.pl +++ b/bin/shell/osh.pl @@ -371,6 +371,7 @@ sub main_exit { "interactive|i" => \my $interactive, "netconf" => \my $netconf, "wait" => \my $wait, + "wait-port=i" => \my $wait_port, "ssh-as=s" => \my $sshAs, "use-key=s" => \my $useKey, "kbd-interactive" => \my $userKbdInteractive, @@ -1504,6 +1505,25 @@ sub main_exit { } } +# if --wait-port is specified, we wait for the host specific port to be open before connecting +if ($wait_port) { + my $startedat = time(); + osh_info "Testing $host port $wait_port, will connect as soon as it's open..."; + while (1) { + my @testportcmd = qw{ nc -w 1 -z -v -n }; + push @testportcmd, $host, $wait_port; + + my $fnretexec = OVH::Bastion::execute(cmd => \@testportcmd, noisy_stdout => 1, noisy_stderr => 1); + $fnretexec or exit(OVH::Bastion::EXIT_EXEC_FAILED); + if ($fnretexec->value->{'sysret'} == 0) { + osh_info "Open after waiting for " . (time() - $startedat) . " seconds, connecting..."; + sleep 2 if (time() > $startedat + 1); # so that ssh has the time to startup... hopefully + last; + } + sleep 1; + } +} + my $logret = OVH::Bastion::log_access_insert( account => $self, cmdtype => $telnet ? 'telnet' : 'ssh', @@ -2001,6 +2021,7 @@ sub long_help { --always-escape Bypass config and force the bugged behavior of old bastions for REMOTE_COMMAND escaping. Don't use. --never-escape Bypass config and force the new behavior of new bastions for REMOTE_COMMAND escaping. Don't use. --wait Ping the host before connecting to it (useful to ssh just after a reboot!) + --wait-port Test the host specific port to be open before connecting to it --long-help Print this [REMOTE_COMMAND] From e3e4c5584ac34450713cb2d4c2b1f94ef69b4c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Fri, 5 Apr 2024 13:40:59 +0000 Subject: [PATCH 2/2] feat: replace --wait by a tcp-based connection try --- bin/shell/osh.pl | 46 +++++++++----------------- tests/functional/tests.d/350-groups.sh | 27 +++++++++++---- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/bin/shell/osh.pl b/bin/shell/osh.pl index 71b8fb44b..d978f6ea7 100755 --- a/bin/shell/osh.pl +++ b/bin/shell/osh.pl @@ -371,7 +371,6 @@ sub main_exit { "interactive|i" => \my $interactive, "netconf" => \my $netconf, "wait" => \my $wait, - "wait-port=i" => \my $wait_port, "ssh-as=s" => \my $sshAs, "use-key=s" => \my $useKey, "kbd-interactive" => \my $userKbdInteractive, @@ -1488,39 +1487,27 @@ sub main_exit { # if --wait is specified, we wait for the host to be alive before connecting if ($wait) { + require IO::Socket::INET; my $startedat = time(); - osh_info "Pinging $host, will connect as soon as it's alive..."; - while (1) { - my @pingcmd = qw{ fping -- }; - push @pingcmd, $host; - - my $fnretexec = OVH::Bastion::execute(cmd => \@pingcmd, noisy_stdout => 1, noisy_stderr => 1); - $fnretexec or exit(OVH::Bastion::EXIT_EXEC_FAILED); - if ($fnretexec->value->{'sysret'} == 0) { + my $loops = 0; + osh_info "Waiting for port $port to be open on $host before attempting to connect..."; + while ($loops < 3600) { # can be up to 2h (socket timeout + sleep 1) + my $Sock = IO::Socket::INET->new( + Proto => 'tcp', + Timeout => 1, + PeerAddr => $ip, + PeerPort => $port, + ); + if ($Sock) { osh_info "Alive after waiting for " . (time() - $startedat) . " seconds, connecting..."; - sleep 2 if (time() > $startedat + 1); # so that ssh has the time to startup... hopefully + $Sock->close(); last; } - sleep 1; - } -} - -# if --wait-port is specified, we wait for the host specific port to be open before connecting -if ($wait_port) { - my $startedat = time(); - osh_info "Testing $host port $wait_port, will connect as soon as it's open..."; - while (1) { - my @testportcmd = qw{ nc -w 1 -z -v -n }; - push @testportcmd, $host, $wait_port; - - my $fnretexec = OVH::Bastion::execute(cmd => \@testportcmd, noisy_stdout => 1, noisy_stderr => 1); - $fnretexec or exit(OVH::Bastion::EXIT_EXEC_FAILED); - if ($fnretexec->value->{'sysret'} == 0) { - osh_info "Open after waiting for " . (time() - $startedat) . " seconds, connecting..."; - sleep 2 if (time() > $startedat + 1); # so that ssh has the time to startup... hopefully - last; + sleep 1; # to avoid looping too fast if the failure is immediate and not a timeout (i.e. port closed) + $loops++; + if ($loops % 5 == 0) { + osh_info("Still trying to connect to $host:$port after " . (time() - $startedat) . " seconds..."); } - sleep 1; } } @@ -2021,7 +2008,6 @@ sub long_help { --always-escape Bypass config and force the bugged behavior of old bastions for REMOTE_COMMAND escaping. Don't use. --never-escape Bypass config and force the new behavior of new bastions for REMOTE_COMMAND escaping. Don't use. --wait Ping the host before connecting to it (useful to ssh just after a reboot!) - --wait-port Test the host specific port to be open before connecting to it --long-help Print this [REMOTE_COMMAND] diff --git a/tests/functional/tests.d/350-groups.sh b/tests/functional/tests.d/350-groups.sh index 92ce132d6..4cc19d57a 100644 --- a/tests/functional/tests.d/350-groups.sh +++ b/tests/functional/tests.d/350-groups.sh @@ -141,28 +141,41 @@ EOS # now that we have several keys, take the opportunity to test force-key - plgfail a1_add_access_force_key_and_pwd_g1 $a1 --osh groupAddServer --host 127.1.2.3 --user-any --port-any --force --force-password '$1$2$3456' --force-key "$key1fp" --group $group1 + plgfail a1_add_access_force_key_and_pwd_g1 $a1 --osh groupAddServer --host 127.0.0.5 --user-any --port-any --force --force-password '$1$2$3456' --force-key "$key1fp" --group $group1 json .error_code ERR_INCOMPATIBLE_PARAMETERS - success a1_add_access_force_key_g1 $a1 --osh groupAddServer --host 127.1.2.3 --user 'ar@base' --port-any --force --force-key "$key1fp" --group $group1 + success a1_add_access_force_key_g1 $a1 --osh groupAddServer --host 127.0.0.5 --user 'ar@base' --port-any --force --force-key "$key1fp" --group $group1 json .value.user 'ar@base' success a1_list_servers_check_force_key_g1 $a1 --osh groupListServers --group $group1 - json '.value|.[]|select(.ip=="127.1.2.3")|.forceKey' "$key1fp" - json '.value|.[]|select(.ip=="127.1.2.3")|.user' "ar@base" + json '.value|.[]|select(.ip=="127.0.0.5")|.forceKey' "$key1fp" + json '.value|.[]|select(.ip=="127.0.0.5")|.user' "ar@base" - # try to use the force key + # try to use the force key AND --wait - run a1_connect_g1_with_forcekey $a1 ar@base@127.1.2.3 -- false + run a1_connect_g1_with_forcekey_and_wait $a1 --wait ar@base@127.0.0.5 -- false contain 'Connecting...' contain 'FORCED IN ACL' contain "$key1fp" nocontain "$key0fp" + contain "Waiting for port 22 to be open on " + contain REGEX "Alive after waiting for [0-9] seconds" - success a1_remove_forcekey_acl_g1 $a1 --osh groupDelServer --host 127.1.2.3 --user 'ar@base' --port-any --group $group1 + success a1_remove_forcekey_acl_g1 $a1 --osh groupDelServer --host 127.0.0.5 --user 'ar@base' --port-any --group $group1 # /force-key + success a1_add_non_routable_ip $a1 --osh groupAddServer --host 192.0.2.0 --user-any --port-any --force --group $group1 + + run a1_ssh_wait $a1 --wait 192.0.2.0 + retvalshouldbe 124 # timeout + contain "Waiting for port 22 to be open on 192.0.2.0" + contain REGEX "Still trying to connect to 192.0.2.0:22 after 1[0-9] seconds" + + success a1_remove_non_routable_ip $a1 --osh groupDelServer --host 192.0.2.0 --user-any --port-any --group $group1 + + # test --alive + run a0_del_key_g1 $a0 --osh groupDelEgressKey --group $group1 --id $key1id retvalshouldbe 106 json .command null .error_code KO_RESTRICTED_COMMAND .value null