From 2a17562195a14da863a52d6440bef2574bc15e30 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Wed, 14 Feb 2024 16:21:11 +0000 Subject: [PATCH 1/3] backport of commit 353fb177016e55c7684faafa664eaabb35958da6 --- testing/internal/e2e/boundary/target.go | 3 + ...et_tcp_connect_session_max_seconds_test.go | 83 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go diff --git a/testing/internal/e2e/boundary/target.go b/testing/internal/e2e/boundary/target.go index f971306ae7..09ac404eb9 100644 --- a/testing/internal/e2e/boundary/target.go +++ b/testing/internal/e2e/boundary/target.go @@ -90,6 +90,9 @@ func CreateNewTargetCli(t testing.TB, ctx context.Context, projectId string, def if opts.WithSessionConnectionLimit != 0 { args = append(args, "-session-connection-limit", fmt.Sprintf("%d", opts.WithSessionConnectionLimit)) } + if opts.WithSessionMaxSeconds != 0 { + args = append(args, "-session-max-seconds", fmt.Sprintf("%d", opts.WithSessionMaxSeconds)) + } output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("targets", "create"), diff --git a/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go new file mode 100644 index 0000000000..7e4e9872ce --- /dev/null +++ b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package base_test + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/session" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/stretchr/testify/require" +) + +// TestCliTcpTargetConnectTargetWithSessionMaxSeconds connects to a target that +// has session max seconds defined. An error should return when trying to +// connect to the target after the time limit has been reached. +func TestCliTcpTargetConnectTargetWithSessionMaxSeconds(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadTestConfig() + require.NoError(t, err) + + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + newOrgId := boundary.CreateNewOrgCli(t, ctx) + t.Cleanup(func() { + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", newOrgId)) + require.NoError(t, output.Err, string(output.Stderr)) + }) + newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) + sessionMaxSeconds := 7 + newTargetId := boundary.CreateNewTargetCli( + t, + ctx, + newProjectId, + c.TargetPort, + target.WithAddress(c.TargetAddress), + target.WithSessionMaxSeconds(uint32(sessionMaxSeconds)), + ) + + // Start a long-running session + var start time.Time + ctxCancel, cancel := context.WithCancel(context.Background()) + sessionChannel := make(chan *e2e.CommandResult) + go func() { + start = time.Now() + sessionChannel <- e2e.RunCommand(ctxCancel, "boundary", + e2e.WithArgs( + "connect", + "-target-id", newTargetId, + "-exec", "/usr/bin/ssh", "--", + "-l", c.TargetSshUser, + "-i", c.TargetSshKeyPath, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + "-p", "{{boundary.port}}", // this is provided by boundary + "{{boundary.ip}}", + "hostname -i; sleep 60", + ), + ) + }() + t.Cleanup(cancel) + s := boundary.WaitForSessionCli(t, ctx, newProjectId) + boundary.WaitForSessionStatusCli(t, ctx, s.Id, session.StatusActive.String()) + + // Check that session was closed once time limit is reached + select { + case output := <-sessionChannel: + require.Equal(t, 255, output.ExitCode, string(output.Stdout), string(output.Stderr)) + + // Ensure that the session did not run for longer than the time limit + // (plus a small buffer) + require.Less(t, time.Since(start).Seconds(), float64(sessionMaxSeconds+1)) + case <-time.After(time.Second * time.Duration(sessionMaxSeconds+5)): + t.Fatal("Timed out waiting for session command to exit") + } +} From ba3149599287ac82dc916a3ce2a410ffa90865a1 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Thu, 15 Feb 2024 01:00:51 +0000 Subject: [PATCH 2/3] backport of commit 4a91aac06c9979154bc33aeb0af72ce7bfd77fec --- ...et_tcp_connect_session_max_seconds_test.go | 84 ++++++++++++++++++- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go index 7e4e9872ce..dfc266f375 100644 --- a/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go +++ b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go @@ -15,10 +15,10 @@ import ( "github.com/stretchr/testify/require" ) -// TestCliTcpTargetConnectTargetWithSessionMaxSeconds connects to a target that -// has session max seconds defined. An error should return when trying to -// connect to the target after the time limit has been reached. -func TestCliTcpTargetConnectTargetWithSessionMaxSeconds(t *testing.T) { +// TestCliTcpTargetConnectTargetWithSessionMaxSecondsTearDown connects to a +// target that has session max seconds defined and executes a long-running +// script. The session should end once the time limit has reached. +func TestCliTcpTargetConnectTargetWithSessionMaxSecondsTearDown(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadTestConfig() require.NoError(t, err) @@ -81,3 +81,79 @@ func TestCliTcpTargetConnectTargetWithSessionMaxSeconds(t *testing.T) { t.Fatal("Timed out waiting for session command to exit") } } + +// TestCliTcpTargetConnectTargetWithSessionMaxSecondsRejectNew connects to a +// target that has session max seconds defined. An error should return when +// trying to connect to the target after the time limit has been reached. +func TestCliTcpTargetConnectTargetWithSessionMaxSecondsRejectNew(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadTestConfig() + require.NoError(t, err) + + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + newOrgId := boundary.CreateNewOrgCli(t, ctx) + t.Cleanup(func() { + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", newOrgId)) + require.NoError(t, output.Err, string(output.Stderr)) + }) + newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) + sessionMaxSeconds := 7 + newTargetId := boundary.CreateNewTargetCli( + t, + ctx, + newProjectId, + c.TargetPort, + target.WithAddress(c.TargetAddress), + target.WithSessionMaxSeconds(uint32(sessionMaxSeconds)), + ) + + // Start a session + var start time.Time + ctxCancel, cancel := context.WithCancel(context.Background()) + port := "12345" + sessionChannel := make(chan *e2e.CommandResult) + go func() { + start = time.Now() + t.Logf("Starting session... %s", start) + sessionChannel <- e2e.RunCommand(ctxCancel, "boundary", + e2e.WithArgs( + "connect", + "-target-id", newTargetId, + "-listen-port", port, + "-format", "json", + ), + ) + }() + t.Cleanup(cancel) + boundary.WaitForSessionCli(t, ctx, newProjectId) + + // Start connections. Expect an error once the time limit is reached + t.Log("Creating connections...") + for { + t.Log(time.Now()) + output := e2e.RunCommand(ctx, "ssh", + e2e.WithArgs( + "localhost", + "-p", port, + "-l", c.TargetSshUser, + "-i", c.TargetSshKeyPath, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + "hostname -i;", + ), + ) + + if output.Err != nil { + break + } + + // Ensure that time limit has not been reached yet + // (plus a small buffer) + require.Less(t, time.Since(start).Seconds(), float64(sessionMaxSeconds+1)) + time.Sleep(time.Second) + } +} From 8a71bf79c7a30de2f3c089424022deeb12e0b250 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Thu, 15 Feb 2024 01:12:34 +0000 Subject: [PATCH 3/3] backport of commit f7dd911a47722465e3be864ae2b7df81cf4b17b3 --- .../target_tcp_connect_session_max_seconds_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go index dfc266f375..643d6ea37b 100644 --- a/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go +++ b/testing/internal/e2e/tests/base/target_tcp_connect_session_max_seconds_test.go @@ -76,7 +76,9 @@ func TestCliTcpTargetConnectTargetWithSessionMaxSecondsTearDown(t *testing.T) { // Ensure that the session did not run for longer than the time limit // (plus a small buffer) - require.Less(t, time.Since(start).Seconds(), float64(sessionMaxSeconds+1)) + end := time.Since(start).Seconds() + require.Less(t, end, float64(sessionMaxSeconds+1)) + require.Greater(t, end, float64(sessionMaxSeconds-1)) case <-time.After(time.Second * time.Duration(sessionMaxSeconds+5)): t.Fatal("Timed out waiting for session command to exit") } @@ -132,6 +134,7 @@ func TestCliTcpTargetConnectTargetWithSessionMaxSecondsRejectNew(t *testing.T) { // Start connections. Expect an error once the time limit is reached t.Log("Creating connections...") + var end time.Time for { t.Log(time.Now()) output := e2e.RunCommand(ctx, "ssh", @@ -148,12 +151,17 @@ func TestCliTcpTargetConnectTargetWithSessionMaxSecondsRejectNew(t *testing.T) { ) if output.Err != nil { + end = time.Now() break } // Ensure that time limit has not been reached yet - // (plus a small buffer) require.Less(t, time.Since(start).Seconds(), float64(sessionMaxSeconds+1)) time.Sleep(time.Second) } + + // Ensure that the session did not run for longer than the time limit + diff := end.Sub(start).Seconds() + require.Less(t, diff, float64(sessionMaxSeconds+1)) + require.Greater(t, diff, float64(sessionMaxSeconds-1)) }