Create IPv6 and dual stack enos tests (#5212)

Co-authored-by: Danielle Miu <29378233+DanielleMiu@users.noreply.github.com>
Co-authored-by: Damian Debkowski <damian.debkowski@hashicorp.com>
This commit is contained in:
authored and GitHub committed 2025-01-21 07:37:04 -08:00
1 parent 34d13a0aae
commit dd9c7b9d60
59 files changed
+1773 -301

No files matched your search

+2 -2
View File
@@ -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
+12
View File
@@ -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"
}
@@ -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
+1 -1
View File
@@ -45,7 +45,7 @@ scenario "e2e_aws_base" {
module = module.read_license
variables {
file_name = local.license_path
license_path = local.license_path
}
}
+68 -25
View File
@@ -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
}
}
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -68,7 +68,7 @@ scenario "e2e_docker_base_plus" {
module = module.read_license
variables {
file_name = local.license_path
license_path = local.license_path
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
+1 -1
View File
@@ -68,7 +68,7 @@ scenario "e2e_docker_base" {
module = module.read_license
variables {
file_name = local.license_path
license_path = local.license_path
}
}
@@ -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
}
}
@@ -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
}
}
+2 -1
View File
@@ -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
+1 -1
View File
@@ -68,7 +68,7 @@ scenario "e2e_ui_docker" {
module = module.read_license
variables {
file_name = local.license_path
license_path = local.license_path
}
}
+6
View File
@@ -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
+168
View File
@@ -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
+18 -4
View File
@@ -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"
+28 -19
View File
@@ -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
}
}
}
+43
View File
@@ -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" {
+13 -3
View File
@@ -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)])
}
+3 -1
View File
@@ -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
+37 -35
View File
@@ -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,
@@ -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"
}
@@ -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"
}
@@ -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"
}
}
}
}
@@ -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"
}
}
}
}
@@ -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}
@@ -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}
@@ -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"
}
}
}
}
@@ -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}
@@ -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"
}
}
}
}
+29
View File
@@ -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 = ""
}
+70 -20
View File
@@ -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))
}
+6 -3
View File
@@ -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
}
+40 -3
View File
@@ -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,
}
}
}
+5 -1
View File
@@ -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, [])
}
+26 -33
View File
@@ -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(
+71
View File
@@ -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
+24 -1
View File
@@ -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)."
}
}
+43 -23
View File
@@ -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
}
}
}
+1 -1
View File
@@ -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" {
+319
View File
@@ -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
}
+37 -22
View File
@@ -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
}
}
}
+11 -1
View File
@@ -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]
}
+4 -4
View File
@@ -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}"]
@@ -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}"]
@@ -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"
}
}
}
}
+37
View File
@@ -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 = ""
}
+2 -2
View File
@@ -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)
}
+19 -2
View File
@@ -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 ? [""] : [
+44 -13
View File
@@ -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))
}
@@ -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
@@ -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.
+17 -15
View File
@@ -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) {
@@ -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",
),
)
@@ -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)
@@ -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
@@ -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)