From ff34cb629e9405546787a298019d159025e244da Mon Sep 17 00:00:00 2001 From: Michael Li Date: Mon, 11 Sep 2023 14:25:32 -0400 Subject: [PATCH] test(e2e): Add a test for a single-hop worker (#3690) * refact(e2e): Add env for workers * test(e2e): Add test for single-hop worker * fixup! refact(e2e): Add env for workers * fixup! test(e2e): Add test for single-hop worker * fixup! test(e2e): Add test for single-hop worker * fixup! test(e2e): Add test for single-hop worker * fixup! test(e2e): Add test for single-hop worker --- .github/workflows/enos-run.yml | 3 +- enos/enos-scenario-e2e-aws.hcl | 8 +- ...s-scenario-e2e-docker-base-with-worker.hcl | 187 +++++++++++++++++ .../docker_boundary/boundary-config-bsr.hcl | 10 + .../docker_boundary/boundary-config.hcl | 6 +- enos/modules/docker_boundary/main.tf | 10 + .../worker-config-bsr-downstream.hcl | 1 + .../docker_worker/worker-config-bsr.hcl | 1 + .../worker-config-downstream.hcl | 1 + enos/modules/docker_worker/worker-config.hcl | 1 + enos/modules/test_e2e/main.tf | 57 ++--- enos/modules/test_e2e_docker/main.tf | 74 ++++--- enos/modules/test_e2e_docker/test_runner.sh | 6 +- enos/modules/test_e2e_ui/main.tf | 33 ++- testing/internal/e2e/boundary/target.go | 3 + testing/internal/e2e/tests/aws/env_test.go | 2 +- testing/internal/e2e/tests/aws/worker_test.go | 8 +- .../e2e/tests/base_with_worker/env_test.go | 29 +++ .../target_tcp_worker_connect_ssh_test.go | 196 ++++++++++++++++++ .../testdata/boundary-controller-policy.hcl | 26 +++ 20 files changed, 573 insertions(+), 89 deletions(-) create mode 100644 enos/enos-scenario-e2e-docker-base-with-worker.hcl create mode 100644 testing/internal/e2e/tests/base_with_worker/env_test.go create mode 100644 testing/internal/e2e/tests/base_with_worker/target_tcp_worker_connect_ssh_test.go create mode 100644 testing/internal/e2e/tests/base_with_worker/testdata/boundary-controller-policy.hcl diff --git a/.github/workflows/enos-run.yml b/.github/workflows/enos-run.yml index 5f81acc6e..2fa9e5092 100644 --- a/.github/workflows/enos-run.yml +++ b/.github/workflows/enos-run.yml @@ -81,6 +81,7 @@ jobs: - filter: 'e2e_docker_base builder:crt' - filter: 'e2e_docker_base_with_vault builder:crt' - filter: 'e2e_docker_base_with_postgres builder:crt' + - filter: 'e2e_docker_base_with_worker builder:crt' runs-on: ${{ fromJSON(vars.RUNNER_LARGE) }} env: GITHUB_TOKEN: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }} @@ -184,7 +185,7 @@ jobs: run: | wget https://releases.hashicorp.com/vault/1.12.2/vault_1.12.2_linux_amd64.zip -O /tmp/test-deps/vault.zip - name: Install Vault CLI - if: matrix.filter == 'e2e_aws_base_with_vault builder:crt' || matrix.filter == 'e2e_database' || matrix.filter == 'e2e_ui builder:crt' || matrix.filter == 'e2e_docker_base_with_vault builder:crt' + if: contains(matrix.filter, 'vault') || contains(matrix.filter, 'e2e_docker') || matrix.filter == 'e2e_database' || matrix.filter == 'e2e_ui builder:crt' run: | unzip /tmp/test-deps/vault.zip -d /usr/local/bin - name: GH fix for localhost resolution diff --git a/enos/enos-scenario-e2e-aws.hcl b/enos/enos-scenario-e2e-aws.hcl index 4e7421704..b53279794 100644 --- a/enos/enos-scenario-e2e-aws.hcl +++ b/enos/enos-scenario-e2e-aws.hcl @@ -179,6 +179,10 @@ scenario "e2e_aws" { } } + locals { + egress_tag = "egress" + } + step "create_isolated_worker" { module = module.worker depends_on = [step.create_boundary_cluster] @@ -194,7 +198,7 @@ scenario "e2e_aws" { cluster_tag = step.create_boundary_cluster.cluster_tag controller_addresses = step.create_boundary_cluster.public_controller_addresses controller_sg_id = step.create_boundary_cluster.controller_aux_sg_id - worker_type_tags = ["worker_e2e_test"] + worker_type_tags = [local.egress_tag] config_file_path = "templates/worker.hcl" } } @@ -247,7 +251,7 @@ scenario "e2e_aws" { aws_host_set_filter2 = step.create_tag2_inputs.tag_string aws_host_set_ips2 = step.create_targets_with_tag2.target_ips target_address = step.create_isolated_target.target_ips[0] - worker_tags = step.create_isolated_worker.worker_tags + worker_tag_egress = local.egress_tag } } diff --git a/enos/enos-scenario-e2e-docker-base-with-worker.hcl b/enos/enos-scenario-e2e-docker-base-with-worker.hcl new file mode 100644 index 000000000..63ce73754 --- /dev/null +++ b/enos/enos-scenario-e2e-docker-base-with-worker.hcl @@ -0,0 +1,187 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +# For this scenario to work, add the following line to /etc/hosts +# 127.0.0.1 localhost boundary +# 127.0.0.1 localhost worker + +scenario "e2e_docker_base_with_worker" { + terraform_cli = terraform_cli.default + terraform = terraform.default + providers = [ + provider.enos.default + ] + + matrix { + builder = ["local", "crt"] + } + + locals { + aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) + local_boundary_dir = abspath(var.local_boundary_dir) + local_boundary_src_dir = abspath(var.local_boundary_src_dir) + boundary_docker_image_file = abspath(var.boundary_docker_image_file) + license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + + network_cluster = "e2e_cluster" + network_host = "e2e_host" + network_database = "e2e_db" + + build_path = { + "local" = "/tmp", + "crt" = var.crt_bundle_path == null ? null : abspath(var.crt_bundle_path) + } + tags = merge({ + "Project Name" : var.project_name + "Project" : "Enos", + "Environment" : "ci" + }, var.tags) + } + + step "build_boundary_docker_image" { + module = matrix.builder == "crt" ? module.build_boundary_docker_crt : module.build_boundary_docker_local + + variables { + path = matrix.builder == "crt" ? local.boundary_docker_image_file : "" + cli_build_path = local.build_path[matrix.builder] + } + } + + step "create_docker_network_database" { + module = module.docker_network + variables { + network_name = local.network_database + } + } + + step "create_docker_network_cluster" { + module = module.docker_network + variables { + network_name = local.network_cluster + } + } + + step "create_docker_network_host" { + module = module.docker_network + variables { + network_name = local.network_host + } + } + + step "create_boundary_database" { + depends_on = [ + step.create_docker_network_cluster + ] + variables { + image_name = "${var.docker_mirror}/library/postgres:latest" + network_name = [local.network_database] + } + module = module.docker_postgres + } + + step "read_license" { + skip_step = var.boundary_edition == "oss" + module = module.read_license + + variables { + file_name = local.license_path + } + } + + step "create_boundary" { + module = module.docker_boundary + depends_on = [ + step.create_docker_network_cluster, + step.create_docker_network_database, + step.create_boundary_database, + step.build_boundary_docker_image + ] + variables { + image_name = matrix.builder == "crt" ? var.boundary_docker_image_name : step.build_boundary_docker_image.image_name + network_name = [local.network_cluster, local.network_database] + database_network = local.network_database + postgres_address = step.create_boundary_database.address + boundary_license = var.boundary_edition != "oss" ? step.read_license.license : "" + } + } + + step "create_vault" { + module = module.docker_vault + depends_on = [ + step.create_docker_network_cluster + ] + variables { + image_name = "${var.docker_mirror}/hashicorp/vault:${var.vault_version}" + network_name = [local.network_cluster] + } + } + + step "create_host" { + module = module.docker_openssh_server + depends_on = [ + step.create_docker_network_host + ] + variables { + image_name = "${var.docker_mirror}/linuxserver/openssh-server:latest" + network_name = [local.network_host] + private_key_file_path = local.aws_ssh_private_key_path + } + } + + locals { + egress_tag = "egress" + } + + step "create_worker" { + module = module.docker_worker + depends_on = [ + step.create_docker_network_cluster, + step.create_docker_network_host, + step.build_boundary_docker_image, + step.create_boundary + ] + variables { + image_name = matrix.builder == "crt" ? var.boundary_docker_image_name : step.build_boundary_docker_image.image_name + boundary_license = var.boundary_edition != "oss" ? step.read_license.license : "" + config_file = "worker-config.hcl" + container_name = "worker" + initial_upstream = step.create_boundary.upstream_address + network_name = [local.network_cluster, local.network_host] + tags = [local.egress_tag] + port = "9402" + } + } + + step "run_e2e_test" { + module = module.test_e2e_docker + depends_on = [ + step.create_boundary, + step.create_vault, + step.create_host, + step.create_worker, + ] + variables { + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base_with_worker" + docker_mirror = var.docker_mirror + network_name = step.create_docker_network_cluster.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_host.address + target_port = step.create_host.port + target_user = "ubuntu" + vault_addr = step.create_vault.address + vault_addr_internal = step.create_vault.address_internal + vault_root_token = step.create_vault.token + vault_port = step.create_vault.port + worker_tag_egress = local.egress_tag + worker_tag_collocated = step.create_boundary.worker_tag + } + } +} diff --git a/enos/modules/docker_boundary/boundary-config-bsr.hcl b/enos/modules/docker_boundary/boundary-config-bsr.hcl index ce2b53995..cd2d6c5f1 100644 --- a/enos/modules/docker_boundary/boundary-config-bsr.hcl +++ b/enos/modules/docker_boundary/boundary-config-bsr.hcl @@ -11,6 +11,16 @@ controller { } } +worker { + name = "boundary-collocated-worker" + description = "A worker that runs alongside the controller in the same process" + address = "boundary:9202" + + tags { + type = ["${worker_type_tag}"] + } +} + listener "tcp" { address = "boundary:9200" purpose = "api" diff --git a/enos/modules/docker_boundary/boundary-config.hcl b/enos/modules/docker_boundary/boundary-config.hcl index f4a1ef2f7..3f40bbe47 100644 --- a/enos/modules/docker_boundary/boundary-config.hcl +++ b/enos/modules/docker_boundary/boundary-config.hcl @@ -12,9 +12,13 @@ controller { } worker { - name = "boundary-colocated-worker" + name = "boundary-collocated-worker" description = "A worker that runs alongside the controller in the same process" address = "boundary:9202" + + tags { + type = ["${worker_type_tag}"] + } } listener "tcp" { diff --git a/enos/modules/docker_boundary/main.tf b/enos/modules/docker_boundary/main.tf index d9817ef5d..d12b352ae 100644 --- a/enos/modules/docker_boundary/main.tf +++ b/enos/modules/docker_boundary/main.tf @@ -49,6 +49,11 @@ variable "config_file" { type = string default = "boundary-config.hcl" } +variable "worker_tag" { + description = "Tag to set on worker for use in worker filters" + type = string + default = "collocated" +} resource "docker_image" "boundary" { name = var.image_name @@ -109,6 +114,7 @@ resource "docker_container" "boundary" { upload { content = templatefile("${abspath(path.module)}/${var.config_file}", { + worker_type_tag = var.worker_tag }) file = "/boundary/boundary-config.hcl" } @@ -163,3 +169,7 @@ output "login_name" { output "password" { value = local.password } + +output "worker_tag" { + value = var.worker_tag +} diff --git a/enos/modules/docker_worker/worker-config-bsr-downstream.hcl b/enos/modules/docker_worker/worker-config-bsr-downstream.hcl index f308e37ec..1a995543a 100644 --- a/enos/modules/docker_worker/worker-config-bsr-downstream.hcl +++ b/enos/modules/docker_worker/worker-config-bsr-downstream.hcl @@ -16,6 +16,7 @@ listener "tcp" { worker { name = "${worker_name}" + public_addr = "${worker_name}:${port}" initial_upstreams = ["${initial_upstream}"] tags { diff --git a/enos/modules/docker_worker/worker-config-bsr.hcl b/enos/modules/docker_worker/worker-config-bsr.hcl index d43c5a952..a07cbfbeb 100644 --- a/enos/modules/docker_worker/worker-config-bsr.hcl +++ b/enos/modules/docker_worker/worker-config-bsr.hcl @@ -16,6 +16,7 @@ listener "tcp" { worker { name = "${worker_name}" + public_addr = "${worker_name}:${port}" initial_upstreams = ["${initial_upstream}"] tags { diff --git a/enos/modules/docker_worker/worker-config-downstream.hcl b/enos/modules/docker_worker/worker-config-downstream.hcl index f1203fc80..11655d64f 100644 --- a/enos/modules/docker_worker/worker-config-downstream.hcl +++ b/enos/modules/docker_worker/worker-config-downstream.hcl @@ -16,6 +16,7 @@ listener "tcp" { worker { name = "${worker_name}" + public_addr = "${worker_name}:${port}" initial_upstreams = ["${initial_upstream}"] tags { diff --git a/enos/modules/docker_worker/worker-config.hcl b/enos/modules/docker_worker/worker-config.hcl index c4929ba9b..280cd2e6d 100644 --- a/enos/modules/docker_worker/worker-config.hcl +++ b/enos/modules/docker_worker/worker-config.hcl @@ -16,6 +16,7 @@ listener "tcp" { worker { name = "${worker_name}" + public_addr = "${worker_name}:${port}" initial_upstreams = ["${initial_upstream}"] tags { diff --git a/enos/modules/test_e2e/main.tf b/enos/modules/test_e2e/main.tf index 45c5f2e89..a702754bf 100644 --- a/enos/modules/test_e2e/main.tf +++ b/enos/modules/test_e2e/main.tf @@ -127,9 +127,13 @@ variable "aws_bucket_name" { type = string default = "" } -variable "worker_tags" { - type = list(string) - default = [""] +variable "worker_tag_ingress" { + type = string + default = "" +} +variable "worker_tag_egress" { + type = string + default = "" } variable "test_timeout" { type = string @@ -151,29 +155,30 @@ locals { resource "enos_local_exec" "run_e2e_test" { environment = { - E2E_TESTS = "true", - BOUNDARY_ADDR = var.alb_boundary_api_addr, - BOUNDARY_LICENSE = var.boundary_license, - E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id, - E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name, - E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password, - E2E_TARGET_ADDRESS = var.target_address, - E2E_TARGET_PORT = var.target_port, - E2E_SSH_USER = var.target_user, - E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path, - E2E_SSH_CA_KEY = "", - VAULT_ADDR = local.vault_addr, - VAULT_TOKEN = var.vault_root_token, - E2E_VAULT_ADDR = local.vault_addr_internal, - E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id, - E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key, - E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1, - E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1, - E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2, - E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2, - E2E_AWS_REGION = var.aws_region, - E2E_AWS_BUCKET_NAME = var.aws_bucket_name, - E2E_WORKER_TAG = jsonencode(var.worker_tags), + E2E_TESTS = "true" + BOUNDARY_ADDR = var.alb_boundary_api_addr + BOUNDARY_LICENSE = var.boundary_license + E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id + E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name + E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password + E2E_TARGET_ADDRESS = var.target_address + E2E_TARGET_PORT = var.target_port + E2E_SSH_USER = var.target_user + E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path + E2E_SSH_CA_KEY = "" + VAULT_ADDR = local.vault_addr + VAULT_TOKEN = var.vault_root_token + E2E_VAULT_ADDR = local.vault_addr_internal + E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id + E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key + E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1 + E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1 + E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2 + E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2 + E2E_AWS_REGION = var.aws_region + E2E_AWS_BUCKET_NAME = var.aws_bucket_name + E2E_WORKER_TAG_INGRESS = var.worker_tag_ingress + E2E_WORKER_TAG_EGRESS = var.worker_tag_egress } inline = var.debug_no_run ? [""] : [ diff --git a/enos/modules/test_e2e_docker/main.tf b/enos/modules/test_e2e_docker/main.tf index 27cb5490a..5b6d751d6 100644 --- a/enos/modules/test_e2e_docker/main.tf +++ b/enos/modules/test_e2e_docker/main.tf @@ -160,9 +160,17 @@ variable "aws_bucket_name" { type = string default = "" } -variable "worker_tags" { - type = list(string) - default = [""] +variable "worker_tag_ingress" { + type = string + default = "" +} +variable "worker_tag_egress" { + type = string + default = "" +} +variable "worker_tag_collocated" { + type = string + default = "" } variable "postgres_user" { type = string @@ -206,38 +214,40 @@ resource "docker_image" "go" { resource "enos_local_exec" "run_e2e_test" { depends_on = [docker_image.go] environment = { - TEST_PACKAGE = var.test_package, - TEST_TIMEOUT = var.test_timeout, - TEST_RUNNER_IMAGE = docker_image.go.image_id, - TEST_NETWORK_NAME = var.network_name, - E2E_TESTS = "true", - BOUNDARY_ADDR = var.alb_boundary_api_addr, - E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id, - E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name, - E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password, - E2E_TARGET_ADDRESS = var.target_address, - E2E_TARGET_PORT = var.target_port, - E2E_SSH_USER = var.target_user, - E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path, - E2E_SSH_CA_KEY = var.target_ca_key, - VAULT_ADDR = local.vault_addr, - VAULT_ADDR_INTERNAL = local.vault_addr_internal, - VAULT_TOKEN = var.vault_root_token, - E2E_VAULT_ADDR = local.vault_addr_internal, - E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id, - E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key, - E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1, - E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1, - E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2, - E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2, - E2E_AWS_REGION = var.aws_region, - E2E_AWS_BUCKET_NAME = var.aws_bucket_name, - E2E_WORKER_TAG = jsonencode(var.worker_tags), + TEST_PACKAGE = var.test_package + TEST_TIMEOUT = var.test_timeout + TEST_RUNNER_IMAGE = docker_image.go.image_id + TEST_NETWORK_NAME = var.network_name + E2E_TESTS = "true" + BOUNDARY_ADDR = var.alb_boundary_api_addr + E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id + E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name + E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password + E2E_TARGET_ADDRESS = var.target_address + E2E_TARGET_PORT = var.target_port + E2E_SSH_USER = var.target_user + E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path + E2E_SSH_CA_KEY = var.target_ca_key + VAULT_ADDR = local.vault_addr + VAULT_ADDR_INTERNAL = local.vault_addr_internal + VAULT_TOKEN = var.vault_root_token + E2E_VAULT_ADDR = local.vault_addr_internal + E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id + E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key + E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1 + E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1 + E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2 + E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2 + E2E_AWS_REGION = var.aws_region + E2E_AWS_BUCKET_NAME = var.aws_bucket_name E2E_POSTGRES_USER = var.postgres_user E2E_POSTGRES_PASSWORD = var.postgres_password E2E_POSTGRES_DB_NAME = var.postgres_database_name - BOUNDARY_DIR = abspath(var.local_boundary_src_dir), - BOUNDARY_CLI_DIR = abspath(var.local_boundary_dir), + E2E_WORKER_TAG_INGRESS = var.worker_tag_ingress + E2E_WORKER_TAG_EGRESS = var.worker_tag_egress + E2E_WORKER_TAG_COLLOCATED = var.worker_tag_collocated + 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 65e7ce498..8350a47cf 100644 --- a/enos/modules/test_e2e_docker/test_runner.sh +++ b/enos/modules/test_e2e_docker/test_runner.sh @@ -18,8 +18,8 @@ docker run \ -e "E2E_PASSWORD_ADMIN_LOGIN_NAME=$E2E_PASSWORD_ADMIN_LOGIN_NAME" \ -e "E2E_PASSWORD_ADMIN_PASSWORD=$E2E_PASSWORD_ADMIN_PASSWORD" \ -e "E2E_TARGET_ADDRESS=$E2E_TARGET_ADDRESS" \ - -e "E2E_SSH_USER=$E2E_SSH_USER" \ -e "E2E_TARGET_PORT=$E2E_TARGET_PORT" \ + -e "E2E_SSH_USER=$E2E_SSH_USER" \ -e "E2E_SSH_CA_KEY=$E2E_SSH_CA_KEY" \ -e "E2E_SSH_KEY_PATH=/keys/target.pem" \ -e "VAULT_ADDR=$VAULT_ADDR_INTERNAL" \ @@ -36,7 +36,9 @@ docker run \ -e "E2E_POSTGRES_USER=$E2E_POSTGRES_USER" \ -e "E2E_POSTGRES_PASSWORD=$E2E_POSTGRES_PASSWORD" \ -e "E2E_POSTGRES_DB_NAME=$E2E_POSTGRES_DB_NAME" \ - -e "E2E_WORKER_TAG=$E2E_WORKER_TAG" \ + -e "E2E_WORKER_TAG_INGRESS=$E2E_WORKER_TAG_INGRESS" \ + -e "E2E_WORKER_TAG_EGRESS=$E2E_WORKER_TAG_EGRESS" \ + -e "E2E_WORKER_TAG_COLLOCATED=$E2E_WORKER_TAG_COLLOCATED" \ --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/enos/modules/test_e2e_ui/main.tf b/enos/modules/test_e2e_ui/main.tf index 6dcd67e37..05aeeb377 100644 --- a/enos/modules/test_e2e_ui/main.tf +++ b/enos/modules/test_e2e_ui/main.tf @@ -128,23 +128,22 @@ locals { resource "enos_local_exec" "run_e2e_ui_test" { environment = { - BOUNDARY_ADDR = var.alb_boundary_api_addr, - E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id, - E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name, - E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password, - E2E_TARGET_ADDRESS = var.target_address, - E2E_TARGET_PORT = var.target_port, - E2E_SSH_USER = var.target_user, - E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path, - VAULT_ADDR = local.vault_addr, - VAULT_TOKEN = var.vault_root_token, - E2E_VAULT_ADDR = local.vault_addr_internal, - E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id, - E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key, - E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1, - E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1, - E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2, - E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2 + BOUNDARY_ADDR = var.alb_boundary_api_addr + E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id + E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name + E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password + E2E_TARGET_ADDRESS = var.target_address + E2E_TARGET_PORT = var.target_port + E2E_SSH_USER = var.target_user + E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path + VAULT_ADDR = local.vault_addr + VAULT_TOKEN = var.vault_root_token + E2E_VAULT_ADDR = local.vault_addr_internal + E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id + E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key + E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1 + E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1 + E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2 } inline = var.debug_no_run ? [""] : ["set -o pipefail; PATH=\"${var.local_boundary_dir}:$PATH\" yarn --cwd ${var.local_boundary_ui_src_dir}/ui/admin run e2e 2>&1 | tee ${path.module}/../../test-e2e-ui.log"] diff --git a/testing/internal/e2e/boundary/target.go b/testing/internal/e2e/boundary/target.go index 387a343fa..c05240cfb 100644 --- a/testing/internal/e2e/boundary/target.go +++ b/testing/internal/e2e/boundary/target.go @@ -83,6 +83,9 @@ func CreateNewTargetCli(t testing.TB, ctx context.Context, projectId string, def if opts.WithIngressWorkerFilter != "" { args = append(args, "-ingress-worker-filter", opts.WithIngressWorkerFilter) } + if opts.WithEgressWorkerFilter != "" { + args = append(args, "-egress-worker-filter", opts.WithEgressWorkerFilter) + } output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("targets", "create"), diff --git a/testing/internal/e2e/tests/aws/env_test.go b/testing/internal/e2e/tests/aws/env_test.go index 9875e24a9..d2cb80c41 100644 --- a/testing/internal/e2e/tests/aws/env_test.go +++ b/testing/internal/e2e/tests/aws/env_test.go @@ -18,7 +18,7 @@ type config struct { TargetSshUser string `envconfig:"E2E_SSH_USER" required:"true"` // e.g. "ubuntu" TargetPort string `envconfig:"E2E_TARGET_PORT" required:"true"` // e.g. "22" TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. "192.168.0.1" - WorkerTags string `envconfig:"E2E_WORKER_TAG" required:"true"` // e.g. "[\"tag1\", \"tag2\"]" + WorkerTagEgress string `envconfig:"E2E_WORKER_TAG_EGRESS" required:"true"` // e.g. "egress" } func loadTestConfig() (*config, error) { diff --git a/testing/internal/e2e/tests/aws/worker_test.go b/testing/internal/e2e/tests/aws/worker_test.go index 403e4b57d..39785b544 100644 --- a/testing/internal/e2e/tests/aws/worker_test.go +++ b/testing/internal/e2e/tests/aws/worker_test.go @@ -5,7 +5,6 @@ package aws_test import ( "context" - "encoding/json" "fmt" "testing" @@ -67,17 +66,12 @@ func TestCliWorker(t *testing.T) { t.Logf("Successfully detected connection failure") // Set correct worker filter, expect connection success - var workerTags []string - err = json.Unmarshal([]byte(c.WorkerTags), &workerTags) - require.NoError(t, err) - require.NotEmpty(t, workerTags) - t.Logf("Setting correct worker filter...") output = e2e.RunCommand(ctx, "boundary", e2e.WithArgs( "targets", "update", "tcp", "-id", newTargetId, - "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, workerTags[0]), + "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagEgress), "-format", "json", ), ) diff --git a/testing/internal/e2e/tests/base_with_worker/env_test.go b/testing/internal/e2e/tests/base_with_worker/env_test.go new file mode 100644 index 000000000..7ecd3ab80 --- /dev/null +++ b/testing/internal/e2e/tests/base_with_worker/env_test.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package base_with_worker_test + +import "github.com/kelseyhightower/envconfig" + +type config struct { + TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. 192.168.0.1 + TargetPort string `envconfig:"E2E_TARGET_PORT" required:"true"` // e.g. 22 + TargetSshUser string `envconfig:"E2E_SSH_USER" required:"true"` // e.g. ubuntu + TargetSshKeyPath string `envconfig:"E2E_SSH_KEY_PATH" required:"true"` // e.g. /Users/username/key.pem + WorkerTagIngress string `envconfig:"E2E_WORKER_TAG_INGRESS" required:"true"` // e.g. "ingress" + WorkerTagEgress string `envconfig:"E2E_WORKER_TAG_EGRESS" required:"true"` // e.g. "egress" + WorkerTagCollocated string `envconfig:"E2E_WORKER_TAG_COLLOCATED" required:"true"` // e.g. "collocated" + // VaultAddr is the address that the Boundary server uses to interact with the running Vault instance + VaultAddr string `envconfig:"E2E_VAULT_ADDR" required:"true"` // e.g. "http://127.0.0.1:8200" + VaultSecretPath string `envconfig:"E2E_VAULT_SECRET_PATH" default:"e2e_secrets"` +} + +func loadTestConfig() (*config, error) { + var c config + err := envconfig.Process("", &c) + if err != nil { + return nil, err + } + + return &c, nil +} diff --git a/testing/internal/e2e/tests/base_with_worker/target_tcp_worker_connect_ssh_test.go b/testing/internal/e2e/tests/base_with_worker/target_tcp_worker_connect_ssh_test.go new file mode 100644 index 000000000..7b64764d0 --- /dev/null +++ b/testing/internal/e2e/tests/base_with_worker/target_tcp_worker_connect_ssh_test.go @@ -0,0 +1,196 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package base_with_worker_test + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/hashicorp/boundary/api/credentiallibraries" + "github.com/hashicorp/boundary/internal/target" + "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/require" +) + +// TestCliTcpTargetWorkerConnectTarget uses the boundary cli to do the +// following... +// - create a target with an egress worker filter and confirm that you can +// connect to it +// - update the target to use a worker filter that can't reach the host and +// confirm that you cannot connect to it +// - attempt to create a target with an ingress worker filter and confirm that +// the operation fails +// Note: This test is specific to the community version +func TestCliTcpTargetWorkerConnectTarget(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("openssh-server"), + target.WithEgressWorkerFilter(fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagEgress)), + ) + + // Configure vault + boundaryPolicyName, kvPolicyFilePath := vault.Setup(t) + t.Cleanup(func() { + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("policy", "delete", boundaryPolicyName), + ) + require.NoError(t, output.Err, string(output.Stderr)) + }) + + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("secrets", "enable", fmt.Sprintf("-path=%s", 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 + privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath) + kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath) + t.Cleanup(func() { + output := e2e.RunCommand(ctx, "vault", + e2e.WithArgs("policy", "delete", kvPolicyName), + ) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Log("Created Vault Credential") + + // Create vault token for boundary + output = e2e.RunCommand(ctx, "vault", + e2e.WithArgs( + "token", "create", + "-no-default-policy=true", + fmt.Sprintf("-policy=%s", boundaryPolicyName), + fmt.Sprintf("-policy=%s", kvPolicyName), + "-orphan=true", + "-period=20m", + "-renewable=true", + "-format=json", + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + var tokenCreateResult vault.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 + newCredentialStoreId := boundary.CreateNewCredentialStoreVaultCli(t, ctx, newProjectId, c.VaultAddr, credStoreToken) + + // Create a credential library + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "credential-libraries", "create", "vault", + "-credential-store-id", newCredentialStoreId, + "-vault-path", fmt.Sprintf("%s/data/%s", c.VaultSecretPath, privateKeySecretName), + "-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 + boundary.AddBrokeredCredentialSourceToTargetCli(t, ctx, newTargetId, newCredentialLibraryId) + + // Connect to target and print host's IP address using retrieved credentials + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "connect", "ssh", + "-target-id", newTargetId, + "-remote-command", "hostname -i", + "--", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + ), + ) + // Note: If this test fails due to: "Unable to connect to worker at + // worker:9402", modify your /etc/hosts file to contain... + // `127.0.0.1 localhost worker`` + require.NoError(t, output.Err, string(output.Stderr)) + require.Equal(t, c.TargetAddress, strings.TrimSpace(string(output.Stdout))) + t.Log("Successfully connected to target") + + // Update the egress filter to one that can't access the target + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "targets", "update", "tcp", + "-id", newTargetId, + "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagCollocated), + ), + ) + require.NoError(t, output.Err, string(output.Stderr)) + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "connect", "ssh", + "-target-id", newTargetId, + "-remote-command", "hostname -i", + "--", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + ), + ) + require.Error(t, output.Err) + require.Equal(t, output.ExitCode, 255) + t.Log("Successfully failed to connect to target with wrong worker filter") + + // Try creating targets with an ingress worker filter. This should result in + // an error + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "targets", "create", "tcp", + "-name", "Target with Ingress Filter", + "-scope-id", newProjectId, + "-default-port", c.TargetPort, + "-ingress-worker-filter", `"tag" in "/tags/type"`, + ), + ) + require.Error(t, output.Err, "Unexpectedly created a target with an ingress worker filter") + + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "targets", "create", "tcp", + "-name", "Target with Ingress Filter", + "-scope-id", newProjectId, + "-default-port", c.TargetPort, + "-ingress-worker-filter", `"tag" in "/tags/type"`, + "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagEgress), + ), + ) + require.Error(t, output.Err, "Unexpectedly created a target with an ingress worker filter") +} diff --git a/testing/internal/e2e/tests/base_with_worker/testdata/boundary-controller-policy.hcl b/testing/internal/e2e/tests/base_with_worker/testdata/boundary-controller-policy.hcl new file mode 100644 index 000000000..bedccc3a5 --- /dev/null +++ b/testing/internal/e2e/tests/base_with_worker/testdata/boundary-controller-policy.hcl @@ -0,0 +1,26 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +path "auth/token/lookup-self" { + capabilities = ["read"] +} + +path "auth/token/renew-self" { + capabilities = ["update"] +} + +path "auth/token/revoke-self" { + capabilities = ["update"] +} + +path "sys/leases/renew" { + capabilities = ["update"] +} + +path "sys/leases/revoke" { + capabilities = ["update"] +} + +path "sys/capabilities-self" { + capabilities = ["update"] +}