diff --git a/.github/workflows/enos-run.yml b/.github/workflows/enos-run.yml index bd3dfcf47..b042c8819 100644 --- a/.github/workflows/enos-run.yml +++ b/.github/workflows/enos-run.yml @@ -76,7 +76,7 @@ jobs: fail-fast: false # don't fail as that can skip required cleanup steps for jobs matrix: include: - - filter: 'e2e_aws builder:crt' + - filter: 'e2e_aws builder:crt ip_version:4' - filter: 'e2e_database' - filter: 'e2e_docker_base builder:crt' - filter: 'e2e_docker_base_plus builder:crt' @@ -279,7 +279,7 @@ jobs: - name: Split matrix filter name id: split run: | - SCENARIO=$(echo "${{ matrix.filter }}" | cut -d' ' -f1) + SCENARIO=$(echo "${{ matrix.filter }}" | cut -d' ' -f1,3 | sed 's/:/_/g') echo fragment="${SCENARIO}" >> "$GITHUB_OUTPUT" - name: Upload e2e tests output uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 diff --git a/enos/enos-modules.hcl b/enos/enos-modules.hcl index 0c7c7facc..965fc96a8 100644 --- a/enos/enos-modules.hcl +++ b/enos/enos-modules.hcl @@ -82,6 +82,18 @@ module "aws_vpc" { } } +module "aws_vpc_ipv6" { + source = "./modules/aws_vpc_ipv6" + + environment = var.environment + common_tags = { + "Project" : "Enos", + "Project Name" : "qti-enos-boundary", + "Enos User" : var.enos_user, + "Environment" : var.environment + } +} + module "read_license" { source = "./modules/read_license" } diff --git a/enos/enos-scenario-e2e-aws-base-with-vault.hcl b/enos/enos-scenario-e2e-aws-base-with-vault.hcl index 2a32de78b..b352f5885 100644 --- a/enos/enos-scenario-e2e-aws-base-with-vault.hcl +++ b/enos/enos-scenario-e2e-aws-base-with-vault.hcl @@ -45,7 +45,7 @@ scenario "e2e_aws_base_with_vault" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } @@ -108,6 +108,7 @@ scenario "e2e_aws_base_with_vault" { ] variables { + deploy = true ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] instance_type = var.vault_instance_type instance_count = 1 diff --git a/enos/enos-scenario-e2e-aws-base.hcl b/enos/enos-scenario-e2e-aws-base.hcl index 87d04dd29..43741023f 100644 --- a/enos/enos-scenario-e2e-aws-base.hcl +++ b/enos/enos-scenario-e2e-aws-base.hcl @@ -45,7 +45,7 @@ scenario "e2e_aws_base" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-aws.hcl b/enos/enos-scenario-e2e-aws.hcl index dcd4cccde..35779d4c4 100644 --- a/enos/enos-scenario-e2e-aws.hcl +++ b/enos/enos-scenario-e2e-aws.hcl @@ -11,18 +11,22 @@ scenario "e2e_aws" { ] matrix { - builder = ["local", "crt"] + builder = ["local", "crt"] + ip_version = ["4"] } locals { aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) boundary_install_dir = abspath(var.boundary_install_dir) local_boundary_dir = var.local_boundary_dir != null ? abspath(var.local_boundary_dir) : null - license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + boundary_license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + vault_license_path = abspath(var.vault_license_path != null ? var.vault_license_path : joinpath(path.root, "./support/vault.hclic")) + 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", @@ -41,12 +45,12 @@ scenario "e2e_aws" { } } - step "read_license" { + step "read_boundary_license" { skip_step = var.boundary_edition == "oss" module = module.read_license variables { - file_name = local.license_path + license_path = local.boundary_license_path } } @@ -64,7 +68,8 @@ scenario "e2e_aws" { } step "create_base_infra" { - module = module.aws_vpc + module = matrix.ip_version == "4" ? module.aws_vpc : module.aws_vpc_ipv6 + depends_on = [ step.find_azs, ] @@ -75,30 +80,59 @@ scenario "e2e_aws" { } } + step "create_vault_cluster" { + module = module.vault + depends_on = [ + step.create_base_infra, + ] + + variables { + deploy = matrix.ip_version == "4" ? false : true + ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] + instance_type = var.vault_instance_type + instance_count = 1 + kms_key_arn = step.create_base_infra.kms_key_arn + storage_backend = "raft" + unseal_method = "shamir" + ip_version = matrix.ip_version + vault_release = { + version = var.vault_version + edition = "oss" + } + vpc_id = step.create_base_infra.vpc_id + } + } + step "create_boundary_cluster" { module = module.aws_boundary depends_on = [ step.create_base_infra, step.create_db_password, - step.build_boundary + step.build_boundary, + step.create_vault_cluster ] variables { - boundary_binary_name = var.boundary_binary_name - boundary_install_dir = local.boundary_install_dir - boundary_license = var.boundary_edition != "oss" ? step.read_license.license : null - common_tags = local.tags - controller_instance_type = var.controller_instance_type - controller_count = var.controller_count - db_pass = step.create_db_password.string - kms_key_arn = step.create_base_infra.kms_key_arn - local_artifact_path = step.build_boundary.artifact_path - ubuntu_ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] - vpc_id = step.create_base_infra.vpc_id - vpc_tag_module = step.create_base_infra.vpc_tag_module - worker_count = var.worker_count - worker_instance_type = var.worker_instance_type - aws_region = var.aws_region + boundary_binary_name = var.boundary_binary_name + boundary_install_dir = local.boundary_install_dir + boundary_license = var.boundary_edition != "oss" ? step.read_boundary_license.license : null + common_tags = local.tags + controller_instance_type = var.controller_instance_type + controller_count = var.controller_count + db_pass = step.create_db_password.string + kms_key_arn = step.create_base_infra.kms_key_arn + local_artifact_path = step.build_boundary.artifact_path + ubuntu_ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] + vpc_id = step.create_base_infra.vpc_id + vpc_tag_module = step.create_base_infra.vpc_tag_module + worker_count = var.worker_count + worker_instance_type = var.worker_instance_type + aws_region = var.aws_region + ip_version = matrix.ip_version + controller_config_file_path = matrix.ip_version == "4" ? "templates/controller.hcl" : "templates/controller_vault_kms.hcl" + worker_config_file_path = matrix.ip_version == "4" ? "templates/worker.hcl" : "templates/worker_vault_kms.hcl" + vault_address = matrix.ip_version == "4" ? "" : step.create_vault_cluster.instance_public_ips[0] + vault_transit_token = matrix.ip_version == "4" ? "" : step.create_vault_cluster.vault_transit_token } } @@ -129,6 +163,9 @@ scenario "e2e_aws" { target_count = var.target_count <= 1 ? 2 : var.target_count additional_tags = step.create_tag1_inputs.tag_map subnet_ids = step.create_boundary_cluster.subnet_ids + ingress_cidr = matrix.ip_version == "4" ? ["10.0.0.0/8"] : [] + ingress_ipv6_cidr = step.create_boundary_cluster.worker_ipv6_cidr + ip_version = matrix.ip_version } } @@ -153,7 +190,7 @@ scenario "e2e_aws" { } locals { - egress_tag = "egress" + isolated_tag = "isolated" } step "create_isolated_worker" { @@ -164,13 +201,16 @@ scenario "e2e_aws" { availability_zones = step.create_base_infra.availability_zone_names kms_key_arn = step.create_base_infra.kms_key_arn ubuntu_ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] + vpc_cidr = step.create_base_infra.vpc_cidr + vpc_cidr_ipv6 = matrix.ip_version == "4" ? "" : step.create_base_infra.vpc_cidr_ipv6 local_artifact_path = step.build_boundary.artifact_path boundary_install_dir = local.boundary_install_dir name_prefix = step.create_boundary_cluster.name_prefix 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 = [local.egress_tag] + worker_type_tags = [local.isolated_tag] + ip_version = matrix.ip_version config_file_path = "templates/worker.hcl" } } @@ -204,8 +244,10 @@ scenario "e2e_aws" { vpc_id = step.create_base_infra.vpc_id target_count = 1 subnet_ids = step.create_isolated_worker.subnet_ids - ingress_cidr = ["10.13.9.0/24"] + ingress_cidr = matrix.ip_version == "4" ? ["10.13.9.0/24"] : [] + ingress_ipv6_cidr = step.create_isolated_worker.worker_ipv6_cidr additional_tags = step.create_tag2_inputs.tag_map + ip_version = matrix.ip_version } } @@ -237,9 +279,10 @@ scenario "e2e_aws" { aws_host_set_filter2 = step.create_tag2_inputs.tag_string aws_host_set_ips2 = step.create_isolated_target.target_private_ips target_address = step.create_isolated_target.target_private_ips[0] - worker_tag_egress = local.egress_tag + worker_tag_isolated = local.isolated_tag max_page_size = step.create_boundary_cluster.max_page_size aws_region = var.aws_region + ip_version = matrix.ip_version } } diff --git a/enos/enos-scenario-e2e-database.hcl b/enos/enos-scenario-e2e-database.hcl index 7db1a90ff..c66997d34 100644 --- a/enos/enos-scenario-e2e-database.hcl +++ b/enos/enos-scenario-e2e-database.hcl @@ -26,7 +26,7 @@ scenario "e2e_database" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } @@ -114,7 +114,7 @@ scenario "e2e_database" { module = module.test_e2e depends_on = [ step.create_targets_with_tag, - step.iam_setup, + step.iam_setup ] variables { diff --git a/enos/enos-scenario-e2e-docker-base-plus.hcl b/enos/enos-scenario-e2e-docker-base-plus.hcl index 216c7a623..01a35da97 100644 --- a/enos/enos-scenario-e2e-docker-base-plus.hcl +++ b/enos/enos-scenario-e2e-docker-base-plus.hcl @@ -68,7 +68,7 @@ scenario "e2e_docker_base_plus" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-base-with-gcp.hcl b/enos/enos-scenario-e2e-docker-base-with-gcp.hcl index d70f47648..91adb7f52 100644 --- a/enos/enos-scenario-e2e-docker-base-with-gcp.hcl +++ b/enos/enos-scenario-e2e-docker-base-with-gcp.hcl @@ -69,7 +69,7 @@ scenario "e2e_docker_base_with_gcp" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-base-with-vault.hcl b/enos/enos-scenario-e2e-docker-base-with-vault.hcl index 08c99064d..4e14211a2 100644 --- a/enos/enos-scenario-e2e-docker-base-with-vault.hcl +++ b/enos/enos-scenario-e2e-docker-base-with-vault.hcl @@ -70,7 +70,7 @@ scenario "e2e_docker_base_with_vault" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-base-with-worker.hcl b/enos/enos-scenario-e2e-docker-base-with-worker.hcl index 03a30486c..bf75db952 100644 --- a/enos/enos-scenario-e2e-docker-base-with-worker.hcl +++ b/enos/enos-scenario-e2e-docker-base-with-worker.hcl @@ -85,7 +85,7 @@ scenario "e2e_docker_base_with_worker" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-base.hcl b/enos/enos-scenario-e2e-docker-base.hcl index 0daa43357..885a0dab0 100644 --- a/enos/enos-scenario-e2e-docker-base.hcl +++ b/enos/enos-scenario-e2e-docker-base.hcl @@ -68,7 +68,7 @@ scenario "e2e_docker_base" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl b/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl index bb3d90e85..36e108bcd 100644 --- a/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl +++ b/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl @@ -85,7 +85,7 @@ scenario "e2e_docker_worker_registration_controller_led" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-docker-worker-registration-worker-led.hcl b/enos/enos-scenario-e2e-docker-worker-registration-worker-led.hcl index 550388426..a896848a1 100644 --- a/enos/enos-scenario-e2e-docker-worker-registration-worker-led.hcl +++ b/enos/enos-scenario-e2e-docker-worker-registration-worker-led.hcl @@ -85,7 +85,7 @@ scenario "e2e_docker_worker_registration_worker_led" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-scenario-e2e-ui-aws.hcl b/enos/enos-scenario-e2e-ui-aws.hcl index 915d47e02..4c6142edb 100644 --- a/enos/enos-scenario-e2e-ui-aws.hcl +++ b/enos/enos-scenario-e2e-ui-aws.hcl @@ -46,7 +46,7 @@ scenario "e2e_ui_aws" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } @@ -114,6 +114,7 @@ scenario "e2e_ui_aws" { ] variables { + deploy = true ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] instance_type = var.vault_instance_type instance_count = 1 diff --git a/enos/enos-scenario-e2e-ui-docker.hcl b/enos/enos-scenario-e2e-ui-docker.hcl index 5b13ac189..080f5e3dc 100644 --- a/enos/enos-scenario-e2e-ui-docker.hcl +++ b/enos/enos-scenario-e2e-ui-docker.hcl @@ -68,7 +68,7 @@ scenario "e2e_ui_docker" { module = module.read_license variables { - file_name = local.license_path + license_path = local.license_path } } diff --git a/enos/enos-variables.hcl b/enos/enos-variables.hcl index 19a5bf867..02c6a33b7 100644 --- a/enos/enos-variables.hcl +++ b/enos/enos-variables.hcl @@ -175,6 +175,12 @@ variable "boundary_license_path" { default = null } +variable "vault_license_path" { + description = "Vault license path" + type = string + default = null +} + variable "go_test_timeout" { description = "Timeout for go test used in e2e tests" type = string diff --git a/enos/ipv6-aws-host.md.md b/enos/ipv6-aws-host.md.md new file mode 100644 index 000000000..85cb2ca9f --- /dev/null +++ b/enos/ipv6-aws-host.md.md @@ -0,0 +1,168 @@ +## running ipv6-only enos tests without an ipv6 connection + +1. create ipv6-enabled vpc +2. create ipv6-enabled subnet +3. create internet gateway and route + - likely needs more detail +4. create t2-medium instance + - attach the vpc + - add public ipv4 + - add public ipv6 + - use at least 20gb storage +5. create a new policy and attach it to the ec2 instance + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "ec2:DescribeImages", + "ec2:DescribeInstanceTypeOfferings", + "ec2:DescribeAvailabilityZones", + "ec2:CreateVpc", + "kms:CreateKey", + "ec2:CreateTags", + "kms:DescribeKey", + "ec2:DescribeVpcs", + "kms:ScheduleKeyDeletion", + "kms:GetKeyPolicy", + "kms:GetKeyRotationStatus", + "kms:ListResourceTags", + "kms:CreateAlias", + "ec2:DeleteVpc", + "ec2:ModifyVpcAttribute", + "kms:ListAliases", + "ec2:DescribeVpcAttribute", + "kms:DeleteAlias", + "ec2:CreateSubnet", + "ec2:CreateInternetGateway", + "ec2:CreateSecurityGroup", + "ec2:DescribeSecurityGroups", + "ec2:AttachInternetGateway", + "ec2:DescribeSubnets", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteSubnet", + "ec2:DeleteSecurityGroup", + "ec2:RevokeSecurityGroupEgress", + "ec2:ModifySubnetAttribute", + "ec2:DetachInternetGateway", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:DeleteInternetGateway", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:DescribeInternetGateways", + "ec2:DescribeRouteTables", + "ec2:CreateRoute", + "ec2:RevokeSecurityGroupIngress", + "iam:CreateUser", + "iam:DeleteUser", + "iam:TagUser", + "iam:GetUser", + "iam:ListGroupsForUser", + "iam:CreateAccessKey", + "iam:DeleteAccessKey", + "iam:PutUserPolicy", + "iam:GetUserPolicy", + "iam:DeleteUserPolicy", + "ec2:DeleteRoute", + "ec2:RunInstances", + "ec2:DescribeInstances", + "rds:CreateDBSubnetGroup", + "iam:CreateRole", + "elasticloadbalancing:DescribeTargetGroups", + "ec2:TerminateInstances", + "ec2:DescribeInstanceTypes", + "rds:DescribeDBSubnetGroups", + "iam:GetRole", + "elasticloadbalancing:CreateTargetGroup", + "elasticloadbalancing:DeleteTargetGroup", + "rds:DeleteDBSubnetGroup", + "iam:ListInstanceProfilesForRole", + "iam:DeleteRole", + "ec2:DescribeTags", + "rds:ListTagsForResource", + "iam:ListRolePolicies", + "elasticloadbalancing:AddTags", + "ec2:DescribeInstanceAttribute", + "rds:CreateDBInstance", + "iam:ListAttachedRolePolicies", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "rds:DeleteDBInstance", + "ec2:DescribeVolumes", + "rds:AddTagsToResource", + "iam:PutRolePolicy", + "iam:DeleteRolePolicy", + "iam:CreateInstanceProfile", + "iam:DeleteInstanceProfile", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "ec2:DescribeInstanceCreditSpecifications", + "rds:DescribeDBInstances", + "iam:GetRolePolicy", + "iam:GetInstanceProfile", + "elasticloadbalancing:DescribeTags", + "iam:RemoveRoleFromInstanceProfile", + "iam:AddRoleToInstanceProfile", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:DeregisterTargets", + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DeleteListener", + "ec2:AssociateRouteTable", + "ec2:DisassociateRouteTable", + "ec2:CreateIpam", + "ec2:DeleteIpam", + "ec2:ModifyIpam", + "ec2:DescribeIpams", + "ec2:CreateIpamPool", + "ec2:DeleteIpamPool", + "ec2:ModifyIpamPool", + "ec2:DescribeIpamPools", + "ec2:AllocateIpamPoolCidr", + "ec2:ProvisionIpamPoolCidr", + "s3:CreateBucket", + "s3:DeleteBucket", + "s3:ListBucket", + "s3:ListAllMyBuckets", + "s3:GetAccelerateConfiguration", + "s3:PutAccelerateConfiguration", + "s3:GetLifecycleConfiguration", + "s3:PutLifecycleConfiguration", + "s3:GetReplicationConfiguration", + "s3:PutReplicationConfiguration", + "s3:GetBucket*", + "s3:PutBucket*", + "s3:GetEncryptionConfiguration", + "s3:PutEncryptionConfiguration", + "ec2:AttachNetworkInterface", + "ec2:DetachNetworkInterface", + "iam:CreateServiceLinkedRole", + "iam:DeleteServiceLinkedRole", + "iam:PassRole" + ], + "Resource": "*" + } + ] + } + ``` +6. change sudo password + - `sudo passwd ec2-user` + - enter new password (doesn't need to be secure) +7. install git + - `sudo yum install git` +8. install homebrew + - https://brew.sh/ + - follow post-install instructions given by homebrew in the cli (install gcc and other utils) +9. install enos deps + - https://github.com/hashicorp/boundary/blob/main/enos/README.md + - you do **not** need to install doormat + - you will need to create/add/use a github token on the ec2 instance + - https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account + - also install nvm and run `nvm install 20` and `nvm use 20` + - https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating +10. you should now be able to run enos instances from the enos directory diff --git a/enos/modules/aws_boundary/alb.tf b/enos/modules/aws_boundary/alb.tf index 335e8b81c..ab436c46e 100644 --- a/enos/modules/aws_boundary/alb.tf +++ b/enos/modules/aws_boundary/alb.tf @@ -1,11 +1,22 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 +locals { + stack = { + "4" = "ipv4", + "dual" = "dualstack", + "6" = "dualstack-without-public-ipv4", + } +} + resource "aws_alb" "boundary_alb" { name = "boundary-alb-${random_string.cluster_id.result}" depends_on = [aws_instance.controller] security_groups = [aws_security_group.boundary_alb_sg.id] subnets = data.aws_subnets.infra.ids + + ip_address_type = lookup(local.stack, var.ip_version, local.stack["4"]) + tags = merge(local.common_tags, { Name = "boundary-alb-${random_string.cluster_id.result}" @@ -14,10 +25,11 @@ resource "aws_alb" "boundary_alb" { } resource "aws_alb_target_group" "boundary_tg" { - name = "boundary-tg-${random_string.cluster_id.result}" - port = var.listener_api_port - protocol = "HTTP" - vpc_id = var.vpc_id + name = "boundary-tg-${random_string.cluster_id.result}" + port = var.listener_api_port + protocol = "HTTP" + vpc_id = var.vpc_id + ip_address_type = var.ip_version == "6" ? "ipv6" : "ipv4" health_check { path = var.healthcheck_path @@ -26,6 +38,7 @@ resource "aws_alb_target_group" "boundary_tg" { timeout = 2 healthy_threshold = 2 } + tags = merge(local.common_tags, { Name = "boundary-tg-${random_string.cluster_id.result}" @@ -45,6 +58,7 @@ resource "aws_alb_listener" "boundary" { load_balancer_arn = aws_alb.boundary_alb.arn port = var.alb_listener_api_port protocol = "HTTP" + default_action { target_group_arn = aws_alb_target_group.boundary_tg.arn type = "forward" diff --git a/enos/modules/aws_boundary/boundary-instances.tf b/enos/modules/aws_boundary/boundary-instances.tf index 9005f5082..e074e6003 100644 --- a/enos/modules/aws_boundary/boundary-instances.tf +++ b/enos/modules/aws_boundary/boundary-instances.tf @@ -15,6 +15,7 @@ resource "aws_instance" "controller" { key_name = var.ssh_aws_keypair iam_instance_profile = aws_iam_instance_profile.boundary_profile.name monitoring = var.controller_monitoring + ipv6_address_count = local.network_stack[var.ip_version].ipv6_address_count root_block_device { iops = var.controller_ebs_iops @@ -41,6 +42,7 @@ resource "aws_instance" "worker" { key_name = var.ssh_aws_keypair iam_instance_profile = aws_iam_instance_profile.boundary_profile.name monitoring = var.worker_monitoring + ipv6_address_count = local.network_stack[var.ip_version].ipv6_address_count root_block_device { iops = var.worker_ebs_iops @@ -69,7 +71,7 @@ resource "enos_bundle_install" "controller" { transport = { ssh = { - host = aws_instance.controller[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.controller[tonumber(each.value)].ipv6_addresses[0] : aws_instance.controller[tonumber(each.value)].public_ip } } } @@ -86,7 +88,7 @@ resource "enos_remote_exec" "update_path_controller" { transport = { ssh = { - host = aws_instance.controller[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.controller[tonumber(each.value)].ipv6_addresses[0] : aws_instance.controller[tonumber(each.value)].public_ip } } } @@ -101,26 +103,29 @@ resource "enos_file" "controller_config" { destination = "/etc/boundary/boundary.hcl" content = templatefile("${path.module}/${var.controller_config_file_path}", { id = each.value - dbuser = var.db_user, - dbpass = var.db_pass, + dbuser = var.db_user + dbpass = var.db_pass dbhost = var.db_host == null ? aws_db_instance.boundary[0].address : var.db_host dbport = var.db_port - dbname = local.db_name, + dbname = local.db_name db_max_open_connections = var.db_max_open_connections - kms_key_id = data.aws_kms_key.kms_key.id, - local_ipv4 = aws_instance.controller[tonumber(each.value)].private_ip + kms_key_id = data.aws_kms_key.kms_key.id + listener_address = var.ip_version == "4" ? "0.0.0.0" : "[::]" api_port = var.listener_api_port ops_port = var.listener_ops_port + cluster_address = var.ip_version == "4" ? aws_instance.controller[tonumber(each.value)].private_ip : format("[%s]", aws_instance.controller[tonumber(each.value)].ipv6_addresses[0]) cluster_port = var.listener_cluster_port region = var.aws_region max_page_size = var.max_page_size audit_log_dir = local.audit_log_directory + vault_address = local.network_stack[var.ip_version].vault_address + vault_transit_token = var.vault_transit_token }) for_each = toset([for idx in range(var.controller_count) : tostring(idx)]) transport = { ssh = { - host = aws_instance.controller[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.controller[tonumber(each.value)].ipv6_addresses[0] : aws_instance.controller[tonumber(each.value)].public_ip } } } @@ -135,7 +140,7 @@ resource "enos_boundary_init" "controller" { transport = { ssh = { - host = try(aws_instance.controller[0].public_ip, null) + host = try(var.ip_version == "6" ? aws_instance.controller[0].ipv6_addresses[0] : aws_instance.controller[0].public_ip, null) } } @@ -152,7 +157,7 @@ resource "enos_boundary_start" "controller_start" { transport = { ssh = { - host = aws_instance.controller[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.controller[tonumber(each.value)].ipv6_addresses[0] : aws_instance.controller[tonumber(each.value)].public_ip } } @@ -177,10 +182,11 @@ resource "enos_remote_exec" "create_controller_audit_log_dir" { transport = { ssh = { - host = aws_instance.controller[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.controller[tonumber(each.value)].ipv6_addresses[0] : aws_instance.controller[tonumber(each.value)].public_ip } } } + resource "enos_bundle_install" "worker" { depends_on = [aws_instance.worker] for_each = toset([for idx in range(var.worker_count) : tostring(idx)]) @@ -193,7 +199,7 @@ resource "enos_bundle_install" "worker" { transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } @@ -210,7 +216,7 @@ resource "enos_remote_exec" "update_path_worker" { transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } @@ -221,19 +227,22 @@ resource "enos_file" "worker_config" { content = templatefile("${path.module}/${var.worker_config_file_path}", { id = each.value kms_key_id = data.aws_kms_key.kms_key.id, - controller_ips = jsonencode(aws_instance.controller.*.private_ip), - public_addr = aws_instance.worker[tonumber(each.value)].public_ip + controller_ips = var.ip_version == "4" ? jsonencode(aws_instance.controller.*.private_ip) : jsonencode(formatlist("[%s]:9201", flatten(aws_instance.controller.*.ipv6_addresses))) + listener_address = var.ip_version == "4" ? "0.0.0.0" : "[::]" + public_address = var.ip_version == "6" ? format("[%s]", aws_instance.worker[tonumber(each.value)].ipv6_addresses[0]) : aws_instance.worker[tonumber(each.value)].public_ip region = var.aws_region type = jsonencode(var.worker_type_tags) recording_storage_path = var.recording_storage_path audit_log_dir = local.audit_log_directory hcp_boundary_cluster_id = var.hcp_boundary_cluster_id + vault_address = local.network_stack[var.ip_version].vault_address + vault_transit_token = var.vault_transit_token }) for_each = toset([for idx in range(var.worker_count) : tostring(idx)]) transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } @@ -250,7 +259,7 @@ resource "enos_boundary_start" "worker_start" { transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } @@ -270,7 +279,7 @@ resource "enos_remote_exec" "create_worker_audit_log_dir" { transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } @@ -282,7 +291,7 @@ resource "enos_remote_exec" "get_worker_token" { inline = ["timeout 10s bash -c 'set -eo pipefail; until journalctl -u boundary.service | cat | grep \"Worker Auth Registration Request: .*\" | rev | cut -d \" \" -f 1 | rev | xargs; do sleep 2; done'"] transport = { ssh = { - host = aws_instance.worker[tonumber(each.value)].public_ip + host = var.ip_version == "6" ? aws_instance.worker[tonumber(each.value)].ipv6_addresses[0] : aws_instance.worker[tonumber(each.value)].public_ip } } } diff --git a/enos/modules/aws_boundary/main.tf b/enos/modules/aws_boundary/main.tf index e962e966b..b85d917ba 100644 --- a/enos/modules/aws_boundary/main.tf +++ b/enos/modules/aws_boundary/main.tf @@ -25,6 +25,49 @@ locals { Pet = random_pet.default.id }, ) + + network_stack = { + "4" = { + ingress_cidr_blocks = flatten([ + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), + join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), + formatlist("%s/32", var.alb_sg_additional_ips), + ]) + ingress_ipv6_cidr_blocks = [], + egress_cidr_blocks = ["0.0.0.0/0"], + egress_ipv6_cidr_blocks = [], + ipv6_address_count = 0, + vault_address = var.vault_address, + }, + "6" = { + ingress_cidr_blocks = [], + ingress_ipv6_cidr_blocks = flatten([ + try([for ip in coalesce(data.enos_environment.localhost.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], []), + [data.aws_vpc.infra.ipv6_cidr_block], + [for ip in var.alb_sg_additional_ipv6_ips : cidrsubnet("${ip}/64", 0, 0)], + ]) + egress_cidr_blocks = [], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + vault_address = format("[%s]", var.vault_address) + }, + "dual" = { + ingress_cidr_blocks = flatten([ + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), + join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), + formatlist("%s/32", var.alb_sg_additional_ips), + ]) + ingress_ipv6_cidr_blocks = flatten([ + try([for ip in coalesce(data.enos_environment.localhost.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], []), + [data.aws_vpc.infra.ipv6_cidr_block], + [for ip in var.alb_sg_additional_ipv6_ips : cidrsubnet("${ip}/64", 0, 0)], + ]), + egress_cidr_blocks = ["0.0.0.0/0"], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + vault_address = try(format("[%s]", var.vault_address), "") + } + } } resource "random_string" "cluster_id" { diff --git a/enos/modules/aws_boundary/outputs.tf b/enos/modules/aws_boundary/outputs.tf index 1d56e0b36..688bafe4e 100644 --- a/enos/modules/aws_boundary/outputs.tf +++ b/enos/modules/aws_boundary/outputs.tf @@ -3,12 +3,12 @@ output "controller_ips" { description = "Public IPs of boundary controllers" - value = aws_instance.controller.*.public_ip + value = var.ip_version == "6" ? flatten(aws_instance.controller.*.ipv6_addresses) : aws_instance.controller.*.public_ip } output "worker_ips" { description = "Public IPs of boundary workers" - value = aws_instance.worker.*.public_ip + value = var.ip_version == "6" ? flatten(aws_instance.worker.*.ipv6_addresses) : aws_instance.worker.*.public_ip } output "alb_hostname" { @@ -208,7 +208,7 @@ output "cluster_tag" { } output "public_controller_addresses" { - value = aws_instance.controller[*].public_ip + value = var.ip_version == "4" ? aws_instance.controller[*].public_ip : aws_instance.controller[*].ipv6_addresses[0] } output "controller_aux_sg_id" { @@ -232,3 +232,13 @@ output "worker_tokens" { for token in enos_remote_exec.get_worker_token : trimspace(token.stdout) ], null) } + +output "worker_cidr" { + description = "List of ipv4 subnets of all workers" + value = formatlist("%s/32", aws_instance.worker.*.public_ip) +} + +output "worker_ipv6_cidr" { + description = "List of ipv6 subnets of all workers" + value = distinct([for ip in flatten(aws_instance.worker.*.ipv6_addresses) : cidrsubnet("${ip}/64", 0, 0)]) +} diff --git a/enos/modules/aws_boundary/rds.tf b/enos/modules/aws_boundary/rds.tf index 2010945c9..76f5335b9 100644 --- a/enos/modules/aws_boundary/rds.tf +++ b/enos/modules/aws_boundary/rds.tf @@ -17,9 +17,11 @@ resource "aws_db_instance" "boundary" { instance_class = var.db_class monitoring_interval = var.db_monitoring_interval monitoring_role_arn = var.db_monitoring_role_arn - publicly_accessible = true + publicly_accessible = false db_name = local.db_name + network_type = var.ip_version == "4" ? "IPV4" : "DUAL" + // username and password must not be provided when restoring from a snapshot username = local.is_restored_db ? null : var.db_user password = local.is_restored_db ? null : var.db_pass diff --git a/enos/modules/aws_boundary/security-groups.tf b/enos/modules/aws_boundary/security-groups.tf index 5e166d93e..7a321505b 100644 --- a/enos/modules/aws_boundary/security-groups.tf +++ b/enos/modules/aws_boundary/security-groups.tf @@ -22,24 +22,22 @@ resource "aws_security_group" "boundary_sg" { # SSH ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = flatten([formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses)]) + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } dynamic "ingress" { for_each = local.listener_ports content { - cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), - ]) + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks description = ingress.key from_port = ingress.value to_port = ingress.value - ipv6_cidr_blocks = [] prefix_list_ids = [] protocol = "tcp" self = null @@ -48,10 +46,11 @@ resource "aws_security_group" "boundary_sg" { } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = local.network_stack[var.ip_version].egress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].egress_ipv6_cidr_blocks } tags = merge( @@ -86,27 +85,29 @@ resource "aws_security_group" "boundary_alb_sg" { content { cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), try(format("%s/32", aws_instance.controller.0.public_ip), []), - formatlist("%s/32", var.alb_sg_additional_ips) - ]) - description = ingress.key - from_port = ingress.value - to_port = ingress.value - ipv6_cidr_blocks = [] - prefix_list_ids = [] - protocol = "tcp" - self = null - security_groups = [] + local.network_stack[var.ip_version].ingress_cidr_blocks] + ) + ipv6_cidr_blocks = flatten([ + try(cidrsubnet("${aws_instance.controller.0.ipv6_addresses[0]}/64", 0, 0), []), + local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks] + ) + description = ingress.key + from_port = ingress.value + to_port = ingress.value + prefix_list_ids = [] + protocol = "tcp" + self = null + security_groups = [] } } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = local.network_stack[var.ip_version].egress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].egress_ipv6_cidr_blocks } tags = merge( @@ -124,11 +125,11 @@ resource "aws_security_group" "boundary_db_sg" { vpc_id = var.vpc_id ingress { - cidr_blocks = flatten([formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block)]) + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks description = "database" from_port = 5432 to_port = 5432 - ipv6_cidr_blocks = [] prefix_list_ids = [] protocol = "tcp" self = null @@ -136,10 +137,11 @@ resource "aws_security_group" "boundary_db_sg" { } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = local.network_stack[var.ip_version].egress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].egress_ipv6_cidr_blocks } tags = merge(local.common_tags, diff --git a/enos/modules/aws_boundary/templates/controller.hcl b/enos/modules/aws_boundary/templates/controller.hcl index 03739b490..abe6eb9cc 100644 --- a/enos/modules/aws_boundary/templates/controller.hcl +++ b/enos/modules/aws_boundary/templates/controller.hcl @@ -18,7 +18,7 @@ controller { # API listener configuration block listener "tcp" { # Should be the address of the NIC that the controller server will be reached on - address = "${local_ipv4}:${api_port}" + address = "${listener_address}:${api_port}" # The purpose of this listener block purpose = "api" tls_disable = true @@ -31,7 +31,7 @@ listener "tcp" { # API listener configuration block listener "tcp" { - address = "${local_ipv4}:${ops_port}" + address = "${listener_address}:${ops_port}" purpose = "ops" tls_disable = true } @@ -39,7 +39,7 @@ listener "tcp" { # Data-plane listener configuration block (used for worker coordination) listener "tcp" { # Should be the IP of the NIC that the worker will connect on - address = "${local_ipv4}:${cluster_port}" + address = "${cluster_address}:${cluster_port}" # The purpose of this listener purpose = "cluster" } diff --git a/enos/modules/aws_boundary/templates/controller_bsr.hcl b/enos/modules/aws_boundary/templates/controller_bsr.hcl index d18c4eb6e..13407b4fb 100644 --- a/enos/modules/aws_boundary/templates/controller_bsr.hcl +++ b/enos/modules/aws_boundary/templates/controller_bsr.hcl @@ -18,7 +18,7 @@ controller { # API listener configuration block listener "tcp" { # Should be the address of the NIC that the controller server will be reached on - address = "${local_ipv4}:${api_port}" + address = "${listener_address}:${api_port}" # The purpose of this listener block purpose = "api" tls_disable = true @@ -31,7 +31,7 @@ listener "tcp" { # API listener configuration block listener "tcp" { - address = "${local_ipv4}:${ops_port}" + address = "${listener_address}:${ops_port}" purpose = "ops" tls_disable = true } @@ -39,7 +39,7 @@ listener "tcp" { # Data-plane listener configuration block (used for worker coordination) listener "tcp" { # Should be the IP of the NIC that the worker will connect on - address = "${local_ipv4}:${cluster_port}" + address = "${cluster_address}:${cluster_port}" # The purpose of this listener purpose = "cluster" } diff --git a/enos/modules/aws_boundary/templates/controller_bsr_vault_kms.hcl b/enos/modules/aws_boundary/templates/controller_bsr_vault_kms.hcl new file mode 100644 index 000000000..01f436cc0 --- /dev/null +++ b/enos/modules/aws_boundary/templates/controller_bsr_vault_kms.hcl @@ -0,0 +1,126 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +disable_mlock = true + +controller { + name = "boundary-controller-${id}" + description = "Enos Boundary controller ${id}" + + max_page_size = ${max_page_size} + + database { + url = "postgresql://${dbuser}:${dbpass}@${dbhost}:${dbport}/${dbname}" + max_open_connections = ${db_max_open_connections} + } +} + +# API listener configuration block +listener "tcp" { + # Should be the address of the NIC that the controller server will be reached on + address = "${listener_address}:${api_port}" + # The purpose of this listener block + purpose = "api" + tls_disable = true + + # Uncomment to enable CORS for the Admin UI. Be sure to set the allowed origin(s) + # to appropriate values. + #cors_enabled = true + #cors_allowed_origins = ["https://yourcorp.yourdomain.com", "serve://boundary"] +} + +# API listener configuration block +listener "tcp" { + address = "${listener_address}:${ops_port}" + purpose = "ops" + tls_disable = true +} + +# Data-plane listener configuration block (used for worker coordination) +listener "tcp" { + # Should be the IP of the NIC that the worker will connect on + address = "${cluster_address}:${cluster_port}" + # The purpose of this listener + purpose = "cluster" +} + +kms "transit" { + purpose = "worker-auth" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-worker-auth" + mount_path = "transit/" + tls_skip_verify = "true" +} + +kms "transit" { + purpose = "root" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-root" + mount_path = "transit/" + tls_skip_verify = "true" +} + +kms "transit" { + purpose = "recovery" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-recovery" + mount_path = "transit/" + tls_skip_verify = "true" +} + +kms "transit" { + purpose = "bsr" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-bsr" + mount_path = "transit/" + tls_skip_verify = "true" +} + +events { + audit_enabled = true + observations_enabled = true + sysevents_enabled = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + "\"/data/request_info/path\" contains \"/health\"", + ] + } + + sink { + name = "audit-sink" + description = "Audit sent to a file" + event_types = ["audit"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + ] + + file { + path = "${audit_log_dir}" + file_name = "audit.log" + } + + audit_config { + audit_filter_overrides { + secret = "encrypt" + sensitive = "hmac-sha256" + } + } + } +} diff --git a/enos/modules/aws_boundary/templates/controller_vault_kms.hcl b/enos/modules/aws_boundary/templates/controller_vault_kms.hcl new file mode 100644 index 000000000..8f429b3e9 --- /dev/null +++ b/enos/modules/aws_boundary/templates/controller_vault_kms.hcl @@ -0,0 +1,115 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +disable_mlock = true + +controller { + name = "boundary-controller-${id}" + description = "Enos Boundary controller ${id}" + max_page_size = ${max_page_size} + + database { + url = "postgresql://${dbuser}:${dbpass}@${dbhost}:${dbport}/${dbname}" + max_open_connections = ${db_max_open_connections} + } +} + +# API listener configuration block +listener "tcp" { + # Should be the address of the NIC that the controller server will be reached on + address = "${listener_address}:${api_port}" + # The purpose of this listener block + purpose = "api" + tls_disable = true + + # Uncomment to enable CORS for the Admin UI. Be sure to set the allowed origin(s) + # to appropriate values. + #cors_enabled = true + #cors_allowed_origins = ["https://yourcorp.yourdomain.com", "serve://boundary"] +} + +# API listener configuration block +listener "tcp" { + address = "${listener_address}:${ops_port}" + purpose = "ops" + tls_disable = true +} + +# Data-plane listener configuration block (used for worker coordination) +listener "tcp" { + # Should be the IP of the NIC that the worker will connect on + address = "${cluster_address}:${cluster_port}" + # The purpose of this listener + purpose = "cluster" +} + +kms "transit" { + purpose = "worker-auth" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-worker-auth" + mount_path = "transit/" + tls_skip_verify = "true" +} + +kms "transit" { + purpose = "root" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-root" + mount_path = "transit/" + tls_skip_verify = "true" +} + +kms "transit" { + purpose = "recovery" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-recovery" + mount_path = "transit/" + tls_skip_verify = "true" +} + +events { + audit_enabled = true + observations_enabled = true + sysevents_enabled = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + "\"/data/request_info/path\" contains \"/health\"", + ] + } + + sink { + name = "audit-sink" + description = "Audit sent to a file" + event_types = ["audit"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + ] + + file { + path = "${audit_log_dir}" + file_name = "audit.log" + } + + audit_config { + audit_filter_overrides { + secret = "encrypt" + sensitive = "hmac-sha256" + } + } + } +} diff --git a/enos/modules/aws_boundary/templates/worker.hcl b/enos/modules/aws_boundary/templates/worker.hcl index afcd95ce4..76cd55b17 100644 --- a/enos/modules/aws_boundary/templates/worker.hcl +++ b/enos/modules/aws_boundary/templates/worker.hcl @@ -2,9 +2,9 @@ # SPDX-License-Identifier: BUSL-1.1 listener "tcp" { - purpose = "proxy" + purpose = "proxy" tls_disable = true - address = "0.0.0.0" + address = "${listener_address}" } worker { @@ -15,7 +15,7 @@ worker { # Workers must be able to reach controllers on :9201 initial_upstreams = ${controller_ips} - public_addr = "${public_addr}" + public_addr = "${public_address}" tags { type = ${type} diff --git a/enos/modules/aws_boundary/templates/worker_bsr.hcl b/enos/modules/aws_boundary/templates/worker_bsr.hcl index 764735fef..75db102cf 100644 --- a/enos/modules/aws_boundary/templates/worker_bsr.hcl +++ b/enos/modules/aws_boundary/templates/worker_bsr.hcl @@ -2,9 +2,9 @@ # SPDX-License-Identifier: BUSL-1.1 listener "tcp" { - purpose = "proxy" + purpose = "proxy" tls_disable = true - address = "0.0.0.0" + address = "${listener_address}" } worker { @@ -15,7 +15,7 @@ worker { # Workers must be able to reach controllers on :9201 initial_upstreams = ${controller_ips} - public_addr = "${public_addr}" + public_addr = "${public_address}" tags { type = ${type} diff --git a/enos/modules/aws_boundary/templates/worker_bsr_vault_kms.hcl b/enos/modules/aws_boundary/templates/worker_bsr_vault_kms.hcl new file mode 100644 index 000000000..aa86f51d9 --- /dev/null +++ b/enos/modules/aws_boundary/templates/worker_bsr_vault_kms.hcl @@ -0,0 +1,78 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +listener "tcp" { + purpose = "proxy" + tls_disable = true + address = "${listener_address}:9202" +} + +worker { + # Name attr must be unique across workers + name = "demo-worker-${id}" + description = "Enos Boundary worker ${id}" + + # Workers must be able to reach controllers on :9201 + initial_upstreams = ${controller_ips} + + public_addr = "${public_address}" + + tags { + type = ${type} + region = ["${region}"] + } + + recording_storage_path = "${recording_storage_path}" +} + +# must be same key as used on controller config +kms "transit" { + purpose = "worker-auth" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-worker-auth" + mount_path = "transit/" + tls_skip_verify = "true" +} + +events { + audit_enabled = true + observations_enabled = true + sysevents_enabled = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + "\"/data/request_info/path\" contains \"/health\"", + ] + } + + sink { + name = "audit-sink" + description = "Audit sent to a file" + event_types = ["audit"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + ] + + file { + path = "${audit_log_dir}" + file_name = "audit.log" + } + + audit_config { + audit_filter_overrides { + secret = "encrypt" + sensitive = "hmac-sha256" + } + } + } +} diff --git a/enos/modules/aws_boundary/templates/worker_hcp_bsr.hcl b/enos/modules/aws_boundary/templates/worker_hcp_bsr.hcl index 11b858cb9..80f5d1728 100644 --- a/enos/modules/aws_boundary/templates/worker_hcp_bsr.hcl +++ b/enos/modules/aws_boundary/templates/worker_hcp_bsr.hcl @@ -2,15 +2,15 @@ # SPDX-License-Identifier: BUSL-1.1 listener "tcp" { - purpose = "proxy" + purpose = "proxy" tls_disable = true - address = "0.0.0.0" + address = "${listener_address}" } hcp_boundary_cluster_id = "${hcp_boundary_cluster_id}" worker { - public_addr = "${public_addr}" + public_addr = "${public_address}" tags { type = ${type} diff --git a/enos/modules/aws_boundary/templates/worker_vault_kms.hcl b/enos/modules/aws_boundary/templates/worker_vault_kms.hcl new file mode 100644 index 000000000..e304fe96c --- /dev/null +++ b/enos/modules/aws_boundary/templates/worker_vault_kms.hcl @@ -0,0 +1,76 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +listener "tcp" { + purpose = "proxy" + tls_disable = true + address = "${listener_address}" +} + +worker { + # Name attr must be unique across workers + name = "demo-worker-${id}" + description = "Enos Boundary worker ${id}" + + # Workers must be able to reach controllers on :9201 + initial_upstreams = ${controller_ips} + + public_addr = "${public_address}" + + tags { + type = ${type} + region = ["${region}"] + } +} + +# must be same key as used on controller config +kms "transit" { + purpose = "worker-auth" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-worker-auth" + mount_path = "transit/" + tls_skip_verify = "true" +} + +events { + audit_enabled = true + observations_enabled = true + sysevents_enabled = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + "\"/data/request_info/path\" contains \"/health\"", + ] + } + + sink { + name = "audit-sink" + description = "Audit sent to a file" + event_types = ["audit"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + ] + + file { + path = "${audit_log_dir}" + file_name = "audit.log" + } + + audit_config { + audit_filter_overrides { + secret = "encrypt" + sensitive = "hmac-sha256" + } + } + } +} diff --git a/enos/modules/aws_boundary/variables.tf b/enos/modules/aws_boundary/variables.tf index fd5b70bd9..038320205 100644 --- a/enos/modules/aws_boundary/variables.tf +++ b/enos/modules/aws_boundary/variables.tf @@ -334,6 +334,12 @@ variable "alb_sg_additional_ips" { default = [] } +variable "alb_sg_additional_ipv6_ips" { + description = "Additional ipv6 IPs to be allowed (ingress) on an ALB Security Group" + type = list(string) + default = [] +} + variable "boundary_license" { description = "Boundary license (not needed for OSS, required for enterprise)" type = string @@ -378,3 +384,26 @@ variable "hcp_boundary_cluster_id" { // If using HCP int, ensure that the cluster id starts with "int-" // Example: "int-19283a-123123-..." } + +variable "ip_version" { + description = "ip version used to setup boundary instance, should be 4, 6, or dual" + type = string + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "ip_version must be one of: [4, 6, dual]" + } +} + +variable "vault_address" { + description = "network address to a vault instance" + type = string + default = "localhost" +} + +variable "vault_transit_token" { + description = "vault token used for kms transit in the boundary config" + type = string + default = "" +} \ No newline at end of file diff --git a/enos/modules/aws_target/main.tf b/enos/modules/aws_target/main.tf index 70ddf4191..eaa6e85aa 100644 --- a/enos/modules/aws_target/main.tf +++ b/enos/modules/aws_target/main.tf @@ -26,36 +26,84 @@ variable "ingress_cidr" { default = ["10.0.0.0/8"] } +variable "ingress_ipv6_cidr" { + type = list(string) + default = [] +} + +variable "ip_version" { + description = "ip version used to setup boundary instance, should be 4, 6, or dual" + type = string + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "ip_version must be one of: [4, 6, dual]" + } +} + data "enos_environment" "current" {} data "aws_caller_identity" "current" {} +locals { + network_stack = { + "4" = { + ingress_cidr_blocks = flatten([ + formatlist("%s/32", data.enos_environment.current.public_ipv4_addresses), + var.ingress_cidr, + ]) + ingress_ipv6_cidr_blocks = [], + egress_cidr_blocks = ["0.0.0.0/0"], + egress_ipv6_cidr_blocks = [], + ipv6_address_count = 0, + }, + "6" = { + ingress_cidr_blocks = [], + ingress_ipv6_cidr_blocks = flatten([ + [for ip in coalesce(data.enos_environment.current.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], + var.ingress_ipv6_cidr, + ]) + egress_cidr_blocks = [], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + }, + "dual" = { + ingress_cidr_blocks = flatten([ + # allow ingress from ipv4 to allow for test setup from ci + formatlist("%s/32", data.enos_environment.current.public_ipv4_addresses), + var.ingress_cidr, + ]) + ingress_ipv6_cidr_blocks = flatten([ + [for ip in coalesce(data.enos_environment.current.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], + var.ingress_ipv6_cidr, + ]) + egress_cidr_blocks = [], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + } + } +} resource "aws_security_group" "boundary_target" { name_prefix = "boundary-target-sg" description = "SSH and boundary Traffic" vpc_id = var.vpc_id - # SSH ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - 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)]) + description = "SSH to the instance" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = local.network_stack[var.ip_version].egress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].egress_ipv6_cidr_blocks } tags = { @@ -71,6 +119,8 @@ resource "aws_instance" "target" { subnet_id = var.subnet_ids[count.index % length(var.subnet_ids)] key_name = var.aws_ssh_keypair_name + ipv6_address_count = local.network_stack[var.ip_version].ipv6_address_count + tags = merge(var.additional_tags, { "Name" : "boundary-target-${count.index}-${split(":", data.aws_caller_identity.current.user_id)[1]}", "Type" : "target", @@ -90,15 +140,15 @@ resource "enos_remote_exec" "wait" { transport = { ssh = { - host = aws_instance.target[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.target[each.key].ipv6_addresses[0] : aws_instance.target[each.key].public_ip } } } output "target_private_ips" { - value = aws_instance.target.*.private_ip + value = var.ip_version == "4" ? aws_instance.target.*.private_ip : formatlist("[%s]", flatten(aws_instance.target.*.ipv6_addresses)) } output "target_public_ips" { - value = aws_instance.target.*.public_ip + value = var.ip_version == "4" ? aws_instance.target.*.public_ip : formatlist("[%s]", flatten(aws_instance.target.*.ipv6_addresses)) } diff --git a/enos/modules/aws_vault/iam.tf b/enos/modules/aws_vault/iam.tf index 7eb568082..32dc26112 100644 --- a/enos/modules/aws_vault/iam.tf +++ b/enos/modules/aws_vault/iam.tf @@ -36,17 +36,20 @@ data "aws_iam_policy_document" "vault_profile" { } resource "aws_iam_role" "vault_instance_role" { + count = var.deploy ? 1 : 0 name = "vault_instance_role-${random_string.cluster_id.result}" assume_role_policy = data.aws_iam_policy_document.vault_instance_role.json } resource "aws_iam_instance_profile" "vault_profile" { - name = "vault_instance_profile-${random_string.cluster_id.result}" - role = aws_iam_role.vault_instance_role.name + count = var.deploy ? 1 : 0 + name = "vault_instance_profile-${random_string.cluster_id.result}" + role = aws_iam_role.vault_instance_role[0].name } resource "aws_iam_role_policy" "vault_policy" { + count = var.deploy ? 1 : 0 name = "vault_policy-${random_string.cluster_id.result}" - role = aws_iam_role.vault_instance_role.id + role = aws_iam_role.vault_instance_role[0].id policy = data.aws_iam_policy_document.vault_profile.json } diff --git a/enos/modules/aws_vault/locals.tf b/enos/modules/aws_vault/locals.tf index 81a94507f..695fa3ba7 100644 --- a/enos/modules/aws_vault/locals.tf +++ b/enos/modules/aws_vault/locals.tf @@ -11,9 +11,9 @@ locals { "awskms" = null "shamir" = 3 } - followers = toset(slice(local.instances, 1, length(local.instances))) - instances = [for idx in range(var.instance_count) : tostring(idx)] - leader = toset(slice(local.instances, 0, 1)) + followers = var.deploy ? toset(slice(local.instances, 1, length(local.instances))) : toset([]) + instances = var.deploy ? [for idx in range(var.instance_count) : tostring(idx)] : [] + leader = var.deploy ? toset(slice(local.instances, 0, 1)) : toset([]) name_suffix = "${var.project_name}-${var.environment}" recovery_shares = { "awskms" = 5 @@ -53,4 +53,41 @@ locals { audit_device_file_path = "/var/log/vault/vault_audit.log" vault_service_user = "vault" enable_audit_device = var.enable_file_audit_device && var.vault_init + network_stack = { + "4" = { + ingress_cidr_blocks = flatten([ + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), + join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), + formatlist("%s/32", var.sg_additional_ips), + ]) + ingress_ipv6_cidr_blocks = [], + egress_cidr_blocks = ["0.0.0.0/0"], + egress_ipv6_cidr_blocks = [], + ipv6_address_count = 0, + }, + "6" = { + ingress_cidr_blocks = [], + ingress_ipv6_cidr_blocks = flatten([ + [for ip in coalesce(data.enos_environment.localhost.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], + [data.aws_vpc.infra.ipv6_cidr_block], + [for ip in var.sg_additional_ipv6_ips : cidrsubnet("${ip}/64", 0, 0)], + ]) + egress_cidr_blocks = [], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + }, + "dual" = { + ingress_cidr_blocks = flatten([ + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), + ]) + ingress_ipv6_cidr_blocks = flatten([ + [for ip in coalesce(data.enos_environment.localhost.public_ipv6_addresses, []) : cidrsubnet("${ip}/64", 0, 0)], + [data.aws_vpc.infra.ipv6_cidr_block], + [for ip in var.sg_additional_ipv6_ips : cidrsubnet("${ip}/64", 0, 0)], + ]) + egress_cidr_blocks = [], + egress_ipv6_cidr_blocks = ["::/0"], + ipv6_address_count = 1, + } + } } diff --git a/enos/modules/aws_vault/outputs.tf b/enos/modules/aws_vault/outputs.tf index 48b1cc1bf..fb04f2241 100644 --- a/enos/modules/aws_vault/outputs.tf +++ b/enos/modules/aws_vault/outputs.tf @@ -8,7 +8,7 @@ output "instance_ids" { output "instance_public_ips" { description = "Public IPs of Vault instances" - value = [for instance in aws_instance.vault_instance : instance.public_ip] + value = var.ip_version == "4" ? [for instance in aws_instance.vault_instance : instance.public_ip] : flatten([for instance in aws_instance.vault_instance : instance.ipv6_addresses]) } output "instance_private_ips" { @@ -40,6 +40,10 @@ output "vault_root_token" { value = coalesce(var.vault_root_token, try(enos_vault_init.leader[0].root_token, null), "none") } +output "vault_transit_token" { + value = try([for token in enos_remote_exec.vault_kms_policy : trimspace(token.stdout)][0], "") +} + output "vault_unseal_keys_b64" { value = try(enos_vault_init.leader[0].unseal_keys_b64, []) } diff --git a/enos/modules/aws_vault/security-groups.tf b/enos/modules/aws_vault/security-groups.tf index b96d33106..2a887c03f 100644 --- a/enos/modules/aws_vault/security-groups.tf +++ b/enos/modules/aws_vault/security-groups.tf @@ -2,52 +2,44 @@ # SPDX-License-Identifier: BUSL-1.1 resource "aws_security_group" "enos_vault_sg" { + count = var.deploy ? 1 : 0 name = "vault-sg-${random_string.cluster_id.result}" description = "SSH and Vault Traffic" vpc_id = var.vpc_id # SSH ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), - ]) + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } # Vault traffic ingress { - from_port = 8200 - to_port = 8201 - protocol = "tcp" - cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), - formatlist("%s/32", var.sg_additional_ips), - ]) + from_port = 8200 + to_port = 8201 + protocol = "tcp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } # Consul Agent traffic ingress { - from_port = 8301 - to_port = 8301 - protocol = "tcp" - cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), - ]) + from_port = 8301 + to_port = 8301 + protocol = "tcp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } ingress { - from_port = 8301 - to_port = 8301 - protocol = "udp" - cidr_blocks = flatten([ - formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses), - join(",", data.aws_vpc.infra.cidr_block_associations.*.cidr_block), - ]) + from_port = 8301 + to_port = 8301 + protocol = "udp" + cidr_blocks = local.network_stack[var.ip_version].ingress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].ingress_ipv6_cidr_blocks } # Internal Traffic @@ -59,10 +51,11 @@ resource "aws_security_group" "enos_vault_sg" { } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = local.network_stack[var.ip_version].egress_cidr_blocks + ipv6_cidr_blocks = local.network_stack[var.ip_version].egress_ipv6_cidr_blocks } tags = merge( diff --git a/enos/modules/aws_vault/templates/create-kms-policy.sh b/enos/modules/aws_vault/templates/create-kms-policy.sh new file mode 100755 index 000000000..cae7db79a --- /dev/null +++ b/enos/modules/aws_vault/templates/create-kms-policy.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +function retry { + local retries=$1 + shift + local count=0 + + until "$@"; do + exit=$? + wait=$((2 ** count)) + count=$((count + 1)) + + if [ "$count" -lt "$retries" ]; then + sleep "$wait" + else + return "$exit" + fi + done + + return 0 +} + +export VAULT_ADDR=http://localhost:8200 +export VAULT_TOKEN='${vault_root_token}' + +cat > ./kms-transit.hcl << EOF +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +path "transit/encrypt/boundary-recovery" { + capabilities = ["create", "update"] +} + +path "transit/decrypt/boundary-recovery" { + capabilities = ["create", "update"] +} + +path "transit/encrypt/boundary-worker-auth" { + capabilities = ["create", "update"] +} + +path "transit/decrypt/boundary-worker-auth" { + capabilities = ["create", "update"] +} + +path "transit/encrypt/boundary-root" { + capabilities = ["create", "update"] +} + +path "transit/decrypt/boundary-root" { + capabilities = ["create", "update"] +} + +path "transit/encrypt/boundary-bsr" { + capabilities = ["create", "update"] +} + +path "transit/decrypt/boundary-bsr" { + capabilities = ["create", "update"] +} +EOF + +retry 5 ${vault_bin_path} secrets enable transit > /dev/null + +retry 5 ${vault_bin_path} policy write boundary-kms-transit-policy ./kms-transit.hcl > /dev/null + +rm ./kms-transit.hcl > /dev/null + +retry 5 ${vault_bin_path} token create -policy=boundary-kms-transit-policy -field=token diff --git a/enos/modules/aws_vault/variables.tf b/enos/modules/aws_vault/variables.tf index 2dd409803..5107cfdf0 100644 --- a/enos/modules/aws_vault/variables.tf +++ b/enos/modules/aws_vault/variables.tf @@ -71,6 +71,12 @@ variable "environment" { type = string } +variable "deploy" { + description = "Flag to toggle whether or not all the resources defined in this module should be deployed." + type = bool + default = false +} + variable "instance_count" { description = "Number of EC2 instances in each subnet" type = number @@ -101,7 +107,13 @@ variable "project_name" { } variable "sg_additional_ips" { - description = "A list of additional IPs to allow by Vault security groups (use with caution)" + description = "A list of additional IPv4 IPs to allow by Vault security groups (use with caution)" + type = list(string) + default = [] +} + +variable "sg_additional_ipv6_ips" { + description = "A list of additional IPv6 IPs to allow by Vault security groups (use with caution)" type = list(string) default = [] } @@ -253,3 +265,14 @@ variable "enable_file_audit_device" { type = bool default = true } + +variable "ip_version" { + type = string + description = "Optional variable that configures the vault instance to run on a ipv4, ipv6, or dualstack network" + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "Valid values for ip_version are (4, 6, dual)." + } +} diff --git a/enos/modules/aws_vault/vault-instances.tf b/enos/modules/aws_vault/vault-instances.tf index 3007d99a3..2fd1b092c 100644 --- a/enos/modules/aws_vault/vault-instances.tf +++ b/enos/modules/aws_vault/vault-instances.tf @@ -5,10 +5,11 @@ resource "aws_instance" "vault_instance" { for_each = local.vault_instances ami = var.ami_id instance_type = var.instance_type - vpc_security_group_ids = [aws_security_group.enos_vault_sg.id] + vpc_security_group_ids = [aws_security_group.enos_vault_sg[0].id] subnet_id = tolist(data.aws_subnets.infra.ids)[each.key % length(data.aws_subnets.infra.ids)] key_name = var.ssh_aws_keypair - iam_instance_profile = aws_iam_instance_profile.vault_profile.name + iam_instance_profile = aws_iam_instance_profile.vault_profile[0].name + ipv6_address_count = local.network_stack[var.ip_version].ipv6_address_count tags = merge( var.common_tags, { @@ -31,7 +32,7 @@ resource "enos_remote_exec" "install_dependencies" { transport = { ssh = { - host = aws_instance.vault_instance[each.value].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.value].ipv6_addresses[0] : aws_instance.vault_instance[each.value].public_ip } } } @@ -47,7 +48,7 @@ resource "enos_bundle_install" "consul" { transport = { ssh = { - host = each.value.public_ip + host = var.ip_version == "6" ? each.value.ipv6_addresses[0] : each.value.public_ip } } } @@ -62,7 +63,7 @@ resource "enos_bundle_install" "vault" { transport = { ssh = { - host = each.value.public_ip + host = var.ip_version == "6" ? each.value.ipv6_addresses[0] : each.value.public_ip } } } @@ -88,7 +89,7 @@ resource "enos_consul_start" "consul" { transport = { ssh = { - host = aws_instance.vault_instance[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } @@ -104,13 +105,13 @@ resource "enos_vault_start" "leader" { config_dir = var.vault_config_dir manage_service = var.manage_service config = { - api_addr = "http://${aws_instance.vault_instance[each.key].private_ip}:8200" - cluster_addr = "http://${aws_instance.vault_instance[each.key].private_ip}:8201" + api_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8200" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8200" + cluster_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8201" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8201" cluster_name = local.vault_cluster_tag listener = { type = "tcp" attributes = { - address = "0.0.0.0:8200" + address = var.ip_version == "4" ? "0.0.0.0:8200" : "[::]:8200" tls_disable = "true" } } @@ -130,7 +131,7 @@ resource "enos_vault_start" "leader" { transport = { ssh = { - host = aws_instance.vault_instance[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } @@ -145,14 +146,14 @@ resource "enos_vault_start" "followers" { config_dir = var.vault_config_dir manage_service = var.manage_service config = { - api_addr = "http://${aws_instance.vault_instance[each.key].private_ip}:8200" - cluster_addr = "http://${aws_instance.vault_instance[each.key].private_ip}:8201" + api_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8200" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8200" + cluster_addr = var.ip_version == "4" ? "http://${aws_instance.vault_instance[each.key].private_ip}:8201" : "http://[${aws_instance.vault_instance[each.key].ipv6_addresses[0]}]:8201" cluster_name = local.vault_cluster_tag log_level = var.vault_log_level listener = { type = "tcp" attributes = { - address = "0.0.0.0:8200" + address = var.ip_version == "4" ? "0.0.0.0:8200" : "[::]:8200" tls_disable = "true" } } @@ -171,7 +172,7 @@ resource "enos_vault_start" "followers" { transport = { ssh = { - host = aws_instance.vault_instance[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } @@ -194,7 +195,7 @@ resource "enos_vault_init" "leader" { transport = { ssh = { - host = aws_instance.vault_instance[0].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } @@ -212,7 +213,7 @@ resource "enos_vault_unseal" "leader" { transport = { ssh = { - host = aws_instance.vault_instance[0].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } @@ -237,7 +238,7 @@ resource "enos_remote_exec" "create_audit_log_dir" { transport = { ssh = { - host = aws_instance.vault_instance[each.value].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.value].ipv6_addresses[0] : aws_instance.vault_instance[each.value].public_ip } } } @@ -254,7 +255,7 @@ resource "enos_remote_exec" "init_audit_device" { environment = { VAULT_TOKEN = enos_vault_init.leader[each.key].root_token - VAULT_ADDR = "http://127.0.0.1:8200" + VAULT_ADDR = var.ip_version == "4" ? "http://127.0.0.1:8200" : "http://[::1]:8200" VAULT_BIN_PATH = local.vault_bin_path LOG_FILE_PATH = local.audit_device_file_path SERVICE_USER = local.vault_service_user @@ -266,7 +267,7 @@ resource "enos_remote_exec" "init_audit_device" { transport = { ssh = { - host = aws_instance.vault_instance[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } @@ -289,7 +290,7 @@ resource "enos_vault_unseal" "followers" { transport = { ssh = { - host = aws_instance.vault_instance[0].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } @@ -315,13 +316,13 @@ resource "enos_vault_unseal" "when_vault_unseal_when_no_init_is_set" { transport = { ssh = { - host = aws_instance.vault_instance[each.key].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[each.key].ipv6_addresses[0] : aws_instance.vault_instance[each.key].public_ip } } } resource "enos_remote_exec" "vault_write_license" { - count = var.vault_init ? 1 : 0 + count = var.deploy && var.vault_init ? 1 : 0 depends_on = [ enos_vault_unseal.leader, enos_vault_unseal.when_vault_unseal_when_no_init_is_set, @@ -335,7 +336,26 @@ resource "enos_remote_exec" "vault_write_license" { transport = { ssh = { - host = aws_instance.vault_instance[0].public_ip + host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip + } + } +} + +resource "enos_remote_exec" "vault_kms_policy" { + count = var.deploy && var.vault_init ? 1 : 0 + depends_on = [ + enos_remote_exec.vault_write_license, + ] + + content = templatefile("${path.module}/templates/create-kms-policy.sh", { + vault_bin_path = local.vault_bin_path, + vault_root_token = coalesce(var.vault_root_token, try(enos_vault_init.leader[0].root_token, null), "none") + policy_path = "${path.module}/policies/kms-transit.hcl" + }) + + transport = { + ssh = { + host = var.ip_version == "6" ? aws_instance.vault_instance[0].ipv6_addresses[0] : aws_instance.vault_instance[0].public_ip } } } diff --git a/enos/modules/aws_vpc/main.tf b/enos/modules/aws_vpc/main.tf index f3fa8ff9e..8cf54843c 100644 --- a/enos/modules/aws_vpc/main.tf +++ b/enos/modules/aws_vpc/main.tf @@ -210,7 +210,7 @@ output "vpc_id" { output "vpc_cidr" { description = "CIDR for whole VPC" - value = var.cidr + value = aws_vpc.vpc.cidr_block } output "vpc_subnets" { diff --git a/enos/modules/aws_vpc_ipv6/main.tf b/enos/modules/aws_vpc_ipv6/main.tf new file mode 100644 index 000000000..bf957435f --- /dev/null +++ b/enos/modules/aws_vpc_ipv6/main.tf @@ -0,0 +1,319 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +terraform { + required_version = ">= 1.1.2" + + required_providers { + enos = { + source = "registry.terraform.io/hashicorp-forge/enos" + version = ">= 0.3.25" + } + } +} + +variable "name" { + type = string + default = "enos-vpc" + description = "The name of the VPC" +} + +variable "availability_zones" { + description = "List of AWS availability zones to use (or * for all available)" + type = list(string) + default = ["*"] +} + +variable "cidr" { + type = string + default = "10.13.0.0/16" + description = "CIDR block for the VPC" +} + +variable "ipv6_cidr" { + type = string + default = "fd13:1:1::/48" + description = "IPv6 CIDR block for the VPC" +} + +variable "environment" { + description = "Name of the environment." + type = string + default = "enos-environment" +} + +variable "common_tags" { + description = "Tags to set for all resources" + type = map(string) + default = { "Project" : "enos" } +} + +variable "create_kms_key" { + description = "Whether or not to create an key management service key" + type = bool + default = true +} + +variable "ami_architectures" { + type = list(string) + description = "The AMI architectures to fetch AMI IDs for." + default = ["amd64", "arm64"] +} + +variable "ip_version" { + description = "ip version used to setup boundary instance, should be 4, 6, or dual" + type = string + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "ip_version must be one of: [4, 6, dual]" + } +} + +data "enos_environment" "localhost" {} + +check "ipv6_connection" { + assert { + condition = var.ip_version != "6" || data.enos_environment.localhost.public_ipv6_addresses != null + error_message = "no ipv6 connectivity detected, unable to set up ipv6-only tests" + } +} + +locals { + // AWS AMIs standardized on the x86_64 label for 64bit x86 architectures, therefore amd64 should be rather x86_64. + architecture_filters = [for arch in var.ami_architectures : (arch == "amd64" ? "x86_64" : arch)] + tag_module = "aws_vpc" + common_tags = merge( + var.common_tags, + { + "Module" = local.tag_module + }, + ) +} + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "zone-name" + values = var.availability_zones + } +} + +data "aws_ami" "ubuntu" { + most_recent = true + count = length(local.architecture_filters) + + # Currently latest LTS-1 + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-*-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + filter { + name = "architecture" + values = [local.architecture_filters[count.index]] + } + + owners = ["099720109477"] # Canonical +} + +data "aws_ami" "rhel" { + most_recent = true + count = length(local.architecture_filters) + + filter { + name = "name" + values = ["RHEL-8.8*HVM-20*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + filter { + name = "architecture" + values = [local.architecture_filters[count.index]] + } + + owners = ["309956199498"] # Redhat +} + +resource "random_string" "cluster_id" { + length = 8 + lower = true + upper = false + numeric = false + special = false +} + +resource "aws_kms_key" "key" { + count = var.create_kms_key ? 1 : 0 + description = "enos-kms-key" + deletion_window_in_days = 7 // 7 is the shortest allowed window +} + +resource "aws_kms_alias" "alias" { + count = var.create_kms_key ? 1 : 0 + name = "alias/enos_key-${random_string.cluster_id.result}" + target_key_id = aws_kms_key.key[0].key_id +} + +resource "aws_vpc" "vpc" { + cidr_block = var.cidr + enable_dns_hostnames = true + enable_dns_support = true + assign_generated_ipv6_cidr_block = true + tags = merge( + local.common_tags, + { + "Name" = var.name + }, + ) +} + +resource "aws_subnet" "subnet" { + count = length(data.aws_availability_zones.available.names) + vpc_id = aws_vpc.vpc.id + cidr_block = cidrsubnet(var.cidr, 8, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] + map_public_ip_on_launch = true + ipv6_cidr_block = cidrsubnet(aws_vpc.vpc.ipv6_cidr_block, 8, count.index + 16) # + 16 so we have some guaranteed unused subnets for other workers + assign_ipv6_address_on_creation = true + + tags = merge( + local.common_tags, + { + "Name" = "${var.name}-subnet-${data.aws_availability_zones.available.names[count.index]}" + }, + ) +} + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + + tags = merge( + local.common_tags, + { + "Name" = "${var.name}-igw" + }, + ) +} + +resource "aws_route" "igw_ipv6" { + route_table_id = aws_vpc.vpc.default_route_table_id + destination_ipv6_cidr_block = "::/0" + gateway_id = aws_internet_gateway.igw.id +} + +resource "aws_route" "igw_ipv4" { + count = var.ip_version == "6" ? 0 : 1 + route_table_id = aws_vpc.vpc.default_route_table_id + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id +} + +resource "aws_security_group" "default" { + vpc_id = aws_vpc.vpc.id + + ingress { + description = "allow_ingress_from_ipv6_only" + from_port = 0 + to_port = 0 + protocol = "tcp" + ipv6_cidr_blocks = ["::/0"] + } + + ingress { + description = "allow_ingress_only_from_ipv4_enos_host" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = flatten([ + # allow ingress from ipv4 to allow for test setup from ci + # TODO: remove this when github actions has ipv6 compatibility + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses) + ]) + } + + egress { + description = "allow_egress_only_to_ipv4_enos_host" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = flatten([ + # allow egress to ipv4 to allow for test setup from ci + # TODO: remove this when github actions has ipv6 compatibility + formatlist("%s/32", data.enos_environment.localhost.public_ipv4_addresses) + ]) + } + + egress { + description = "allow_egress_from_ipv6_only" + from_port = 0 + to_port = 0 + protocol = "-1" + ipv6_cidr_blocks = ["::/0"] + } + + tags = merge( + local.common_tags, + { + "Name" = "${var.name}-default" + }, + ) +} + +output "vpc_id" { + description = "Created VPC ID" + value = aws_vpc.vpc.id +} + +output "vpc_cidr" { + description = "CIDR for whole VPC" + value = aws_vpc.vpc.cidr_block +} + +output "vpc_cidr_ipv6" { + description = "ipv6 CIDR for whole VPC" + value = aws_vpc.vpc.ipv6_cidr_block +} + +output "vpc_subnets" { + description = "Generated subnet IDs and CIDRs" + value = { for s in aws_subnet.subnet : s.id => s.cidr_block } +} + +output "kms_key_arn" { + description = "ARN of the generated KMS key" + value = try(aws_kms_key.key[0].arn, null) +} + +output "kms_key_alias" { + description = "Alias of the generated KMS key" + value = try(aws_kms_alias.alias[0].name, null) +} + +output "availability_zone_names" { + description = "All availability zones with resources" + value = data.aws_availability_zones.available.names +} + +output "ami_ids" { + description = "The AWS AMI IDs for to use for ubuntu and rhel based instance for the amd64 and arm64 architectures." + value = { + ubuntu = { for idx, arch in var.ami_architectures : arch => data.aws_ami.ubuntu[idx].id } + rhel = { for idx, arch in var.ami_architectures : arch => data.aws_ami.rhel[idx].id } + } +} + +output "vpc_tag_module" { + value = local.tag_module +} diff --git a/enos/modules/aws_worker/main.tf b/enos/modules/aws_worker/main.tf index 1d1ce692d..0fd1c9e39 100644 --- a/enos/modules/aws_worker/main.tf +++ b/enos/modules/aws_worker/main.tf @@ -56,10 +56,13 @@ resource "random_integer" "az" { # Create a subnet so that the worker doesn't share one with a controller resource "aws_subnet" "default" { - vpc_id = var.vpc_id - cidr_block = "10.13.9.0/24" - map_public_ip_on_launch = true - availability_zone = local.selected_az + vpc_id = var.vpc_id + cidr_block = "10.13.9.0/24" + ipv6_cidr_block = var.ip_version != "4" ? cidrsubnet(var.vpc_cidr_ipv6, 8, 0) : null + + map_public_ip_on_launch = true + assign_ipv6_address_on_creation = var.ip_version != "4" + availability_zone = local.selected_az tags = merge( local.common_tags, { @@ -79,22 +82,28 @@ resource "aws_security_group" "default" { from_port = 22 to_port = 22 protocol = "tcp" - cidr_blocks = flatten([formatlist("%s/32", data.enos_environment.current.public_ipv4_addresses)]) + cidr_blocks = var.ip_version == "6" ? [] : flatten([ + # allow ingress from ipv4 to allow for test setup from ci + formatlist("%s/32", data.enos_environment.current.public_ipv4_addresses) + ]) + ipv6_cidr_blocks = var.ip_version == "4" ? [] : (data.enos_environment.current.public_ipv6_addresses != null ? [for ip in data.enos_environment.current.public_ipv6_addresses : cidrsubnet("${ip}/64", 0, 0)] : []) } ingress { - description = "Communication from Boundary controller to worker" - from_port = 9202 - to_port = 9202 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + description = "Communication from Boundary controller to worker" + from_port = 9202 + to_port = 9202 + protocol = "tcp" + cidr_blocks = var.ip_version == "6" ? [] : ["0.0.0.0/0"] + ipv6_cidr_blocks = var.ip_version == "4" ? [] : ["::/0"] } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = var.ip_version == "6" ? [] : ["0.0.0.0/0"] + ipv6_cidr_blocks = var.ip_version == "4" ? [] : ["::/0"] } tags = merge( @@ -110,7 +119,8 @@ resource "aws_security_group" "default" { resource "aws_vpc_security_group_ingress_rule" "worker_to_controller" { description = "This rule allows traffic from a worker to controllers over WAN" security_group_id = var.controller_sg_id - cidr_ipv4 = "${aws_instance.worker.public_ip}/32" + cidr_ipv4 = var.ip_version == "4" ? "${aws_instance.worker.public_ip}/32" : null + cidr_ipv6 = var.ip_version == "4" ? null : cidrsubnet("${aws_instance.worker.ipv6_addresses[0]}/64", 0, 0) from_port = 9201 to_port = 9201 ip_protocol = "tcp" @@ -134,6 +144,8 @@ resource "aws_instance" "worker" { iam_instance_profile = aws_iam_instance_profile.boundary_profile.name monitoring = var.worker_monitoring + ipv6_address_count = var.ip_version == "4" ? 0 : 1 + root_block_device { iops = var.ebs_iops volume_size = var.ebs_size @@ -160,7 +172,7 @@ resource "enos_bundle_install" "worker" { transport = { ssh = { - host = aws_instance.worker.public_ip + host = var.ip_version == "6" ? aws_instance.worker.ipv6_addresses[0] : aws_instance.worker.public_ip } } } @@ -176,7 +188,7 @@ resource "enos_remote_exec" "update_path_worker" { transport = { ssh = { - host = aws_instance.worker.public_ip + host = var.ip_version == "6" ? aws_instance.worker.ipv6_addresses[0] : aws_instance.worker.public_ip } } } @@ -193,17 +205,20 @@ resource "enos_file" "worker_config" { content = templatefile("${path.module}/${var.config_file_path}", { id = random_pet.worker.id kms_key_id = data.aws_kms_key.kms_key.id - public_addr = aws_instance.worker.public_ip + controller_ips = var.ip_version == "4" ? jsonencode(var.controller_addresses) : jsonencode(formatlist("[%s]:9201", var.controller_addresses)) + listener_address = var.ip_version == "4" ? "0.0.0.0" : "[::]" + public_address = var.ip_version == "6" ? format("[%s]", aws_instance.worker.ipv6_addresses[0]) : aws_instance.worker.public_ip type = jsonencode(var.worker_type_tags) region = data.aws_availability_zone.worker_az.region - controller_addresses = jsonencode(var.controller_addresses) recording_storage_path = var.recording_storage_path audit_log_dir = local.audit_log_directory + vault_address = var.ip_version == "4" ? var.vault_address : try(format("[%s]", var.vault_address), "") + vault_transit_token = var.vault_transit_token }) transport = { ssh = { - host = aws_instance.worker.public_ip + host = var.ip_version == "6" ? aws_instance.worker.ipv6_addresses[0] : aws_instance.worker.public_ip } } } @@ -219,7 +234,7 @@ resource "enos_boundary_start" "worker_start" { recording_storage_path = var.recording_storage_path != "" ? var.recording_storage_path : null transport = { ssh = { - host = aws_instance.worker.public_ip + host = var.ip_version == "6" ? aws_instance.worker.ipv6_addresses[0] : aws_instance.worker.public_ip } } } @@ -238,7 +253,7 @@ resource "enos_remote_exec" "create_worker_audit_log_dir" { transport = { ssh = { - host = aws_instance.worker.public_ip + host = var.ip_version == "6" ? aws_instance.worker.ipv6_addresses[0] : aws_instance.worker.public_ip } } } diff --git a/enos/modules/aws_worker/outputs.tf b/enos/modules/aws_worker/outputs.tf index 51e9cdb04..413468e7e 100644 --- a/enos/modules/aws_worker/outputs.tf +++ b/enos/modules/aws_worker/outputs.tf @@ -3,7 +3,7 @@ output "worker_ip" { description = "The public IP of the Boundary worker" - value = aws_instance.worker.public_ip + value = var.ip_version == "6" ? format("[%s]", aws_instance.worker.ipv6_addresses[0]) : aws_instance.worker.public_ip } output "worker_tags" { @@ -25,3 +25,13 @@ output "role_arn" { description = "The ARN of the IAM role used in this module" value = aws_iam_role.boundary_instance_role.arn } + +output "worker_cidr" { + description = "The subnet of the isolated worker" + value = var.ip_version == "6" ? [] : [aws_subnet.default.cidr_block] +} + +output "worker_ipv6_cidr" { + description = "The ipv6 subnet of the isolated worker" + value = var.ip_version == "4" ? [] : [aws_subnet.default.ipv6_cidr_block] +} diff --git a/enos/modules/aws_worker/templates/worker.hcl b/enos/modules/aws_worker/templates/worker.hcl index 604451966..8d42bab9f 100644 --- a/enos/modules/aws_worker/templates/worker.hcl +++ b/enos/modules/aws_worker/templates/worker.hcl @@ -2,9 +2,9 @@ # SPDX-License-Identifier: BUSL-1.1 listener "tcp" { - purpose = "proxy" + purpose = "proxy" tls_disable = true - address = "0.0.0.0" + address = "${listener_address}:9202" } worker { @@ -13,9 +13,9 @@ worker { description = "Enos Boundary worker ${id}" # Workers must be able to reach controllers on :9201 - initial_upstreams = ${controller_addresses} + initial_upstreams = ${controller_ips} - public_addr = "${public_addr}" + public_addr = "${public_address}" tags { region = ["${region}"] diff --git a/enos/modules/aws_worker/templates/worker_bsr.hcl b/enos/modules/aws_worker/templates/worker_bsr.hcl index 6cd749f5d..268725fe1 100644 --- a/enos/modules/aws_worker/templates/worker_bsr.hcl +++ b/enos/modules/aws_worker/templates/worker_bsr.hcl @@ -4,7 +4,7 @@ listener "tcp" { purpose = "proxy" tls_disable = true - address = "0.0.0.0" + address = "${listener_address}:9202" } worker { @@ -13,9 +13,9 @@ worker { description = "Enos Boundary worker ${id}" # Workers must be able to reach controllers on :9201 - initial_upstreams = ${controller_addresses} + initial_upstreams = ${controller_ips} - public_addr = "${public_addr}" + public_addr = "${public_address}" tags { region = ["${region}"] diff --git a/enos/modules/aws_worker/templates/worker_bsr_vault_kms.hcl b/enos/modules/aws_worker/templates/worker_bsr_vault_kms.hcl new file mode 100644 index 000000000..aa86f51d9 --- /dev/null +++ b/enos/modules/aws_worker/templates/worker_bsr_vault_kms.hcl @@ -0,0 +1,78 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +listener "tcp" { + purpose = "proxy" + tls_disable = true + address = "${listener_address}:9202" +} + +worker { + # Name attr must be unique across workers + name = "demo-worker-${id}" + description = "Enos Boundary worker ${id}" + + # Workers must be able to reach controllers on :9201 + initial_upstreams = ${controller_ips} + + public_addr = "${public_address}" + + tags { + type = ${type} + region = ["${region}"] + } + + recording_storage_path = "${recording_storage_path}" +} + +# must be same key as used on controller config +kms "transit" { + purpose = "worker-auth" + address = "http://${vault_address}:8200" + token = "${vault_transit_token}" + disable_renewal = "false" + key_name = "boundary-worker-auth" + mount_path = "transit/" + tls_skip_verify = "true" +} + +events { + audit_enabled = true + observations_enabled = true + sysevents_enabled = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + "\"/data/request_info/path\" contains \"/health\"", + ] + } + + sink { + name = "audit-sink" + description = "Audit sent to a file" + event_types = ["audit"] + format = "cloudevents-json" + + deny_filters = [ + "\"/data/request_info/method\" contains \"Status\"", + ] + + file { + path = "${audit_log_dir}" + file_name = "audit.log" + } + + audit_config { + audit_filter_overrides { + secret = "encrypt" + sensitive = "hmac-sha256" + } + } + } +} diff --git a/enos/modules/aws_worker/variables.tf b/enos/modules/aws_worker/variables.tf index 2b3dfbe42..6b1d51033 100644 --- a/enos/modules/aws_worker/variables.tf +++ b/enos/modules/aws_worker/variables.tf @@ -142,3 +142,40 @@ variable "bucket_arn" { type = string default = "" } + +variable "vpc_cidr" { + description = "cidr subnet of the vpc created for the enos scenario" + type = string + nullable = true + default = null +} + +variable "vpc_cidr_ipv6" { + description = "ipv6 cidr subnet of the vpc created for the enos scenario" + type = string + nullable = true + default = null +} + +variable "ip_version" { + description = "ip version used to setup boundary instance, should be 4, 6, or dual" + type = string + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "ip_version must be one of: [4, 6, dual]" + } +} + +variable "vault_address" { + description = "network address to a vault instance" + type = string + default = "localhost" +} + +variable "vault_transit_token" { + description = "vault token used for kms transit in the boundary config" + type = string + default = "" +} diff --git a/enos/modules/read_license/main.tf b/enos/modules/read_license/main.tf index 823714f5d..9ae9e0743 100644 --- a/enos/modules/read_license/main.tf +++ b/enos/modules/read_license/main.tf @@ -1,8 +1,8 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 -variable "file_name" {} +variable "license_path" {} output "license" { - value = file(var.file_name) + value = file(var.license_path) } diff --git a/enos/modules/test_e2e/main.tf b/enos/modules/test_e2e/main.tf index 237a2059c..ff964386a 100644 --- a/enos/modules/test_e2e/main.tf +++ b/enos/modules/test_e2e/main.tf @@ -140,7 +140,11 @@ variable "worker_tag_ingress" { type = string default = "" } -variable "worker_tag_egress" { +variable "worker_tag_isolated" { + type = string + default = "" +} +variable "worker_tag_collocated" { type = string default = "" } @@ -157,6 +161,17 @@ variable "boundary_license" { default = "" } +variable "ip_version" { + description = "ip version used to setup boundary instance, should be 4, 6, or dual" + type = string + default = "4" + + validation { + condition = contains(["4", "6", "dual"], var.ip_version) + error_message = "ip_version must be one of: [4, 6, dual]" + } +} + locals { aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) vault_addr = var.vault_addr != "" ? "http://${var.vault_addr}:${var.vault_port}" : "" @@ -192,9 +207,11 @@ resource "enos_local_exec" "run_e2e_test" { E2E_AWS_BUCKET_NAME = var.aws_bucket_name E2E_AWS_ROLE_ARN = var.aws_role_arn E2E_WORKER_TAG_INGRESS = var.worker_tag_ingress - E2E_WORKER_TAG_EGRESS = var.worker_tag_egress + E2E_WORKER_TAG_ISOLATED = var.worker_tag_isolated + E2E_WORKER_TAG_COLLOCATED = var.worker_tag_collocated E2E_WORKER_ADDRESS = var.worker_address E2E_MAX_PAGE_SIZE = var.max_page_size + E2E_IP_VERSION = var.ip_version } inline = var.debug_no_run ? [""] : [ diff --git a/testing/internal/e2e/boundary/host.go b/testing/internal/e2e/boundary/host.go index f85df4eea..96a7cadc8 100644 --- a/testing/internal/e2e/boundary/host.go +++ b/testing/internal/e2e/boundary/host.go @@ -203,19 +203,30 @@ func AddHostToHostSetCli(t testing.TB, ctx context.Context, hostSetId string, ho // CreateAwsHostCatalogCli uses the cli to create a new AWS dynamic host catalog. // Returns the id of the new host catalog. -func CreateAwsHostCatalogCli(t testing.TB, ctx context.Context, projectId, accessKeyId, secretAccessKey, region string) (string, error) { +func CreateAwsHostCatalogCli(t testing.TB, ctx context.Context, projectId, accessKeyId, secretAccessKey, region string, dualStack bool) (string, error) { name, err := base62.Random(16) if err != nil { return "", err } - output := e2e.RunCommand(ctx, "boundary", + opts := []e2e.Option{ e2e.WithArgs( "host-catalogs", "create", "plugin", "-scope-id", projectId, "-plugin-name", "aws", "-attr", "disable_credential_rotation=true", "-attr", fmt.Sprintf("region=%s", region), + ), + } + if dualStack { + opts = append(opts, + e2e.WithArgs( + "-attr", fmt.Sprintf("dual_stack=%t", dualStack), + ), + ) + } + opts = append(opts, + e2e.WithArgs( "-secret", "access_key_id=env://E2E_AWS_ACCESS_KEY_ID", "-secret", "secret_access_key=env://E2E_AWS_SECRET_ACCESS_KEY", "-name", fmt.Sprintf("e2e Host Catalog %s", name), @@ -225,6 +236,7 @@ func CreateAwsHostCatalogCli(t testing.TB, ctx context.Context, projectId, acces e2e.WithEnv("E2E_AWS_ACCESS_KEY_ID", accessKeyId), e2e.WithEnv("E2E_AWS_SECRET_ACCESS_KEY", secretAccessKey), ) + output := e2e.RunCommand(ctx, "boundary", opts...) if output.Err != nil { return "", fmt.Errorf("%w: %s", output.Err, string(output.Stderr)) } @@ -242,22 +254,41 @@ func CreateAwsHostCatalogCli(t testing.TB, ctx context.Context, projectId, acces // CreatePluginHostSetCli uses the cli to create a new host set from a dynamic host catalog. // Returns the id of the new host set. -func CreatePluginHostSetCli(t testing.TB, ctx context.Context, hostCatalogId string, filter string) (string, error) { +func CreatePluginHostSetCli(t testing.TB, ctx context.Context, hostCatalogId string, filter string, ipVersion string) (string, error) { name, err := base62.Random(16) if err != nil { return "", err } - output := e2e.RunCommand(ctx, "boundary", - e2e.WithArgs( - "host-sets", "create", "plugin", - "-host-catalog-id", hostCatalogId, - "-attr", "filters="+filter, - "-name", fmt.Sprintf("e2e Host Set %s", name), - "-description", "e2e", - "-format", "json", - ), - ) + var output *e2e.CommandResult + switch ipVersion { + case "4": + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "host-sets", "create", "plugin", + "-host-catalog-id", hostCatalogId, + "-attr", "filters="+filter, + "-name", fmt.Sprintf("e2e Host Set %s", name), + "-description", "e2e", + "-format", "json", + ), + ) + case "6", "dual": + output = e2e.RunCommand(ctx, "boundary", + e2e.WithArgs( + "host-sets", "create", "plugin", + "-host-catalog-id", hostCatalogId, + "-preferred-endpoint", "cidr:::/0", + "-attr", "filters="+filter, + "-name", fmt.Sprintf("e2e Host Set %s", name), + "-description", "e2e", + "-format", "json", + ), + ) + default: + return "", fmt.Errorf("unknown ip version: %q", ipVersion) + } + if output.Err != nil { return "", fmt.Errorf("%w: %s", output.Err, string(output.Stderr)) } diff --git a/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_empty_test.go b/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_empty_test.go index a37f517d4..0b8ab051c 100644 --- a/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_empty_test.go +++ b/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_empty_test.go @@ -38,11 +38,11 @@ func TestCliCreateAwsDynamicHostCatalogWithEmptyHostSet(t *testing.T) { }) projectId, err := boundary.CreateProjectCli(t, ctx, orgId) require.NoError(t, err) - hostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion) + hostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion, c.IpVersion != "4") require.NoError(t, err) // Set up a host set - hostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, "tag:empty_test=true") + hostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, "tag:empty_test=true", c.IpVersion) require.NoError(t, err) // Check that there are no hosts in the host set diff --git a/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_test.go b/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_test.go index 517fec504..7276a3890 100644 --- a/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_test.go +++ b/testing/internal/e2e/tests/aws/dynamichostcatalog_host_set_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "errors" "fmt" - "strings" + "regexp" "testing" "time" @@ -17,12 +17,17 @@ import ( "github.com/hashicorp/boundary/api/hosts" "github.com/hashicorp/boundary/api/hostsets" "github.com/hashicorp/boundary/api/scopes" + "github.com/hashicorp/boundary/api/sessions" + "github.com/hashicorp/boundary/internal/session" + "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/testing/internal/e2e" "github.com/hashicorp/boundary/testing/internal/e2e/boundary" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +var sessionRegex = regexp.MustCompile("Session ID:\\s+(.+)(?:$|\n)") + // TestCliCreateAwsDynamicHostCatalogWithHostSet 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. @@ -43,11 +48,11 @@ func TestCliCreateAwsDynamicHostCatalogWithHostSet(t *testing.T) { }) projectId, err := boundary.CreateProjectCli(t, ctx, orgId) require.NoError(t, err) - hostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion) + hostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion, c.IpVersion != "4") require.NoError(t, err) // Set up a host set - hostSetId1, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.AwsHostSetFilter1) + hostSetId1, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.AwsHostSetFilter1, c.IpVersion) require.NoError(t, err) var targetIps1 []string err = json.Unmarshal([]byte(c.AwsHostSetIps1), &targetIps1) @@ -56,7 +61,7 @@ func TestCliCreateAwsDynamicHostCatalogWithHostSet(t *testing.T) { boundary.WaitForNumberOfHostsInHostSetCli(t, ctx, hostSetId1, expectedHostSetCount1) // Set up another host set - hostSetId2, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.AwsHostSetFilter2) + hostSetId2, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.AwsHostSetFilter2, c.IpVersion) require.NoError(t, err) var targetIps2 []string err = json.Unmarshal([]byte(c.AwsHostSetIps2), &targetIps2) @@ -112,41 +117,63 @@ func TestCliCreateAwsDynamicHostCatalogWithHostSet(t *testing.T) { assert.Equal(t, expectedHostCatalogCount, actualHostCatalogCount, "Numbers of hosts in host catalog did not match expected amount") // Create target - targetId, err := boundary.CreateTargetCli(t, ctx, projectId, c.TargetPort) + var targetId string + if c.IpVersion == "4" { + targetId, err = boundary.CreateTargetCli(t, ctx, projectId, c.TargetPort) + } else { + targetId, err = boundary.CreateTargetCli(t, ctx, projectId, c.TargetPort, + target.WithIngressWorkerFilter(fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagCollocated)), + ) + } + require.NoError(t, err) err = boundary.AddHostSourceToTargetCli(t, ctx, targetId, hostSetId1) require.NoError(t, err) // Connect to target + ctxCancel, cancel := context.WithCancel(context.Background()) + go func() { + e2e.RunCommand(ctxCancel, "boundary", + e2e.WithArgs( + "connect", + "-target-id", targetId, + "-exec", "/usr/bin/ssh", "--", + "-l", c.TargetSshUser, + "-i", c.TargetSshKeyPath, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + "-p", "{{boundary.port}}", // this is provided by boundary + "{{boundary.ip}}", + "hostname -i; sleep 60", + ), + ) + }() + t.Cleanup(cancel) + + s := boundary.WaitForSessionCli(t, ctx, projectId) + boundary.WaitForSessionStatusCli(t, ctx, s.Id, session.StatusActive.String()) + output = e2e.RunCommand(ctx, "boundary", - e2e.WithArgs( - "connect", - "-target-id", targetId, - "-exec", "/usr/bin/ssh", "--", - "-l", c.TargetSshUser, - "-i", c.TargetSshKeyPath, - "-o", "UserKnownHostsFile=/dev/null", - "-o", "StrictHostKeyChecking=no", - "-o", "IdentitiesOnly=yes", // forces the use of the provided key - "-p", "{{boundary.port}}", // this is provided by boundary - "{{boundary.ip}}", - "hostname", "-i", - ), + e2e.WithArgs("sessions", "read", "-id", s.Id, "-format", "json"), ) require.NoError(t, output.Err, string(output.Stderr)) + var sessionReadResult sessions.SessionReadResult + err = json.Unmarshal(output.Stdout, &sessionReadResult) + require.NoError(t, err) + s = sessionReadResult.Item - parts := strings.Fields(string(output.Stdout)) - hostIp := parts[len(parts)-1] - t.Log("Successfully connected to the target") - - // Check if connected host exists in the host set - hostIpInList := false - for _, v := range targetIps1 { - if v == hostIp { - hostIpInList = true - } + require.Greater(t, len(s.Connections), 0) + var hostIp string + switch c.IpVersion { + case "4": + hostIp = s.Connections[0].EndpointTcpAddress + case "6", "dual": + hostIp = fmt.Sprintf("[%s]", s.Connections[0].EndpointTcpAddress) + default: + require.Fail(t, "unknown ip version", c.IpVersion) } - require.True(t, hostIpInList, fmt.Sprintf("Connected host (%s) is not in expected list (%s)", hostIp, targetIps1)) + require.Contains(t, targetIps1, hostIp, fmt.Sprintf("Connected host (%s) is not in expected list (%s)", hostIp, targetIps1)) } // TestApiCreateAwsDynamicHostCatalog uses the Go api to create a host catalog with the AWS plugin. diff --git a/testing/internal/e2e/tests/aws/env_test.go b/testing/internal/e2e/tests/aws/env_test.go index 5e6cbe642..16a7ff10e 100644 --- a/testing/internal/e2e/tests/aws/env_test.go +++ b/testing/internal/e2e/tests/aws/env_test.go @@ -6,21 +6,23 @@ package aws_test import "github.com/kelseyhightower/envconfig" type config struct { - AwsAccessKeyId string `envconfig:"E2E_AWS_ACCESS_KEY_ID" required:"true"` - AwsBucketName string `envconfig:"E2E_AWS_BUCKET_NAME" required:"true"` - AwsSecretAccessKey string `envconfig:"E2E_AWS_SECRET_ACCESS_KEY" required:"true"` - AwsHostSetFilter1 string `envconfig:"E2E_AWS_HOST_SET_FILTER" required:"true"` // e.g. "tag:testtag=true" - AwsHostSetIps1 string `envconfig:"E2E_AWS_HOST_SET_IPS" required:"true"` // e.g. "[\"1.2.3.4\", \"2.3.4.5\"]" - AwsHostSetFilter2 string `envconfig:"E2E_AWS_HOST_SET_FILTER2" required:"true"` // e.g. "tag:testtagtwo=test" - AwsHostSetIps2 string `envconfig:"E2E_AWS_HOST_SET_IPS2" required:"true"` // e.g. "[\"1.2.3.4\"]" - AwsRegion string `envconfig:"E2E_AWS_REGION" required:"true"` // e.g. "us-east-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" - TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. "192.168.0.1" - WorkerTagEgress string `envconfig:"E2E_WORKER_TAG_EGRESS" required:"true"` // e.g. "egress" - WorkerAddress string `envconfig:"E2E_WORKER_ADDRESS" required:"true"` // e.g. ""192.168.0.2" - MaxPageSize int `envconfig:"E2E_MAX_PAGE_SIZE" default:"1000"` + AwsAccessKeyId string `envconfig:"E2E_AWS_ACCESS_KEY_ID" required:"true"` + AwsBucketName string `envconfig:"E2E_AWS_BUCKET_NAME" required:"true"` + AwsSecretAccessKey string `envconfig:"E2E_AWS_SECRET_ACCESS_KEY" required:"true"` + AwsHostSetFilter1 string `envconfig:"E2E_AWS_HOST_SET_FILTER" required:"true"` // e.g. "tag:testtag=true" + AwsHostSetIps1 string `envconfig:"E2E_AWS_HOST_SET_IPS" required:"true"` // e.g. "[\"1.2.3.4\", \"2.3.4.5\"]" + AwsHostSetFilter2 string `envconfig:"E2E_AWS_HOST_SET_FILTER2" required:"true"` // e.g. "tag:testtagtwo=test" + AwsHostSetIps2 string `envconfig:"E2E_AWS_HOST_SET_IPS2" required:"true"` // e.g. "[\"1.2.3.4\"]" + AwsRegion string `envconfig:"E2E_AWS_REGION" required:"true"` // e.g. "us-east-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" + TargetAddress string `envconfig:"E2E_TARGET_ADDRESS" required:"true"` // e.g. "192.168.0.1" + WorkerTagIsolated string `envconfig:"E2E_WORKER_TAG_ISOLATED" required:"true"` // e.g. "isolated" + WorkerTagCollocated string `envconfig:"E2E_WORKER_TAG_COLLOCATED" required:"true"` // e.g. "collocated" + WorkerAddress string `envconfig:"E2E_WORKER_ADDRESS" required:"true"` // e.g. "192.168.0.2" + MaxPageSize int `envconfig:"E2E_MAX_PAGE_SIZE" default:"1000"` + IpVersion string `envconfig:"E2E_IP_VERSION" default:"4"` // should always be 4, 6, or dual } 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 60bf596c5..564c7cee1 100644 --- a/testing/internal/e2e/tests/aws/worker_test.go +++ b/testing/internal/e2e/tests/aws/worker_test.go @@ -74,7 +74,7 @@ func TestCliWorker(t *testing.T) { e2e.WithArgs( "targets", "update", "tcp", "-id", targetId, - "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagEgress), + "-egress-worker-filter", fmt.Sprintf(`"%s" in "/tags/type"`, c.WorkerTagIsolated), "-format", "json", ), ) diff --git a/testing/internal/e2e/tests/database/migration_test.go b/testing/internal/e2e/tests/database/migration_test.go index 7e6c89558..447b34a20 100644 --- a/testing/internal/e2e/tests/database/migration_test.go +++ b/testing/internal/e2e/tests/database/migration_test.go @@ -241,9 +241,9 @@ func populateBoundaryDatabase(t testing.TB, ctx context.Context, c *config, te T require.NoError(t, err) // Create AWS dynamic host catalog - awsHostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion) + awsHostCatalogId, err := boundary.CreateAwsHostCatalogCli(t, ctx, projectId, c.AwsAccessKeyId, c.AwsSecretAccessKey, c.AwsRegion, false) require.NoError(t, err) - awsHostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, awsHostCatalogId, c.AwsHostSetFilter) + awsHostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, awsHostCatalogId, c.AwsHostSetFilter, "4") require.NoError(t, err) boundary.WaitForHostsInHostSetCli(t, ctx, awsHostSetId) diff --git a/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_empty_test.go b/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_empty_test.go index 6b96a1c14..f96574c1a 100644 --- a/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_empty_test.go +++ b/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_empty_test.go @@ -43,7 +43,7 @@ func TestCliCreateGcpDynamicHostCatalogWithEmptyHostSet(t *testing.T) { require.NoError(t, err) // Set up a host set - hostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, "labels.empty_test=true") + hostSetId, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, "labels.empty_test=true", "4") require.NoError(t, err) // Check that there are no hosts in the host set diff --git a/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_test.go b/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_test.go index 0ac727405..375e16a48 100644 --- a/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_test.go +++ b/testing/internal/e2e/tests/gcp/dynamichostcatalog_host_set_test.go @@ -48,12 +48,12 @@ func TestCliCreateGcpDynamicHostCatalogWithHostSet(t *testing.T) { require.NoError(t, err) // Set up a host set - hostSetId1, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.GcpHostSetFilter1) + hostSetId1, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.GcpHostSetFilter1, "4") require.NoError(t, err) boundary.WaitForNumberOfHostsInHostSetCli(t, ctx, hostSetId1, 1) // Set up another host set - hostSetId2, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.GcpHostSetFilter2) + hostSetId2, err := boundary.CreatePluginHostSetCli(t, ctx, hostCatalogId, c.GcpHostSetFilter2, "4") require.NoError(t, err) boundary.WaitForNumberOfHostsInHostSetCli(t, ctx, hostSetId2, 1)