diff --git a/enos/modules/aws_target/main.tf b/enos/modules/aws_target/main.tf index 9a623f766d..2c11e11384 100644 --- a/enos/modules/aws_target/main.tf +++ b/enos/modules/aws_target/main.tf @@ -1,6 +1,14 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 +terraform { + required_providers { + enos = { + source = "app.terraform.io/hashicorp-qti/enos" + } + } +} + variable "vpc_id" {} variable "ami_id" {} variable "subnet_ids" {} @@ -18,6 +26,8 @@ variable "ingress_cidr" { default = ["10.0.0.0/8"] } +data "enos_environment" "current" {} + resource "aws_security_group" "boundary_target" { name_prefix = "boundary-target-sg" description = "SSH and boundary Traffic" @@ -31,6 +41,14 @@ resource "aws_security_group" "boundary_target" { cidr_blocks = var.ingress_cidr } + ingress { + description = "SSH to the instance" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = flatten([formatlist("%s/32", data.enos_environment.current.public_ipv4_addresses)]) + } + egress { from_port = 0 to_port = 0 diff --git a/testing/internal/e2e/tests/base/target_tcp_connect_scp_test.go b/testing/internal/e2e/tests/base/target_tcp_connect_scp_test.go new file mode 100644 index 0000000000..0ba2c0df60 --- /dev/null +++ b/testing/internal/e2e/tests/base/target_tcp_connect_scp_test.go @@ -0,0 +1,155 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package base_test + +import ( + "context" + "crypto/rand" + "fmt" + "io" + "os" + "strings" + "testing" + + "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" +) + +// TestCliTcpTargetConnectTargetAndScp connects to a target and validates scp +// operations by uploading and downloading a file to a host and verifies that +// checksums match +func TestCliTcpTargetConnectTargetAndScp(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) + newTargetId := boundary.CreateNewTargetCli( + t, + ctx, + newProjectId, + c.TargetPort, + target.WithAddress(c.TargetAddress), + ) + + // Start a session + ctxCancel, cancel := context.WithCancel(context.Background()) + port := "12345" + cmdChan := make(chan *e2e.CommandResult) + go func() { + t.Log("Starting session...") + cmdChan <- e2e.RunCommand(ctxCancel, "boundary", + e2e.WithArgs( + "connect", + "-target-id", newTargetId, + "-listen-port", port, + "-format", "json", + ), + ) + }() + t.Cleanup(cancel) + + boundary.WaitForSessionCli(t, ctx, newProjectId) + + // Create file to scp + testDir := t.TempDir() + t.Log("Creating text file...") + fileSource := fmt.Sprintf("%s/%s_src.txt", testDir, t.Name()) + f, err := os.Create(fileSource) + require.NoError(t, err) + io.CopyN(f, rand.Reader, 256000) + + fi, err := f.Stat() + require.NoError(t, err) + require.Greater(t, fi.Size(), int64(16000), "Generated file is not larger than 16K") + + output := e2e.RunCommand(ctx, "cksum", e2e.WithArgs(fileSource)) + require.NoError(t, output.Err, string(output.Stderr)) + parts := strings.Fields(string(output.Stdout)) + cksumSource := parts[0] + + // Copy file to host + t.Log("Copying file to host...") + fileUpload := fmt.Sprintf("/tmp/%s_upload.txt", t.Name()) + output = e2e.RunCommand(ctx, "scp", + e2e.WithArgs( + "-i", c.TargetSshKeyPath, + "-P", port, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + fileSource, + fmt.Sprintf("%s@localhost:%s", c.TargetSshUser, fileUpload), + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Validate checksum + 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 + fmt.Sprintf("cksum %s", fileUpload), + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + parts = strings.Fields(string(output.Stdout)) + cksumUpload := parts[0] + require.Equal(t, cksumSource, cksumUpload, "Checksum of uploaded file does not match source") + + // Read file on host + t.Log("Reading file on host...") + 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 + fmt.Sprintf("cat %s", fileUpload), + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Download file from host + t.Log("Downloading file from host...") + fileDownload := fmt.Sprintf("%s/%s_download.txt", testDir, t.Name()) + output = e2e.RunCommand(ctx, "scp", + e2e.WithArgs( + "-i", c.TargetSshKeyPath, + "-P", port, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + fmt.Sprintf("%s@localhost:%s", c.TargetSshUser, fileUpload), + fileDownload, + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Validate checksum + output = e2e.RunCommand(ctx, "cksum", e2e.WithArgs(fileDownload)) + require.NoError(t, output.Err, string(output.Stderr)) + parts = strings.Fields(string(output.Stdout)) + cksumDownload := parts[0] + require.Equal(t, cksumSource, cksumDownload, "Checksum of downloaded file does not match source") +}