diff --git a/.github/workflows/enos-run.yml b/.github/workflows/enos-run.yml index 1ffff063c..e6dfbcb3e 100644 --- a/.github/workflows/enos-run.yml +++ b/.github/workflows/enos-run.yml @@ -26,9 +26,9 @@ jobs: matrix: include: - filter: 'integration test:cli_ui' - - filter: 'e2e_credential_vault' - - filter: 'e2e_host_aws' - - filter: 'e2e_host_static' + - filter: 'e2e_aws' + - filter: 'e2e_static' + - filter: 'e2e_static_with_vault' runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }} @@ -123,7 +123,7 @@ jobs: run: | wget https://releases.hashicorp.com/vault/1.11.4/vault_1.11.4_linux_amd64.zip -O /tmp/test-deps/vault.zip - name: Install Vault for integration testing - if: matrix.filter == 'e2e_credential_vault' + if: matrix.filter == 'e2e_static_with_vault' run: | unzip /tmp/test-deps/vault.zip -d /usr/local/bin - name: Download Linux AMD64 Boundary bundle diff --git a/enos/enos-scenario-e2e-host-aws.hcl b/enos/enos-scenario-e2e-aws.hcl similarity index 98% rename from enos/enos-scenario-e2e-host-aws.hcl rename to enos/enos-scenario-e2e-aws.hcl index 384145096..4687f7811 100644 --- a/enos/enos-scenario-e2e-host-aws.hcl +++ b/enos/enos-scenario-e2e-aws.hcl @@ -1,5 +1,5 @@ # This scenario requires access to the boundary team's test AWS account -scenario "e2e_host_aws" { +scenario "e2e_aws" { terraform_cli = terraform_cli.default terraform = terraform.default providers = [ @@ -168,7 +168,7 @@ scenario "e2e_host_aws" { ] variables { - test_package = "github.com/hashicorp/boundary/testing/internal/e2e/host/aws" + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/aws" alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr auth_method_id = step.create_boundary_cluster.auth_method_id auth_login_name = step.create_boundary_cluster.auth_login_name diff --git a/enos/enos-scenario-e2e-credential-vault.hcl b/enos/enos-scenario-e2e-static-with-vault.hcl similarity index 98% rename from enos/enos-scenario-e2e-credential-vault.hcl rename to enos/enos-scenario-e2e-static-with-vault.hcl index 9e9da08f2..3ae279899 100644 --- a/enos/enos-scenario-e2e-credential-vault.hcl +++ b/enos/enos-scenario-e2e-static-with-vault.hcl @@ -1,4 +1,4 @@ -scenario "e2e_credential_vault" { +scenario "e2e_static_with_vault" { terraform_cli = terraform_cli.default terraform = terraform.default providers = [ @@ -124,7 +124,7 @@ scenario "e2e_credential_vault" { ] variables { - test_package = "github.com/hashicorp/boundary/testing/internal/e2e/credential/vault" + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/static_with_vault" alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr auth_method_id = step.create_boundary_cluster.auth_method_id auth_login_name = step.create_boundary_cluster.auth_login_name diff --git a/enos/enos-scenario-e2e-host-static.hcl b/enos/enos-scenario-e2e-static.hcl similarity index 98% rename from enos/enos-scenario-e2e-host-static.hcl rename to enos/enos-scenario-e2e-static.hcl index 77db938c2..38f19aa05 100644 --- a/enos/enos-scenario-e2e-host-static.hcl +++ b/enos/enos-scenario-e2e-static.hcl @@ -1,4 +1,4 @@ -scenario "e2e_host_static" { +scenario "e2e_static" { terraform_cli = terraform_cli.default terraform = terraform.default providers = [ @@ -101,7 +101,7 @@ scenario "e2e_host_static" { ] variables { - test_package = "github.com/hashicorp/boundary/testing/internal/e2e/host/static" + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/static" alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr auth_method_id = step.create_boundary_cluster.auth_method_id auth_login_name = step.create_boundary_cluster.auth_login_name diff --git a/testing/internal/e2e/host/static/static_credential_store_test.go b/testing/internal/e2e/host/static/static_credential_store_test.go deleted file mode 100644 index 43c6dae35..000000000 --- a/testing/internal/e2e/host/static/static_credential_store_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package static_test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/hashicorp/boundary/api/credentials" - "github.com/hashicorp/boundary/testing/internal/e2e" - "github.com/hashicorp/boundary/testing/internal/e2e/boundary" - "github.com/stretchr/testify/require" -) - -// TestStaticCredentialStoreCli validates various credential-store operations using the cli -func TestStaticCredentialStoreCli(t *testing.T) { - e2e.MaybeSkipTest(t) - c, err := loadConfig() - require.NoError(t, err) - - ctx := context.Background() - boundary.AuthenticateAdminCli(t, ctx) - newOrgId := boundary.CreateNewOrgCli(t, ctx) - newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) - newCredentialStoreId := boundary.CreateNewCredentialStoreStaticCli(t, ctx, newProjectId) - - // Create ssh key credentials - output := e2e.RunCommand(ctx, "boundary", - e2e.WithArgs( - "credentials", "create", "ssh-private-key", - "-credential-store-id", newCredentialStoreId, - "-username", c.TargetSshUser, - "-private-key", "file://"+c.TargetSshKeyPath, - "-format", "json", - ), - ) - require.NoError(t, output.Err, string(output.Stderr)) - var keyCredentialsResult credentials.CredentialCreateResult - err = json.Unmarshal(output.Stdout, &keyCredentialsResult) - require.NoError(t, err) - keyCredentialsId := keyCredentialsResult.Item.Id - t.Logf("Created SSH Private Key Credentials: %s", keyCredentialsId) - - // Create username/password credentials - output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs( - "credentials", "create", "username-password", - "-credential-store-id", newCredentialStoreId, - "-username", c.TargetSshUser, - "-password", "env://E2E_CREDENTIALS_PASSWORD", - "-format", "json", - ), - e2e.WithEnv("E2E_CREDENTIALS_PASSWORD", "password"), - ) - require.NoError(t, output.Err, string(output.Stderr)) - var pwCredentialsResult credentials.CredentialCreateResult - err = json.Unmarshal(output.Stdout, &pwCredentialsResult) - require.NoError(t, err) - pwCredentialsId := pwCredentialsResult.Item.Id - t.Logf("Created Username/Password Credentials: %s", pwCredentialsId) - - // Delete credential store - output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs("credential-stores", "delete", "-id", newCredentialStoreId), - ) - require.NoError(t, output.Err, string(output.Stderr)) - err = backoff.RetryNotify( - func() error { - output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs("credential-stores", "read", "-id", newCredentialStoreId, "-format", "json"), - ) - if output.Err == nil { - return fmt.Errorf("Deleted credential can still be read: '%s'", output.Stdout) - } - - var response e2e.CliError - err = json.Unmarshal(output.Stderr, &response) - require.NoError(t, err) - statusCode := response.Status - if statusCode != 404 { - return backoff.Permanent( - fmt.Errorf("Command did not return expected status code. Expected: 404, Actual: %d", statusCode), - ) - } - - return nil - }, - backoff.WithMaxRetries(backoff.NewConstantBackOff(3*time.Second), 5), - func(err error, td time.Duration) { - t.Logf("%s. Retrying...", err.Error()) - }, - ) - require.NoError(t, err) - t.Logf("Successfully deleted credential store") -} diff --git a/testing/internal/e2e/host/aws/dynamichostcatalog_test.go b/testing/internal/e2e/tests/aws/dynamichostcatalog_test.go similarity index 98% rename from testing/internal/e2e/host/aws/dynamichostcatalog_test.go rename to testing/internal/e2e/tests/aws/dynamichostcatalog_test.go index 139c4cc09..19512980e 100644 --- a/testing/internal/e2e/host/aws/dynamichostcatalog_test.go +++ b/testing/internal/e2e/tests/aws/dynamichostcatalog_test.go @@ -42,10 +42,10 @@ func loadConfig() (*config, error) { return &c, err } -// TestCreateAwsDynamicHostCatalogCli uses the boundary cli to create a host catalog with the AWS +// TestCliCreateAwsDynamicHostCatalog uses the boundary cli to create a host catalog with the AWS // plugin. The test sets up an AWS dynamic host catalog, creates some host sets, sets up a target to // one of the host sets, and attempts to connect to the target. -func TestCreateAwsDynamicHostCatalogCli(t *testing.T) { +func TestCliCreateAwsDynamicHostCatalog(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) @@ -263,10 +263,10 @@ func TestCreateAwsDynamicHostCatalogCli(t *testing.T) { require.True(t, hostIpInList, fmt.Sprintf("Connected host (%s) is not in expected list (%s)", hostIp, targetIps1)) } -// TestCreateAwsDynamicHostCatalogApi uses the Go api to create a host catalog with the AWS plugin. +// TestApiCreateAwsDynamicHostCatalog uses the Go api to create a host catalog with the AWS plugin. // The test sets up an AWS dynamic host catalog, creates a host set, and sets up a target to the // host set. -func TestCreateAwsDynamicHostCatalogApi(t *testing.T) { +func TestApiCreateAwsDynamicHostCatalog(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) diff --git a/testing/internal/e2e/host/static/connect_authz_token_test.go b/testing/internal/e2e/tests/static/connect_authz_token_test.go similarity index 97% rename from testing/internal/e2e/host/static/connect_authz_token_test.go rename to testing/internal/e2e/tests/static/connect_authz_token_test.go index a0a54e5ad..76ef38f88 100644 --- a/testing/internal/e2e/host/static/connect_authz_token_test.go +++ b/testing/internal/e2e/tests/static/connect_authz_token_test.go @@ -16,9 +16,9 @@ import ( "github.com/stretchr/testify/require" ) -// TestConnectTargetWithAuthzTokenCli uses the boundary cli to connect to a target using the +// TestCliConnectTargetWithAuthzToken uses the boundary cli to connect to a target using the // `authz_token` option -func TestConnectTargetWithAuthzTokenCli(t *testing.T) { +func TestCliConnectTargetWithAuthzToken(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) diff --git a/testing/internal/e2e/tests/static/connect_ssh_test.go b/testing/internal/e2e/tests/static/connect_ssh_test.go new file mode 100644 index 000000000..d77ed81af --- /dev/null +++ b/testing/internal/e2e/tests/static/connect_ssh_test.go @@ -0,0 +1,96 @@ +package static_test + +import ( + "context" + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCliConnectTargetWithSsh uses the boundary cli to create a credential using boundary's +// built-in credential store. The test attaches that credential to a target and attempts to connect +// to that target using those credentials. +func TestCliConnectTargetWithSsh(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadConfig() + require.NoError(t, err) + + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + newOrgId := boundary.CreateNewOrgCli(t, ctx) + newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) + newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId) + newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId) + newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp) + boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId) + newTargetId := boundary.CreateNewTargetCli(t, ctx, newProjectId, c.TargetPort) + boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId) + newCredentialStoreId := boundary.CreateNewCredentialStoreStaticCli(t, ctx, newProjectId) + + // Create credentials + output := e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "credentials", "create", "ssh-private-key", + "-credential-store-id", newCredentialStoreId, + "-username", c.TargetSshUser, + "-private-key", "file://"+c.TargetSshKeyPath, + "-format", "json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newCredentialsResult credentials.CredentialCreateResult + err = json.Unmarshal(output.Stdout, &newCredentialsResult) + require.NoError(t, err) + newCredentialsId := newCredentialsResult.Item.Id + t.Logf("Created Credentials: %s", newCredentialsId) + + // Add credentials to target + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "targets", "add-credential-sources", + "-id", newTargetId, + "-brokered-credential-source", newCredentialsId, + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Get credentials for target + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs("targets", "authorize-session", "-id", newTargetId, "-format", "json"), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newSessionAuthorizationResult targets.SessionAuthorizationResult + err = json.Unmarshal(output.Stdout, &newSessionAuthorizationResult) + require.NoError(t, err) + + newSessionAuthorization := newSessionAuthorizationResult.Item + retrievedUser := fmt.Sprintf("%s", newSessionAuthorization.Credentials[0].Credential["username"]) + retrievedKey := fmt.Sprintf("%s\n", newSessionAuthorization.Credentials[0].Credential["private_key"]) + assert.Equal(t, c.TargetSshUser, retrievedUser) + + k, err := os.ReadFile(c.TargetSshKeyPath) + require.NoError(t, err) + require.Equal(t, string(k), retrievedKey) + t.Log("Successfully retrieved credentials for target") + + // Connect to target using ssh option + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "connect", "ssh", + "-target-id", newTargetId, "--", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + t.Log("Successfully connected to target") +} diff --git a/testing/internal/e2e/host/static/connect_test.go b/testing/internal/e2e/tests/static/connect_test.go similarity index 61% rename from testing/internal/e2e/host/static/connect_test.go rename to testing/internal/e2e/tests/static/connect_test.go index 85a5f0258..dd110a2f9 100644 --- a/testing/internal/e2e/host/static/connect_test.go +++ b/testing/internal/e2e/tests/static/connect_test.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/require" ) -// TestConnectTargetCli uses the boundary cli to create a number of supporting objects +// TestCliConnectTarget uses the boundary cli to create a number of supporting objects // to connect to a target. It then attempts to connect to that target and verifies that // the connection was successful. -func TestConnectTargetCli(t *testing.T) { +func TestCliConnectTarget(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) @@ -52,25 +52,3 @@ func TestConnectTargetCli(t *testing.T) { require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output") t.Log("Successfully connected to target") } - -// TestCreateTargetApi uses the Go api to create a number of supporting objects -// to connect to a target. This test does not connect to the target due to the complexity -// when not using the cli. -func TestCreateTargetApi(t *testing.T) { - e2e.MaybeSkipTest(t) - c, err := loadConfig() - require.NoError(t, err) - - client, err := boundary.NewApiClient() - require.NoError(t, err) - ctx := context.Background() - - newOrgId := boundary.CreateNewOrgApi(t, ctx, client) - newProjectId := boundary.CreateNewProjectApi(t, ctx, client, newOrgId) - newHostCatalogId := boundary.CreateNewHostCatalogApi(t, ctx, client, newProjectId) - newHostSetId := boundary.CreateNewHostSetApi(t, ctx, client, newHostCatalogId) - newHostId := boundary.CreateNewHostApi(t, ctx, client, newHostCatalogId, c.TargetIp) - boundary.AddHostToHostSetApi(t, ctx, client, newHostSetId, newHostId) - newTargetId := boundary.CreateNewTargetApi(t, ctx, client, newProjectId, c.TargetPort) - boundary.AddHostSourceToTargetApi(t, ctx, client, newTargetId, newHostSetId) -} diff --git a/testing/internal/e2e/host/static/connect_ssh_test.go b/testing/internal/e2e/tests/static/credential_store_test.go similarity index 59% rename from testing/internal/e2e/host/static/connect_ssh_test.go rename to testing/internal/e2e/tests/static/credential_store_test.go index d06871f6e..ba3e85c2f 100644 --- a/testing/internal/e2e/host/static/connect_ssh_test.go +++ b/testing/internal/e2e/tests/static/credential_store_test.go @@ -6,7 +6,9 @@ import ( "fmt" "os" "testing" + "time" + "github.com/cenkalti/backoff/v4" "github.com/hashicorp/boundary/api/credentials" "github.com/hashicorp/boundary/api/targets" "github.com/hashicorp/boundary/testing/internal/e2e" @@ -15,10 +17,8 @@ import ( "github.com/stretchr/testify/require" ) -// TestConnectTargetWithSshCli uses the boundary cli to create a credential using boundary's -// built-in credential store. The test attaches that credential to a target and attempts to connect -// to that target using those credentials. -func TestConnectTargetWithSshCli(t *testing.T) { +// TestCliStaticCredentialStore validates various credential-store operations using the cli +func TestCliStaticCredentialStore(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) @@ -27,15 +27,9 @@ func TestConnectTargetWithSshCli(t *testing.T) { boundary.AuthenticateAdminCli(t, ctx) newOrgId := boundary.CreateNewOrgCli(t, ctx) newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) - newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId) - newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId) - newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp) - boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId) - newTargetId := boundary.CreateNewTargetCli(t, ctx, newProjectId, c.TargetPort) - boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId) newCredentialStoreId := boundary.CreateNewCredentialStoreStaticCli(t, ctx, newProjectId) - // Create credentials + // Create ssh key credentials output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs( "credentials", "create", "ssh-private-key", @@ -46,58 +40,68 @@ func TestConnectTargetWithSshCli(t *testing.T) { ), ) require.NoError(t, output.Err, string(output.Stderr)) - var newCredentialsResult credentials.CredentialCreateResult - err = json.Unmarshal(output.Stdout, &newCredentialsResult) + var keyCredentialsResult credentials.CredentialCreateResult + err = json.Unmarshal(output.Stdout, &keyCredentialsResult) require.NoError(t, err) - newCredentialsId := newCredentialsResult.Item.Id - t.Logf("Created Credentials: %s", newCredentialsId) + keyCredentialsId := keyCredentialsResult.Item.Id + t.Logf("Created SSH Private Key Credentials: %s", keyCredentialsId) - // Add credentials to target + // Create username/password credentials output = e2e.RunCommand(ctx, "boundary", e2e.WithArgs( - "targets", "add-credential-sources", - "-id", newTargetId, - "-brokered-credential-source", newCredentialsId, + "credentials", "create", "username-password", + "-credential-store-id", newCredentialStoreId, + "-username", c.TargetSshUser, + "-password", "env://E2E_CREDENTIALS_PASSWORD", + "-format", "json", ), + e2e.WithEnv("E2E_CREDENTIALS_PASSWORD", "password"), ) require.NoError(t, output.Err, string(output.Stderr)) - - // Get credentials for target - output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs("targets", "authorize-session", "-id", newTargetId, "-format", "json"), - ) - require.NoError(t, output.Err, string(output.Stderr)) - var newSessionAuthorizationResult targets.SessionAuthorizationResult - err = json.Unmarshal(output.Stdout, &newSessionAuthorizationResult) + var pwCredentialsResult credentials.CredentialCreateResult + err = json.Unmarshal(output.Stdout, &pwCredentialsResult) require.NoError(t, err) + pwCredentialsId := pwCredentialsResult.Item.Id + t.Logf("Created Username/Password Credentials: %s", pwCredentialsId) - newSessionAuthorization := newSessionAuthorizationResult.Item - retrievedUser := fmt.Sprintf("%s", newSessionAuthorization.Credentials[0].Credential["username"]) - retrievedKey := fmt.Sprintf("%s\n", newSessionAuthorization.Credentials[0].Credential["private_key"]) - assert.Equal(t, c.TargetSshUser, retrievedUser) - - k, err := os.ReadFile(c.TargetSshKeyPath) - require.NoError(t, err) - require.Equal(t, string(k), retrievedKey) - t.Log("Successfully retrieved credentials for target") - - // Connect to target and print host's IP address using retrieved credentials + // Delete credential store output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs( - "connect", "ssh", - "-target-id", newTargetId, "--", - "-o", "UserKnownHostsFile=/dev/null", - "-o", "StrictHostKeyChecking=no", - "-o", "IdentitiesOnly=yes", // forces the use of the provided key - ), + e2e.WithArgs("credential-stores", "delete", "-id", newCredentialStoreId), ) require.NoError(t, output.Err, string(output.Stderr)) - t.Log("Successfully connected to target") + err = backoff.RetryNotify( + func() error { + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs("credential-stores", "read", "-id", newCredentialStoreId, "-format", "json"), + ) + if output.Err == nil { + return fmt.Errorf("Deleted credential can still be read: '%s'", output.Stdout) + } + + var response e2e.CliError + err = json.Unmarshal(output.Stderr, &response) + require.NoError(t, err) + statusCode := response.Status + if statusCode != 404 { + return backoff.Permanent( + fmt.Errorf("Command did not return expected status code. Expected: 404, Actual: %d", statusCode), + ) + } + + return nil + }, + backoff.WithMaxRetries(backoff.NewConstantBackOff(3*time.Second), 5), + func(err error, td time.Duration) { + t.Logf("%s. Retrying...", err.Error()) + }, + ) + require.NoError(t, err) + t.Logf("Successfully deleted credential store") } -// TestCreateTargetWithStaticCredentialStoreApi uses the Go api to create a credential using +// TestApiStaticCredentialStore uses the Go api to create a credential using // boundary's built-in credential store. The test then attaches that credential to a target. -func TestCreateTargetWithStaticCredentialStoreApi(t *testing.T) { +func TestApiStaticCredentialStore(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) diff --git a/testing/internal/e2e/host/static/env_test.go b/testing/internal/e2e/tests/static/env_test.go similarity index 97% rename from testing/internal/e2e/host/static/env_test.go rename to testing/internal/e2e/tests/static/env_test.go index 944f97f22..f7ff9b9ab 100644 --- a/testing/internal/e2e/host/static/env_test.go +++ b/testing/internal/e2e/tests/static/env_test.go @@ -16,5 +16,5 @@ func loadConfig() (*config, error) { return nil, err } - return &c, err + return &c, nil } diff --git a/testing/internal/e2e/host/static/session_cancel_admin_test.go b/testing/internal/e2e/tests/static/session_cancel_admin_test.go similarity index 97% rename from testing/internal/e2e/host/static/session_cancel_admin_test.go rename to testing/internal/e2e/tests/static/session_cancel_admin_test.go index 4b5aaf460..bf79c0364 100644 --- a/testing/internal/e2e/host/static/session_cancel_admin_test.go +++ b/testing/internal/e2e/tests/static/session_cancel_admin_test.go @@ -15,8 +15,8 @@ import ( "github.com/stretchr/testify/require" ) -// TestSessionCancelAdminCli uses the boundary cli to start and then cancel a session -func TestSessionCancelAdminCli(t *testing.T) { +// TestCliSessionCancelAdmin uses the boundary cli to start and then cancel a session +func TestCliSessionCancelAdmin(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) diff --git a/testing/internal/e2e/host/static/session_cancel_user_test.go b/testing/internal/e2e/tests/static/session_cancel_user_test.go similarity index 97% rename from testing/internal/e2e/host/static/session_cancel_user_test.go rename to testing/internal/e2e/tests/static/session_cancel_user_test.go index 311bad08c..1eae7f968 100644 --- a/testing/internal/e2e/host/static/session_cancel_user_test.go +++ b/testing/internal/e2e/tests/static/session_cancel_user_test.go @@ -17,10 +17,10 @@ import ( "github.com/stretchr/testify/require" ) -// TestSessionCancelUserCli uses the cli to create a new user and sets up the right permissions for +// TestCliSessionCancelUser uses the cli to create a new user and sets up the right permissions for // the user to connect to the created target. The test also confirms that an admin can cancel the // user's session. -func TestSessionCancelUserCli(t *testing.T) { +func TestCliSessionCancelUser(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) @@ -195,8 +195,8 @@ func TestSessionCancelUserCli(t *testing.T) { t.Log("Successfully cancelled session") } -// TestCreateUserApi uses the Go api to create a new user and add some grants to the user -func TestCreateUserApi(t *testing.T) { +// TestApiCreateUser uses the Go api to create a new user and add some grants to the user +func TestApiCreateUser(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) diff --git a/testing/internal/e2e/tests/static_with_vault/connect_ssh_test.go b/testing/internal/e2e/tests/static_with_vault/connect_ssh_test.go new file mode 100644 index 000000000..cc6cb3834 --- /dev/null +++ b/testing/internal/e2e/tests/static_with_vault/connect_ssh_test.go @@ -0,0 +1,154 @@ +package static_with_vault_test + +import ( + "context" + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/hashicorp/boundary/api/credentiallibraries" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/hashicorp/boundary/testing/internal/e2e/vault" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCliVaultConnectTargetWithSsh uses the boundary and vault clis to add secrets management for a +// target. The test sets up vault as a credential store, creates a set of credentials in vault to be +// attached to a target, and attempts to connect to that target (with the ssh option) using those +// credentials. +func TestCliVaultConnectTargetWithSsh(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadConfig() + require.NoError(t, err) + + ctx := context.Background() + boundary.AuthenticateAdminCli(t, ctx) + newOrgId := boundary.CreateNewOrgCli(t, ctx) + newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId) + newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId) + newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId) + newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp) + boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId) + newTargetId := boundary.CreateNewTargetCli(t, ctx, newProjectId, c.TargetPort) + boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId) + + // Configure vault + vaultAddr, boundaryPolicyName := vault.Setup(t) + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"), + ) + require.NoError(t, output.Err, string(output.Stderr)) + t.Cleanup(func() { + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("secrets", "disable", c.VaultSecretPath), + ) + require.NoError(t, output.Err, string(output.Stderr)) + }) + + // Create credential in vault + secretName := "TestCreateVaultCredentialStoreCli" + credentialPolicyName := vault.CreateKvPrivateKeyCredential(t, secretName, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath) + t.Log("Created Vault Credential") + + // Create vault token for boundary + output = e2e.RunCommand(ctx, "vault", + e2e.WithArgs( + "token", "create", + "-no-default-policy=true", + "-policy="+boundaryPolicyName, + "-policy="+credentialPolicyName, + "-orphan=true", + "-period=20m", + "-renewable=true", + "-format=json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var tokenCreateResult createTokenResponse + err = json.Unmarshal(output.Stdout, &tokenCreateResult) + require.NoError(t, err) + credStoreToken := tokenCreateResult.Auth.Client_Token + t.Log("Created Vault Cred Store Token") + + // Create a credential store + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "credential-stores", "create", "vault", + "-scope-id", newProjectId, + "-vault-address", vaultAddr, + "-vault-token", credStoreToken, + "-format", "json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newCredentialStoreResult credentialstores.CredentialStoreCreateResult + err = json.Unmarshal(output.Stdout, &newCredentialStoreResult) + require.NoError(t, err) + newCredentialStoreId := newCredentialStoreResult.Item.Id + t.Logf("Created Credential Store: %s", newCredentialStoreId) + + // Create a credential library + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "credential-libraries", "create", "vault", + "-credential-store-id", newCredentialStoreId, + "-vault-path", c.VaultSecretPath+"/data/"+secretName, + "-name", "e2e Automated Test Vault Credential Library", + "-credential-type", "ssh_private_key", + "-format", "json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newCredentialLibraryResult credentiallibraries.CredentialLibraryCreateResult + err = json.Unmarshal(output.Stdout, &newCredentialLibraryResult) + require.NoError(t, err) + newCredentialLibraryId := newCredentialLibraryResult.Item.Id + t.Logf("Created Credential Library: %s", newCredentialLibraryId) + + // Add brokered credentials to target + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "targets", "add-credential-sources", + "-id", newTargetId, + "-brokered-credential-source", newCredentialLibraryId, + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Get credentials for target + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs("targets", "authorize-session", "-id", newTargetId, "-format", "json"), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newSessionAuthorizationResult targets.SessionAuthorizationResult + err = json.Unmarshal(output.Stdout, &newSessionAuthorizationResult) + require.NoError(t, err) + + newSessionAuthorization := newSessionAuthorizationResult.Item + retrievedUser := fmt.Sprintf("%s", newSessionAuthorization.Credentials[0].Credential["username"]) + retrievedKey := fmt.Sprintf("%s", newSessionAuthorization.Credentials[0].Credential["private_key"]) + assert.Equal(t, c.TargetSshUser, retrievedUser) + + k, err := os.ReadFile(c.TargetSshKeyPath) + require.NoError(t, err) + require.Equal(t, string(k), retrievedKey) + t.Log("Successfully retrieved credentials for target") + + // Connect to target using ssh option + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "connect", "ssh", + "-target-id", newTargetId, "--", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + t.Log("Successfully connected to target") +} diff --git a/testing/internal/e2e/credential/vault/vault_test.go b/testing/internal/e2e/tests/static_with_vault/connect_test.go similarity index 60% rename from testing/internal/e2e/credential/vault/vault_test.go rename to testing/internal/e2e/tests/static_with_vault/connect_test.go index a927b4e95..5c0cf7048 100644 --- a/testing/internal/e2e/credential/vault/vault_test.go +++ b/testing/internal/e2e/tests/static_with_vault/connect_test.go @@ -1,4 +1,4 @@ -package vault_test +package static_with_vault_test import ( "context" @@ -43,11 +43,11 @@ type createTokenResponse struct { } } -// TestCreateVaultCredentialStoreCli uses the boundary and vault clis to add secrets management +// TestCliVaultConnectTarget uses the boundary and vault clis to add secrets management // for a target. The test sets up vault as a credential store, creates a set of credentials // in vault to be attached to a target, and attempts to connect to that target using those // credentials. -func TestCreateVaultCredentialStoreCli(t *testing.T) { +func TestCliVaultConnectTarget(t *testing.T) { e2e.MaybeSkipTest(t) c, err := loadConfig() require.NoError(t, err) @@ -64,9 +64,7 @@ func TestCreateVaultCredentialStoreCli(t *testing.T) { boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId) // Configure vault - vaultAddr, boundaryPolicyName := vault.Setup(t) - output := e2e.RunCommand(ctx, "vault", e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"), ) @@ -199,108 +197,3 @@ func TestCreateVaultCredentialStoreCli(t *testing.T) { require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output") t.Log("Successfully connected to target") } - -// TestCreateVaultCredentialStoreApi uses the Go api along with the vault cli to add secrets -// management for a target. The test sets up vault as a credential stores and creates a set of -// credentials in vault that is attached to a target. -func TestCreateVaultCredentialStoreApi(t *testing.T) { - e2e.MaybeSkipTest(t) - c, err := loadConfig() - require.NoError(t, err) - - client, err := boundary.NewApiClient() - require.NoError(t, err) - ctx := context.Background() - - newOrgId := boundary.CreateNewOrgApi(t, ctx, client) - newProjectId := boundary.CreateNewProjectApi(t, ctx, client, newOrgId) - newHostCatalogId := boundary.CreateNewHostCatalogApi(t, ctx, client, newProjectId) - newHostSetId := boundary.CreateNewHostSetApi(t, ctx, client, newHostCatalogId) - newHostId := boundary.CreateNewHostApi(t, ctx, client, newHostCatalogId, c.TargetIp) - boundary.AddHostToHostSetApi(t, ctx, client, newHostSetId, newHostId) - newTargetId := boundary.CreateNewTargetApi(t, ctx, client, newProjectId, c.TargetPort) - boundary.AddHostSourceToTargetApi(t, ctx, client, newTargetId, newHostSetId) - - // Configure vault - vaultAddr, boundaryPolicyName := vault.Setup(t) - - output := e2e.RunCommand(ctx, "vault", - e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"), - ) - require.NoError(t, output.Err, string(output.Stderr)) - t.Cleanup(func() { - output := e2e.RunCommand(ctx, "vault", - e2e.WithArgs("secrets", "disable", c.VaultSecretPath), - ) - require.NoError(t, output.Err, string(output.Stderr)) - }) - - // Create credential in vault - secretName := "TestCreateVaultCredentialStoreCli" - credentialPolicyName := vault.CreateKvPrivateKeyCredential(t, secretName, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath) - t.Log("Created Vault Credential") - - // Create vault token for boundary - output = e2e.RunCommand(ctx, "vault", - e2e.WithArgs( - "token", "create", - "-no-default-policy=true", - "-policy="+boundaryPolicyName, - "-policy="+credentialPolicyName, - "-orphan=true", - "-period=20m", - "-renewable=true", - "-format=json", - ), - ) - require.NoError(t, output.Err, string(output.Stderr)) - var tokenCreateResult createTokenResponse - err = json.Unmarshal(output.Stdout, &tokenCreateResult) - require.NoError(t, err) - credStoreToken := tokenCreateResult.Auth.Client_Token - t.Log("Created Vault Cred Store Token") - - // Create a credential store - csClient := credentialstores.NewClient(client) - newCredentialStoreResult, err := csClient.Create(ctx, "vault", newProjectId, - credentialstores.WithVaultCredentialStoreAddress(vaultAddr), - credentialstores.WithVaultCredentialStoreToken(credStoreToken), - ) - require.NoError(t, err) - newCredentialStoreId := newCredentialStoreResult.Item.Id - t.Logf("Created Credential Store: %s", newCredentialStoreId) - - // Create a credential library - clClient := credentiallibraries.NewClient(client) - newCredentialLibraryResult, err := clClient.Create(ctx, newCredentialStoreId, - credentiallibraries.WithVaultCredentialLibraryPath(c.VaultSecretPath+"/data/"+secretName), - credentiallibraries.WithCredentialType("ssh_private_key"), - ) - require.NoError(t, err) - newCredentialLibraryId := newCredentialLibraryResult.Item.Id - t.Logf("Created Credential Library: %s", newCredentialLibraryId) - - // Add brokered credentials to target - tClient := targets.NewClient(client) - _, err = tClient.AddCredentialSources(ctx, newTargetId, 0, - targets.WithBrokeredCredentialSourceIds([]string{newCredentialLibraryId}), - targets.WithAutomaticVersioning(true), - ) - require.NoError(t, err) - - // Get credentials for target - newSessionAuthorizationResult, err := tClient.AuthorizeSession(ctx, newTargetId) - require.NoError(t, err) - newSessionAuthorization := newSessionAuthorizationResult.Item - retrievedUser, ok := newSessionAuthorization.Credentials[0].Credential["username"].(string) - require.True(t, ok) - assert.Equal(t, c.TargetSshUser, retrievedUser) - - retrievedKey, ok := newSessionAuthorization.Credentials[0].Credential["private_key"].(string) - require.True(t, ok) - k, err := os.ReadFile(c.TargetSshKeyPath) - require.NoError(t, err) - keysMatch := string(k) == retrievedKey // This is done to prevent printing out key info - require.True(t, keysMatch, "Key retrieved from vault does not match expected value") - t.Log("Successfully retrieved credentials for target") -} diff --git a/testing/internal/e2e/tests/static_with_vault/credential_store_test.go b/testing/internal/e2e/tests/static_with_vault/credential_store_test.go new file mode 100644 index 000000000..f79d63261 --- /dev/null +++ b/testing/internal/e2e/tests/static_with_vault/credential_store_test.go @@ -0,0 +1,122 @@ +package static_with_vault_test + +import ( + "context" + "encoding/json" + "os" + "testing" + + "github.com/hashicorp/boundary/api/credentiallibraries" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/hashicorp/boundary/testing/internal/e2e/vault" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestApiVaultCredentialStore uses the Go api along with the vault cli to add secrets +// management for a target. The test sets up vault as a credential stores and creates a set of +// credentials in vault that is attached to a target. +func TestApiVaultCredentialStore(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadConfig() + require.NoError(t, err) + + client, err := boundary.NewApiClient() + require.NoError(t, err) + ctx := context.Background() + + newOrgId := boundary.CreateNewOrgApi(t, ctx, client) + newProjectId := boundary.CreateNewProjectApi(t, ctx, client, newOrgId) + newHostCatalogId := boundary.CreateNewHostCatalogApi(t, ctx, client, newProjectId) + newHostSetId := boundary.CreateNewHostSetApi(t, ctx, client, newHostCatalogId) + newHostId := boundary.CreateNewHostApi(t, ctx, client, newHostCatalogId, c.TargetIp) + boundary.AddHostToHostSetApi(t, ctx, client, newHostSetId, newHostId) + newTargetId := boundary.CreateNewTargetApi(t, ctx, client, newProjectId, c.TargetPort) + boundary.AddHostSourceToTargetApi(t, ctx, client, newTargetId, newHostSetId) + + // Configure vault + vaultAddr, boundaryPolicyName := vault.Setup(t) + + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"), + ) + require.NoError(t, output.Err, string(output.Stderr)) + t.Cleanup(func() { + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("secrets", "disable", c.VaultSecretPath), + ) + require.NoError(t, output.Err, string(output.Stderr)) + }) + + // Create credential in vault + secretName := "TestCreateVaultCredentialStoreCli" + credentialPolicyName := vault.CreateKvPrivateKeyCredential(t, secretName, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath) + t.Log("Created Vault Credential") + + // Create vault token for boundary + output = e2e.RunCommand(ctx, "vault", + e2e.WithArgs( + "token", "create", + "-no-default-policy=true", + "-policy="+boundaryPolicyName, + "-policy="+credentialPolicyName, + "-orphan=true", + "-period=20m", + "-renewable=true", + "-format=json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var tokenCreateResult createTokenResponse + err = json.Unmarshal(output.Stdout, &tokenCreateResult) + require.NoError(t, err) + credStoreToken := tokenCreateResult.Auth.Client_Token + t.Log("Created Vault Cred Store Token") + + // Create a credential store + csClient := credentialstores.NewClient(client) + newCredentialStoreResult, err := csClient.Create(ctx, "vault", newProjectId, + credentialstores.WithVaultCredentialStoreAddress(vaultAddr), + credentialstores.WithVaultCredentialStoreToken(credStoreToken), + ) + require.NoError(t, err) + newCredentialStoreId := newCredentialStoreResult.Item.Id + t.Logf("Created Credential Store: %s", newCredentialStoreId) + + // Create a credential library + clClient := credentiallibraries.NewClient(client) + newCredentialLibraryResult, err := clClient.Create(ctx, newCredentialStoreId, + credentiallibraries.WithVaultCredentialLibraryPath(c.VaultSecretPath+"/data/"+secretName), + credentiallibraries.WithCredentialType("ssh_private_key"), + ) + require.NoError(t, err) + newCredentialLibraryId := newCredentialLibraryResult.Item.Id + t.Logf("Created Credential Library: %s", newCredentialLibraryId) + + // Add brokered credentials to target + tClient := targets.NewClient(client) + _, err = tClient.AddCredentialSources(ctx, newTargetId, 0, + targets.WithBrokeredCredentialSourceIds([]string{newCredentialLibraryId}), + targets.WithAutomaticVersioning(true), + ) + require.NoError(t, err) + + // Get credentials for target + newSessionAuthorizationResult, err := tClient.AuthorizeSession(ctx, newTargetId) + require.NoError(t, err) + newSessionAuthorization := newSessionAuthorizationResult.Item + retrievedUser, ok := newSessionAuthorization.Credentials[0].Credential["username"].(string) + require.True(t, ok) + assert.Equal(t, c.TargetSshUser, retrievedUser) + + retrievedKey, ok := newSessionAuthorization.Credentials[0].Credential["private_key"].(string) + require.True(t, ok) + k, err := os.ReadFile(c.TargetSshKeyPath) + require.NoError(t, err) + keysMatch := string(k) == retrievedKey // This is done to prevent printing out key info + require.True(t, keysMatch, "Key retrieved from vault does not match expected value") + t.Log("Successfully retrieved credentials for target") +}