From 61fb0793710b43562bf00628ad3af52d2137083e Mon Sep 17 00:00:00 2001 From: Michael Li Date: Fri, 9 Feb 2024 13:07:17 -0500 Subject: [PATCH] test(e2e): Add test for authentication when no keyring is available (#4378) --- enos/enos-scenario-e2e-docker-base-plus.hcl | 53 +++---- enos/modules/docker_boundary/main.tf | 4 + enos/modules/test_e2e_docker/main.tf | 7 +- enos/modules/test_e2e_docker/test_runner.sh | 1 + go.mod | 3 +- .../base_plus/authenticate_no_keyring_test.go | 132 ++++++++++++++++++ .../internal/e2e/tests/base_plus/env_test.go | 29 ++-- 7 files changed, 185 insertions(+), 44 deletions(-) create mode 100644 testing/internal/e2e/tests/base_plus/authenticate_no_keyring_test.go diff --git a/enos/enos-scenario-e2e-docker-base-plus.hcl b/enos/enos-scenario-e2e-docker-base-plus.hcl index 5b737d9453..50cabae249 100644 --- a/enos/enos-scenario-e2e-docker-base-plus.hcl +++ b/enos/enos-scenario-e2e-docker-base-plus.hcl @@ -107,32 +107,33 @@ scenario "e2e_docker_base_plus" { step.create_boundary, ] variables { - test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base_plus" - docker_mirror = var.docker_mirror - network_name = step.create_docker_network.network_name - go_version = var.go_version - debug_no_run = var.e2e_debug_no_run - alb_boundary_api_addr = step.create_boundary.address - auth_method_id = step.create_boundary.auth_method_id - auth_login_name = step.create_boundary.login_name - auth_password = step.create_boundary.password - local_boundary_dir = step.build_boundary_docker_image.cli_zip_path - local_boundary_src_dir = local.local_boundary_src_dir - aws_ssh_private_key_path = local.aws_ssh_private_key_path - target_address = step.create_boundary_database.container_name - target_port = step.create_boundary_database.port - target_user = "ubuntu" - postgres_user = step.create_boundary_database.user - postgres_password = step.create_boundary_database.password - postgres_database_name = step.create_boundary_database.database_name - ldap_address = step.create_ldap_server.address - ldap_domain_dn = step.create_ldap_server.domain_dn - ldap_admin_dn = step.create_ldap_server.admin_dn - ldap_admin_password = step.create_ldap_server.admin_password - ldap_user_name = step.create_ldap_server.user_name - ldap_user_password = step.create_ldap_server.user_password - ldap_group_name = step.create_ldap_server.group_name - max_page_size = step.create_boundary.max_page_size + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base_plus" + docker_mirror = var.docker_mirror + controller_container_name = step.create_boundary.container_name + network_name = step.create_docker_network.network_name + go_version = var.go_version + debug_no_run = var.e2e_debug_no_run + alb_boundary_api_addr = step.create_boundary.address + auth_method_id = step.create_boundary.auth_method_id + auth_login_name = step.create_boundary.login_name + auth_password = step.create_boundary.password + local_boundary_dir = step.build_boundary_docker_image.cli_zip_path + local_boundary_src_dir = local.local_boundary_src_dir + aws_ssh_private_key_path = local.aws_ssh_private_key_path + target_address = step.create_boundary_database.container_name + target_port = step.create_boundary_database.port + target_user = "ubuntu" + postgres_user = step.create_boundary_database.user + postgres_password = step.create_boundary_database.password + postgres_database_name = step.create_boundary_database.database_name + ldap_address = step.create_ldap_server.address + ldap_domain_dn = step.create_ldap_server.domain_dn + ldap_admin_dn = step.create_ldap_server.admin_dn + ldap_admin_password = step.create_ldap_server.admin_password + ldap_user_name = step.create_ldap_server.user_name + ldap_user_password = step.create_ldap_server.user_password + ldap_group_name = step.create_ldap_server.group_name + max_page_size = step.create_boundary.max_page_size } } } diff --git a/enos/modules/docker_boundary/main.tf b/enos/modules/docker_boundary/main.tf index 915198b337..d1a87ad6b7 100644 --- a/enos/modules/docker_boundary/main.tf +++ b/enos/modules/docker_boundary/main.tf @@ -183,3 +183,7 @@ output "worker_tag" { output "max_page_size" { value = var.max_page_size } + +output "container_name" { + value = var.container_name +} diff --git a/enos/modules/test_e2e_docker/main.tf b/enos/modules/test_e2e_docker/main.tf index a83bb78090..eed71c2bf2 100644 --- a/enos/modules/test_e2e_docker/main.tf +++ b/enos/modules/test_e2e_docker/main.tf @@ -22,10 +22,10 @@ variable "network_name" { description = "Name of Docker Network" type = string } -variable "container_name" { - description = "Name of Docker Container" +variable "controller_container_name" { + description = "Name of Docker Container running the Boundary controller" type = string - default = "test_runner" + default = "" } variable "go_version" { description = "Version of Golang used by the application under test" @@ -298,6 +298,7 @@ resource "enos_local_exec" "run_e2e_test" { E2E_LDAP_USER_PASSWORD = var.ldap_user_password E2E_LDAP_GROUP_NAME = var.ldap_group_name E2E_MAX_PAGE_SIZE = var.max_page_size + E2E_CONTROLLER_CONTAINER_NAME = var.controller_container_name BOUNDARY_DIR = abspath(var.local_boundary_src_dir) BOUNDARY_CLI_DIR = abspath(var.local_boundary_dir) MODULE_DIR = abspath(path.module) diff --git a/enos/modules/test_e2e_docker/test_runner.sh b/enos/modules/test_e2e_docker/test_runner.sh index ddae0f319d..e82d5b5236 100644 --- a/enos/modules/test_e2e_docker/test_runner.sh +++ b/enos/modules/test_e2e_docker/test_runner.sh @@ -47,6 +47,7 @@ docker run \ -e "E2E_LDAP_USER_PASSWORD=$E2E_LDAP_USER_PASSWORD" \ -e "E2E_LDAP_GROUP_NAME=$E2E_LDAP_GROUP_NAME" \ -e "E2E_MAX_PAGE_SIZE=$E2E_MAX_PAGE_SIZE" \ + -e "E2E_CONTROLLER_CONTAINER_NAME=$E2E_CONTROLLER_CONTAINER_NAME" \ --mount type=bind,src=$BOUNDARY_DIR,dst=/src/boundary/ \ --mount type=bind,src=$MODULE_DIR/../..,dst=/testlogs \ --mount type=bind,src=$(go env GOCACHE),dst=/root/.cache/go-build \ diff --git a/go.mod b/go.mod index 234b04aeba..929da061f2 100644 --- a/go.mod +++ b/go.mod @@ -119,6 +119,7 @@ require ( ) require ( + github.com/docker/distribution v2.8.2+incompatible // indirect github.com/frankban/quicktest v1.14.5 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect @@ -150,7 +151,7 @@ require ( github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v23.0.1+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dvsekhvalnov/jose2go v1.5.1-0.20231206184617-48ba0b76bc88 // indirect diff --git a/testing/internal/e2e/tests/base_plus/authenticate_no_keyring_test.go b/testing/internal/e2e/tests/base_plus/authenticate_no_keyring_test.go new file mode 100644 index 0000000000..0e69849282 --- /dev/null +++ b/testing/internal/e2e/tests/base_plus/authenticate_no_keyring_test.go @@ -0,0 +1,132 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package base_plus_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/stretchr/testify/require" +) + +// TestCliAuthenticateNoKeyring tests authentication when we're using `-format +// json` and there is no keyring on the system. +// This covers a case where the client cache daemon should intercept the auth +// token in the json response. There was previously a bug where the client cache +// daemon tried to access the keyring when there wasn't one on the system. +func TestCliAuthenticateNoKeyring(t *testing.T) { + e2e.MaybeSkipTest(t) + ctx := context.Background() + + bc, err := boundary.LoadConfig() + require.NoError(t, err) + + containerID := "boundary" + + docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + require.NoError(t, err) + + // Check that `pass` is not in the docker container + execConfig := types.ExecConfig{ + AttachStdout: true, + AttachStderr: true, + Cmd: []string{ + "which", "pass", + }, + } + exec, err := docker.ContainerExecCreate(ctx, containerID, execConfig) + require.NoError(t, err) + resp, err := docker.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{}) + require.NoError(t, err) + t.Cleanup(func() { + resp.Close() + }) + + var outBuf, errBuf bytes.Buffer + outputDone := make(chan error) + go func() { + // StdCopy demultiplexes the stream into two buffers + _, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader) + outputDone <- err + }() + + select { + case err := <-outputDone: + if err != nil { + require.NoError(t, err) + } + break + + case <-ctx.Done(): + require.NoError(t, ctx.Err()) + } + + stdout, err := io.ReadAll(&outBuf) + require.NoError(t, err) + require.Empty(t, string(stdout)) + + // Try to authenticate from inside the docker container + execConfig = types.ExecConfig{ + AttachStdout: true, + AttachStderr: true, + Cmd: []string{ + "boundary", "authenticate", "password", + "-login-name", bc.AdminLoginName, + "-password", "env://E2E_PASSWORD", + "-format", "json", + }, + Env: []string{ + fmt.Sprintf("BOUNDARY_ADDR=%s", bc.Address), + fmt.Sprintf("E2E_PASSWORD=%s", bc.AdminLoginPassword), + }, + } + exec, err = docker.ContainerExecCreate(ctx, containerID, execConfig) + require.NoError(t, err) + authResp, err := docker.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{}) + require.NoError(t, err) + t.Cleanup(func() { + authResp.Close() + }) + + outBuf.Reset() + errBuf.Reset() + go func() { + _, err = stdcopy.StdCopy(&outBuf, &errBuf, authResp.Reader) + outputDone <- err + }() + + select { + case err := <-outputDone: + if err != nil { + require.NoError(t, err) + } + break + + case <-ctx.Done(): + require.NoError(t, ctx.Err()) + } + + stdout, err = io.ReadAll(&outBuf) + require.NoError(t, err) + require.NotEmpty(t, string(stdout)) + stderr, err := io.ReadAll(&errBuf) + require.NoError(t, err) + require.Empty(t, string(stderr)) + + var authenticationResult boundary.AuthenticateCliOutput + err = json.Unmarshal(stdout, &authenticationResult) + require.NoError(t, err) + require.Equal(t, http.StatusOK, authenticationResult.StatusCode, string(stdout)) + require.NotEmpty(t, authenticationResult.Item.Attributes["token"]) +} diff --git a/testing/internal/e2e/tests/base_plus/env_test.go b/testing/internal/e2e/tests/base_plus/env_test.go index f34a9b1680..6bb9af316d 100644 --- a/testing/internal/e2e/tests/base_plus/env_test.go +++ b/testing/internal/e2e/tests/base_plus/env_test.go @@ -6,20 +6,21 @@ package base_plus_test import "github.com/kelseyhightower/envconfig" type config struct { - TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. 192.168.0.1 - TargetSshKeyPath string `envconfig:"E2E_SSH_KEY_PATH" required:"true"` // e.g. /Users/username/key.pem - TargetSshUser string `envconfig:"E2E_SSH_USER" required:"true"` // e.g. ubuntu - TargetPort string `envconfig:"E2E_TARGET_PORT" required:"true"` // e.g. 22 - PostgresDbName string `envconfig:"E2E_POSTGRES_DB_NAME" required:"true"` - PostgresUser string `envconfig:"E2E_POSTGRES_USER" required:"true"` - PostgresPassword string `envconfig:"E2E_POSTGRES_PASSWORD" required:"true"` - LdapAddress string `envconfig:"E2E_LDAP_ADDR" required:"true"` // e.g. ldap://ldap - LdapDomainDn string `envconfig:"E2E_LDAP_DOMAIN_DN" required:"true"` // e.g. dc=example,dc=org - LdapAdminDn string `envconfig:"E2E_LDAP_ADMIN_DN" required:"true"` // e.g. cn=admin,dc=example,dc=org - LdapAdminPassword string `envconfig:"E2E_LDAP_ADMIN_PASSWORD" required:"true"` - LdapUserName string `envconfig:"E2E_LDAP_USER_NAME" required:"true"` - LdapUserPassword string `envconfig:"E2E_LDAP_USER_PASSWORD" required:"true"` - LdapGroupName string `envconfig:"E2E_LDAP_GROUP_NAME" required:"true"` + TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. 192.168.0.1 + TargetSshKeyPath string `envconfig:"E2E_SSH_KEY_PATH" required:"true"` // e.g. /Users/username/key.pem + TargetSshUser string `envconfig:"E2E_SSH_USER" required:"true"` // e.g. ubuntu + TargetPort string `envconfig:"E2E_TARGET_PORT" required:"true"` // e.g. 22 + PostgresDbName string `envconfig:"E2E_POSTGRES_DB_NAME" required:"true"` + PostgresUser string `envconfig:"E2E_POSTGRES_USER" required:"true"` + PostgresPassword string `envconfig:"E2E_POSTGRES_PASSWORD" required:"true"` + LdapAddress string `envconfig:"E2E_LDAP_ADDR" required:"true"` // e.g. ldap://ldap + LdapDomainDn string `envconfig:"E2E_LDAP_DOMAIN_DN" required:"true"` // e.g. dc=example,dc=org + LdapAdminDn string `envconfig:"E2E_LDAP_ADMIN_DN" required:"true"` // e.g. cn=admin,dc=example,dc=org + LdapAdminPassword string `envconfig:"E2E_LDAP_ADMIN_PASSWORD" required:"true"` + LdapUserName string `envconfig:"E2E_LDAP_USER_NAME" required:"true"` + LdapUserPassword string `envconfig:"E2E_LDAP_USER_PASSWORD" required:"true"` + LdapGroupName string `envconfig:"E2E_LDAP_GROUP_NAME" required:"true"` + ControllerContainerName string `envconfig:"E2E_CONTROLLER_CONTAINER_NAME" required:"true"` } func loadTestConfig() (*config, error) {