diff --git a/assessorworkbench_cloud_init.tf b/assessorworkbench_cloud_init.tf index 0cf86631..eebdd6df 100644 --- a/assessorworkbench_cloud_init.tf +++ b/assessorworkbench_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "assessorworkbench_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "assessorworkbench", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/assessorworkbench_ec2.tf b/assessorworkbench_ec2.tf index 6df685e0..26c10ab6 100644 --- a/assessorworkbench_ec2.tf +++ b/assessorworkbench_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "assessorworkbench" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Assessor Workbench EC2 instances @@ -40,7 +40,6 @@ resource "aws_instance" "assessorworkbench" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.assessorworkbench.name instance_type = "t3.medium" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -56,23 +55,24 @@ resource "aws_instance" "assessorworkbench" { volume_size = 128 volume_type = "gp3" } - user_data_base64 = data.cloudinit_config.assessorworkbench_cloud_init_tasks[count.index].rendered - vpc_security_group_ids = [ - aws_security_group.cloudwatch_agent_endpoint_client.id, - aws_security_group.assessorworkbench.id, - aws_security_group.efs_client.id, - aws_security_group.guacamole_accessible.id, - aws_security_group.ssm_agent_endpoint_client.id, - ] + subnet_id = aws_subnet.operations.id tags = { Name = format("AssessorWorkbench%d", count.index) } + user_data_base64 = data.cloudinit_config.assessorworkbench_cloud_init_tasks[count.index].rendered # volume_tags does not yet inherit the default tags from the # provider. See hashicorp/terraform-provider-aws#19188 for more # details. volume_tags = merge(data.aws_default_tags.assessment.tags, { Name = format("AssessorWorkbench%d", count.index) }) + vpc_security_group_ids = [ + aws_security_group.cloudwatch_agent_endpoint_client.id, + aws_security_group.assessorworkbench.id, + aws_security_group.efs_client.id, + aws_security_group.guacamole_accessible.id, + aws_security_group.ssm_agent_endpoint_client.id, + ] } # The EBS volume for each Assessor Workbench instance; it is used to @@ -88,11 +88,10 @@ resource "aws_ebs_volume" "assessorworkbench_docker" { availability_zone = "${var.aws_region}${var.aws_availability_zone}" encrypted = true size = 16 - type = "gp3" - tags = { Name = format("AssessorWorkbench%d Docker", count.index) } + type = "gp3" } # Attach EBS volume to Assessor Workbench instance diff --git a/assessorworkbench_iam.tf b/assessorworkbench_iam.tf index 4b41c11b..6138e288 100644 --- a/assessorworkbench_iam.tf +++ b/assessorworkbench_iam.tf @@ -21,16 +21,16 @@ resource "aws_iam_role" "assessorworkbench_instance_role" { resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_assessorworkbench" { provider = aws.provisionassessment - role = aws_iam_role.assessorworkbench_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.assessorworkbench_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_assessorworkbench" { provider = aws.provisionassessment - role = aws_iam_role.assessorworkbench_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.assessorworkbench_instance_role.id } # Attach a policy that allows the Assessor Workbench instances to @@ -38,6 +38,6 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_assessorw resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_assessorworkbench" { provider = aws.provisionassessment - role = aws_iam_role.assessorworkbench_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.assessorworkbench_instance_role.id } diff --git a/assessorworkbench_route53.tf b/assessorworkbench_route53.tf index 73318568..e5d33653 100644 --- a/assessorworkbench_route53.tf +++ b/assessorworkbench_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "assessorworkbench_A" { count = lookup(var.operations_instance_counts, "assessorworkbench", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "assessorworkbench${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.assessorworkbench[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/assessorworkbench_sg.tf b/assessorworkbench_sg.tf index 2397444a..780cce79 100644 --- a/assessorworkbench_sg.tf +++ b/assessorworkbench_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "assessorworkbench" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { "Name" = "Assessor Workbench" } + vpc_id = aws_vpc.assessment.id } # Allow egress to anywhere via HTTP and HTTPS @@ -16,12 +15,12 @@ resource "aws_security_group_rule" "assessorworkbench_egress_to_anywhere_via_htt for_each = toset(["80", "443"]) provider = aws.provisionassessment - security_group_id = aws_security_group.assessorworkbench.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = each.key + protocol = "tcp" + security_group_id = aws_security_group.assessorworkbench.id to_port = each.key + type = "egress" } # Allow ingress from anywhere via the allowed ports @@ -32,10 +31,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_assessorworkbench_v # acceptable form. for_each = { for d in var.inbound_ports_allowed["assessorworkbench"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.assessorworkbench.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.assessorworkbench.id to_port = each.value["to_port"] + type = "ingress" } diff --git a/backend.tf b/backend.tf index b1284ab6..52e06caa 100644 --- a/backend.tf +++ b/backend.tf @@ -1,10 +1,10 @@ terraform { backend "s3" { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-assessment-terraform/terraform.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-assessment-terraform/terraform.tfstate" } } diff --git a/cloudwatch_agent_endpoint_client_sg.tf b/cloudwatch_agent_endpoint_client_sg.tf index d0563acb..823c1db7 100644 --- a/cloudwatch_agent_endpoint_client_sg.tf +++ b/cloudwatch_agent_endpoint_client_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "cloudwatch_agent_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "CloudWatch agent endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the CloudWatch agent endpoint security @@ -14,10 +13,10 @@ resource "aws_security_group" "cloudwatch_agent_endpoint_client" { resource "aws_security_group_rule" "egress_from_cloudwatch_agent_endpoint_client_to_cloudwatch_agent_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.cloudwatch_agent_endpoint_client.id - type = "egress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.cloudwatch_agent_endpoint_client.id source_security_group_id = aws_security_group.cloudwatch_agent_endpoint.id - from_port = 443 to_port = 443 + type = "egress" } diff --git a/cloudwatch_agent_endpoint_sg.tf b/cloudwatch_agent_endpoint_sg.tf index a0bdad06..15109e8d 100644 --- a/cloudwatch_agent_endpoint_sg.tf +++ b/cloudwatch_agent_endpoint_sg.tf @@ -3,11 +3,10 @@ resource "aws_security_group" "cloudwatch_agent_endpoint" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "CloudWatch agent endpoints" } + vpc_id = aws_vpc.assessment.id } # Allow ingress via HTTPS from the CloudWatch agent endpoint client @@ -15,10 +14,10 @@ resource "aws_security_group" "cloudwatch_agent_endpoint" { resource "aws_security_group_rule" "ingress_from_cloudwatch_agent_endpoint_client_to_cloudwatch_agent_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.cloudwatch_agent_endpoint.id - type = "ingress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.cloudwatch_agent_endpoint.id source_security_group_id = aws_security_group.cloudwatch_agent_endpoint_client.id - from_port = 443 to_port = 443 + type = "ingress" } diff --git a/debiandesktop_cloud_init.tf b/debiandesktop_cloud_init.tf index 4953d739..ce47a64e 100644 --- a/debiandesktop_cloud_init.tf +++ b/debiandesktop_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "debiandesktop_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "debiandesktop", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/debiandesktop_ec2.tf b/debiandesktop_ec2.tf index 7367ddd9..26783d08 100644 --- a/debiandesktop_ec2.tf +++ b/debiandesktop_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "debiandesktop" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The "Debian desktop" EC2 instances @@ -39,7 +39,6 @@ resource "aws_instance" "debiandesktop" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.debiandesktop.name instance_type = "t3.medium" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -55,23 +54,24 @@ resource "aws_instance" "debiandesktop" { volume_size = 128 volume_type = "gp3" } - user_data_base64 = data.cloudinit_config.debiandesktop_cloud_init_tasks[count.index].rendered - vpc_security_group_ids = [ - aws_security_group.cloudwatch_agent_endpoint_client.id, - aws_security_group.debiandesktop.id, - aws_security_group.efs_client.id, - aws_security_group.guacamole_accessible.id, - aws_security_group.ssm_agent_endpoint_client.id, - ] + subnet_id = aws_subnet.operations.id tags = { Name = format("DebianDesktop%d", count.index) } + user_data_base64 = data.cloudinit_config.debiandesktop_cloud_init_tasks[count.index].rendered # volume_tags does not yet inherit the default tags from the # provider. See hashicorp/terraform-provider-aws#19188 for more # details. volume_tags = merge(data.aws_default_tags.assessment.tags, { Name = format("DebianDesktop%d", count.index) }) + vpc_security_group_ids = [ + aws_security_group.cloudwatch_agent_endpoint_client.id, + aws_security_group.debiandesktop.id, + aws_security_group.efs_client.id, + aws_security_group.guacamole_accessible.id, + aws_security_group.ssm_agent_endpoint_client.id, + ] } # CloudWatch alarms for the Debian Desktop instances diff --git a/debiandesktop_iam.tf b/debiandesktop_iam.tf index 2689c145..3c6df43b 100644 --- a/debiandesktop_iam.tf +++ b/debiandesktop_iam.tf @@ -12,24 +12,24 @@ resource "aws_iam_instance_profile" "debiandesktop" { resource "aws_iam_role" "debiandesktop_instance_role" { provider = aws.provisionassessment - name = "debiandesktop_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "debiandesktop_instance_role_${terraform.workspace}" } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_debiandesktop" { provider = aws.provisionassessment - role = aws_iam_role.debiandesktop_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.debiandesktop_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_debiandesktop" { provider = aws.provisionassessment - role = aws_iam_role.debiandesktop_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.debiandesktop_instance_role.id } # Attach a policy that allows the Debian desktop instances to mount and @@ -37,6 +37,6 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_debiandes resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_debiandesktop" { provider = aws.provisionassessment - role = aws_iam_role.debiandesktop_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.debiandesktop_instance_role.id } diff --git a/debiandesktop_route53.tf b/debiandesktop_route53.tf index 595d327d..3d3e6aff 100644 --- a/debiandesktop_route53.tf +++ b/debiandesktop_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "debiandesktop_A" { count = lookup(var.operations_instance_counts, "debiandesktop", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "debiandesktop${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.debiandesktop[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/debiandesktop_sg.tf b/debiandesktop_sg.tf index 7294de60..93631679 100644 --- a/debiandesktop_sg.tf +++ b/debiandesktop_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "debiandesktop" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { "Name" = "Debian Desktop" } + vpc_id = aws_vpc.assessment.id } # Allow egress to Nessus web GUI port (8834) @@ -15,12 +14,12 @@ resource "aws_security_group" "debiandesktop" { resource "aws_security_group_rule" "debiandesktop_egress_to_nessus_via_web_ui" { provider = aws.provisionassessment - security_group_id = aws_security_group.debiandesktop.id - type = "egress" + from_port = 8834 protocol = "tcp" + security_group_id = aws_security_group.debiandesktop.id source_security_group_id = aws_security_group.nessus.id - from_port = 8834 to_port = 8834 + type = "egress" } # Allow egress to anywhere via HTTP and HTTPS @@ -30,12 +29,12 @@ resource "aws_security_group_rule" "debiandesktop_egress_to_anywhere_via_http_an for_each = toset(["80", "443"]) provider = aws.provisionassessment - security_group_id = aws_security_group.debiandesktop.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = each.key + protocol = "tcp" + security_group_id = aws_security_group.debiandesktop.id to_port = each.key + type = "egress" } # Allow ingress from anywhere via the allowed ports @@ -46,10 +45,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_debiandesktop_via_a # acceptable form. for_each = { for d in var.inbound_ports_allowed["debiandesktop"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.debiandesktop.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.debiandesktop.id to_port = each.value["to_port"] + type = "ingress" } diff --git a/dynamodb_endpoint_client_sg.tf b/dynamodb_endpoint_client_sg.tf index 24ce05ae..9d165550 100644 --- a/dynamodb_endpoint_client_sg.tf +++ b/dynamodb_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "dynamodb_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "DynamoDB endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the DynamoDB gateway endpoint. resource "aws_security_group_rule" "egress_to_dynamodb_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.dynamodb_endpoint_client.id - type = "egress" - protocol = "tcp" - prefix_list_ids = [aws_vpc_endpoint.dynamodb.prefix_list_id] from_port = 443 + prefix_list_ids = [aws_vpc_endpoint.dynamodb.prefix_list_id] + protocol = "tcp" + security_group_id = aws_security_group.dynamodb_endpoint_client.id to_port = 443 + type = "egress" } diff --git a/ec2_endpoint_client_sg.tf b/ec2_endpoint_client_sg.tf index 3da01993..21d2111f 100644 --- a/ec2_endpoint_client_sg.tf +++ b/ec2_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "ec2_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "EC2 endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the EC2 endpoint security group. resource "aws_security_group_rule" "egress_from_ec2_endpoint_client_to_ec2_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ec2_endpoint_client.id - type = "egress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ec2_endpoint_client.id source_security_group_id = aws_security_group.ec2_endpoint.id - from_port = 443 to_port = 443 + type = "egress" } diff --git a/ec2_endpoint_sg.tf b/ec2_endpoint_sg.tf index 9943e6ef..957db1a8 100644 --- a/ec2_endpoint_sg.tf +++ b/ec2_endpoint_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "ec2_endpoint" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "EC2 endpoint" } + vpc_id = aws_vpc.assessment.id } # Allow ingress via HTTPS from the EC2 endpoint client security group resource "aws_security_group_rule" "ingress_from_ec2_endpoint_client_to_ec2_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ec2_endpoint.id - type = "ingress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ec2_endpoint.id source_security_group_id = aws_security_group.ec2_endpoint_client.id - from_port = 443 to_port = 443 + type = "ingress" } diff --git a/ec2_service_assume_role_policy.tf b/ec2_service_assume_role_policy.tf index 92c21efe..63cd4106 100644 --- a/ec2_service_assume_role_policy.tf +++ b/ec2_service_assume_role_policy.tf @@ -11,8 +11,8 @@ data "aws_iam_policy_document" "ec2_service_assume_role_doc" { ] effect = "Allow" principals { - type = "Service" identifiers = ["ec2.amazonaws.com", ] + type = "Service" } } } diff --git a/ec2launch/windows-setup.tpl.yml b/ec2launch/windows-setup.tpl.yml index 92cc89f8..de664573 100644 --- a/ec2launch/windows-setup.tpl.yml +++ b/ec2launch/windows-setup.tpl.yml @@ -5,19 +5,15 @@ tasks: # current public SSH key deployed to Linux-based instances. This should # be removed once Windows AMIs are being built with the correct public # SSH key(s) preloaded. Please see #218 for more information. - - task: writeFile - inputs: - - frequency: once - destination: C:\Users\rvauser\.ssh\authorized_keys - content: | + - inputs: + - content: | ${vnc_public_ssh_key} + destination: C:\Users\rvauser\.ssh\authorized_keys + frequency: once + task: writeFile # Extend the OS partition and set up the Samba share as a network drive - - task: executeScript - inputs: - - frequency: always - type: powershell - runAs: localSystem - content: |- + - inputs: + - content: |- # The Windows AMIs we currently use have a System Reserve partition # that contains boot information in addition to the primary OS # partition. The automatic AWS partition extension in EC2Launch v2 @@ -45,3 +41,7 @@ tasks: -PSProvider "FileSystem" ` -Persist } + frequency: always + runAs: localSystem + type: powershell + task: executeScript diff --git a/egressassess_cloud_init.tf b/egressassess_cloud_init.tf index 4fedf72b..022c55ef 100644 --- a/egressassess_cloud_init.tf +++ b/egressassess_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "egressassess_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "egressassess", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/egressassess_ec2.tf b/egressassess_ec2.tf index 08f9db30..33d23ee0 100644 --- a/egressassess_ec2.tf +++ b/egressassess_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "egressassess" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Egress-Assess EC2 instances @@ -33,7 +33,6 @@ resource "aws_instance" "egressassess" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.egressassess.name instance_type = "t3.medium" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -49,22 +48,23 @@ resource "aws_instance" "egressassess" { volume_size = 8 volume_type = "gp3" } - user_data_base64 = data.cloudinit_config.egressassess_cloud_init_tasks[count.index].rendered - vpc_security_group_ids = [ - aws_security_group.cloudwatch_agent_endpoint_client.id, - aws_security_group.egressassess.id, - aws_security_group.guacamole_accessible.id, - aws_security_group.ssm_agent_endpoint_client.id, - ] + subnet_id = aws_subnet.operations.id tags = { Name = format("EgressAssess%d", count.index) } + user_data_base64 = data.cloudinit_config.egressassess_cloud_init_tasks[count.index].rendered # volume_tags does not yet inherit the default tags from the # provider. See hashicorp/terraform-provider-aws#19188 for more # details. volume_tags = merge(data.aws_default_tags.assessment.tags, { Name = format("EgressAssess%d", count.index) }) + vpc_security_group_ids = [ + aws_security_group.cloudwatch_agent_endpoint_client.id, + aws_security_group.egressassess.id, + aws_security_group.guacamole_accessible.id, + aws_security_group.ssm_agent_endpoint_client.id, + ] } # The Elastic IP for each Egress-Assess instance @@ -72,11 +72,11 @@ resource "aws_eip" "egressassess" { count = lookup(var.operations_instance_counts, "egressassess", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("EgressAssess%d EIP", count.index) "Publish Egress" = "True" } + vpc = true } # The EIP association for each Egress-Assess instance @@ -84,8 +84,8 @@ resource "aws_eip_association" "egressassess" { count = lookup(var.operations_instance_counts, "egressassess", 0) provider = aws.provisionassessment - instance_id = aws_instance.egressassess[count.index].id allocation_id = aws_eip.egressassess[count.index].id + instance_id = aws_instance.egressassess[count.index].id } # CloudWatch alarms for the Egress-Assess instances diff --git a/egressassess_iam.tf b/egressassess_iam.tf index 496d5f63..27090118 100644 --- a/egressassess_iam.tf +++ b/egressassess_iam.tf @@ -12,22 +12,22 @@ resource "aws_iam_instance_profile" "egressassess" { resource "aws_iam_role" "egressassess_instance_role" { provider = aws.provisionassessment - name = "egressassess_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "egressassess_instance_role_${terraform.workspace}" } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_egressassess" { provider = aws.provisionassessment - role = aws_iam_role.egressassess_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.egressassess_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_egressassess" { provider = aws.provisionassessment - role = aws_iam_role.egressassess_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.egressassess_instance_role.id } diff --git a/egressassess_route53.tf b/egressassess_route53.tf index 1c54de88..d8e1271e 100644 --- a/egressassess_route53.tf +++ b/egressassess_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "egressassess_A" { count = lookup(var.operations_instance_counts, "egressassess", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "egressassess${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.egressassess[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/egressassess_sg.tf b/egressassess_sg.tf index 7ed1e8cc..91b542eb 100644 --- a/egressassess_sg.tf +++ b/egressassess_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "egressassess" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { "Name" = "Egress-Assess" } + vpc_id = aws_vpc.assessment.id } # Allow egress to anywhere via HTTP and HTTPS @@ -16,12 +15,12 @@ resource "aws_security_group_rule" "egressassess_egress_to_anywhere_via_http_and for_each = toset(["80", "443"]) provider = aws.provisionassessment - security_group_id = aws_security_group.egressassess.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = each.key + protocol = "tcp" + security_group_id = aws_security_group.egressassess.id to_port = each.key + type = "egress" } # Allow ingress from anywhere via all ICMP @@ -30,12 +29,12 @@ resource "aws_security_group_rule" "egressassess_egress_to_anywhere_via_http_and resource "aws_security_group_rule" "ingress_from_anywhere_to_egressassess_via_all_icmp" { provider = aws.provisionassessment - security_group_id = aws_security_group.egressassess.id - type = "ingress" - protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] from_port = -1 + protocol = "icmp" + security_group_id = aws_security_group.egressassess.id to_port = -1 + type = "ingress" } # Allow ingress from anywhere via the allowed ports @@ -46,10 +45,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_egressassess_via_al # acceptable form. for_each = { for d in var.inbound_ports_allowed["egressassess"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.egressassess.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.egressassess.id to_port = each.value["to_port"] + type = "ingress" } diff --git a/gophish_cloud_init.tf b/gophish_cloud_init.tf index 9faee84b..6b5fc8b8 100644 --- a/gophish_cloud_init.tf +++ b/gophish_cloud_init.tf @@ -9,8 +9,8 @@ locals { data "cloudinit_config" "gophish_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "gophish", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/gophish_ec2.tf b/gophish_ec2.tf index 6ec9298a..e3efc615 100644 --- a/gophish_ec2.tf +++ b/gophish_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "gophish" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Gophish EC2 instances @@ -40,7 +40,6 @@ resource "aws_instance" "gophish" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.gophish[count.index].name instance_type = "t3.medium" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -56,7 +55,17 @@ resource "aws_instance" "gophish" { volume_size = 128 volume_type = "gp3" } + subnet_id = aws_subnet.operations.id + tags = { + Name = format("Gophish%d", count.index) + } user_data_base64 = data.cloudinit_config.gophish_cloud_init_tasks[count.index].rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Gophish%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.efs_client.id, @@ -67,15 +76,6 @@ resource "aws_instance" "gophish" { aws_security_group.ssm_agent_endpoint_client.id, aws_security_group.sts_endpoint_client.id, ] - tags = { - Name = format("Gophish%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Gophish%d", count.index) - }) } # The Elastic IP for each Gophish instance @@ -83,11 +83,11 @@ resource "aws_eip" "gophish" { count = lookup(var.operations_instance_counts, "gophish", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("Gophish%d EIP", count.index) "Publish Egress" = "True" } + vpc = true } # The EIP association for each Gophish instance @@ -95,8 +95,8 @@ resource "aws_eip_association" "gophish" { count = lookup(var.operations_instance_counts, "gophish", 0) provider = aws.provisionassessment - instance_id = aws_instance.gophish[count.index].id allocation_id = aws_eip.gophish[count.index].id + instance_id = aws_instance.gophish[count.index].id } # The EBS volume for each Gophish instance; it is used to persist Docker volume @@ -111,11 +111,10 @@ resource "aws_ebs_volume" "gophish_docker" { availability_zone = "${var.aws_region}${var.aws_availability_zone}" encrypted = true size = 16 - type = "gp3" - tags = { Name = format("Gophish%d Docker", count.index) } + type = "gp3" } # Attach EBS volume to Gophish instance diff --git a/gophish_iam.tf b/gophish_iam.tf index 83f727c4..ef67754a 100644 --- a/gophish_iam.tf +++ b/gophish_iam.tf @@ -17,8 +17,8 @@ resource "aws_iam_role" "gophish_instance_role" { provider = aws.provisionassessment - name = "gophish${count.index}_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "gophish${count.index}_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "gophish_assume_delegated_role_policy" { @@ -27,8 +27,8 @@ resource "aws_iam_role_policy" "gophish_assume_delegated_role_policy" { provider = aws.provisionassessment name = "assume_delegated_role_policy" - role = aws_iam_role.gophish_instance_role[count.index].id policy = data.aws_iam_policy_document.gophish_assume_delegated_role_policy_doc[count.index].json + role = aws_iam_role.gophish_instance_role[count.index].id } # Attach the CloudWatch Agent policy to these roles as well @@ -37,8 +37,8 @@ resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_go provider = aws.provisionassessment - role = aws_iam_role.gophish_instance_role[count.index].id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.gophish_instance_role[count.index].id } # Attach the SSM Agent policy to these roles as well @@ -47,8 +47,8 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_gophish" provider = aws.provisionassessment - role = aws_iam_role.gophish_instance_role[count.index].id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.gophish_instance_role[count.index].id } # Attach a policy that allows the Gophish instances to mount and @@ -58,8 +58,8 @@ resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_gophish" provider = aws.provisionassessment - role = aws_iam_role.gophish_instance_role[count.index].id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.gophish_instance_role[count.index].id } ################################ diff --git a/gophish_route53.tf b/gophish_route53.tf index 229f5806..0aeb7fb6 100644 --- a/gophish_route53.tf +++ b/gophish_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "gophish_A" { count = lookup(var.operations_instance_counts, "gophish", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "gophish${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.gophish[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/gophish_sg.tf b/gophish_sg.tf index ecc0f430..b1758a6d 100644 --- a/gophish_sg.tf +++ b/gophish_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "gophish" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Gophish" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from Teamserver instances via port 587 (SMTP mail submission) @@ -14,12 +13,12 @@ resource "aws_security_group" "gophish" { resource "aws_security_group_rule" "ingress_from_teamserver_to_gophish_via_smtp" { provider = aws.provisionassessment - security_group_id = aws_security_group.gophish.id - type = "ingress" + from_port = 587 protocol = "tcp" + security_group_id = aws_security_group.gophish.id source_security_group_id = aws_security_group.teamserver.id - from_port = 587 to_port = 587 + type = "ingress" } # Allow ingress from anywhere via the allowed ports @@ -30,10 +29,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_gophish_via_allowed # acceptable form. for_each = { for d in var.inbound_ports_allowed["gophish"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.gophish.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.gophish.id to_port = each.value["to_port"] + type = "ingress" } diff --git a/guacamole_accessible_sg.tf b/guacamole_accessible_sg.tf index 5e5c18b9..2a2f87bd 100644 --- a/guacamole_accessible_sg.tf +++ b/guacamole_accessible_sg.tf @@ -2,33 +2,32 @@ resource "aws_security_group" "guacamole_accessible" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Guacamole accessible" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from Guacamole instances via SSH resource "aws_security_group_rule" "ingress_from_guacamole_via_ssh" { provider = aws.provisionassessment - security_group_id = aws_security_group.guacamole_accessible.id - type = "ingress" + from_port = 22 protocol = "tcp" + security_group_id = aws_security_group.guacamole_accessible.id source_security_group_id = aws_security_group.guacamole.id - from_port = 22 to_port = 22 + type = "ingress" } # Allow ingress from Guacamole instances via VNC resource "aws_security_group_rule" "ingress_from_guacamole_via_vnc" { provider = aws.provisionassessment - security_group_id = aws_security_group.guacamole_accessible.id - type = "ingress" + from_port = 5901 protocol = "tcp" + security_group_id = aws_security_group.guacamole_accessible.id source_security_group_id = aws_security_group.guacamole.id - from_port = 5901 to_port = 5901 + type = "ingress" } diff --git a/guacamole_cloud_init.tf b/guacamole_cloud_init.tf index d59e44b1..4d370a40 100644 --- a/guacamole_cloud_init.tf +++ b/guacamole_cloud_init.tf @@ -1,8 +1,8 @@ # cloud-init commands for configuring Guacamole instance data "cloudinit_config" "guacamole_cloud_init_tasks" { - gzip = true base64_encode = true + gzip = true #------------------------------------------------------------------------------- # Cloud Config parts diff --git a/guacamole_ec2.tf b/guacamole_ec2.tf index b9e60064..eec20312 100644 --- a/guacamole_ec2.tf +++ b/guacamole_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "guacamole" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Guacamole EC2 instance @@ -49,7 +49,6 @@ resource "aws_instance" "guacamole" { ami = data.aws_ami.guacamole.id iam_instance_profile = aws_iam_instance_profile.guacamole.name instance_type = "t3.medium" - subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -71,7 +70,17 @@ resource "aws_instance" "guacamole" { volume_size = 8 volume_type = "gp3" } + subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id + tags = { + Name = "Guacamole" + } user_data_base64 = data.cloudinit_config.guacamole_cloud_init_tasks.rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = "Guacamole" + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.ec2_endpoint_client.id, @@ -81,15 +90,6 @@ resource "aws_instance" "guacamole" { aws_security_group.ssm_endpoint_client.id, aws_security_group.sts_endpoint_client.id, ] - tags = { - Name = "Guacamole" - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = "Guacamole" - }) } # CloudWatch alarms for the Guacamole instances diff --git a/guacamole_iam.tf b/guacamole_iam.tf index b3e1f2bb..88317559 100644 --- a/guacamole_iam.tf +++ b/guacamole_iam.tf @@ -25,16 +25,16 @@ resource "aws_iam_instance_profile" "guacamole" { resource "aws_iam_role" "guacamole_instance_role" { provider = aws.provisionassessment - name = "guacamole_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "guacamole_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "guacamole_assume_delegated_role_policy" { provider = aws.provisionassessment name = "assume_delegated_role_policy" - role = aws_iam_role.guacamole_instance_role.id policy = data.aws_iam_policy_document.guacamole_assume_delegated_role_policy_doc.json + role = aws_iam_role.guacamole_instance_role.id } # Attach the EC2 read-only policy to this role. This policy is @@ -44,24 +44,24 @@ resource "aws_iam_role_policy" "guacamole_assume_delegated_role_policy" { resource "aws_iam_role_policy_attachment" "ec2_read_only_policy_attachment_guacamole" { provider = aws.provisionassessment - role = aws_iam_role.guacamole_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess" + role = aws_iam_role.guacamole_instance_role.id } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_guacamole" { provider = aws.provisionassessment - role = aws_iam_role.guacamole_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.guacamole_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_guacamole" { provider = aws.provisionassessment - role = aws_iam_role.guacamole_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.guacamole_instance_role.id } ################################ diff --git a/guacamole_parameterstore_assume_role_policy_doc.tf b/guacamole_parameterstore_assume_role_policy_doc.tf index b54afb95..0400ad71 100644 --- a/guacamole_parameterstore_assume_role_policy_doc.tf +++ b/guacamole_parameterstore_assume_role_policy_doc.tf @@ -11,10 +11,10 @@ data "aws_iam_policy_document" "guacamole_parameterstore_assume_role_doc" { ] principals { - type = "AWS" identifiers = [ aws_iam_role.guacamole_instance_role.arn, ] + type = "AWS" } } } diff --git a/guacamole_route53.tf b/guacamole_route53.tf index 3c533559..ada875fd 100644 --- a/guacamole_route53.tf +++ b/guacamole_route53.tf @@ -1,17 +1,16 @@ resource "aws_route53_record" "guacamole_A" { provider = aws.dns_sharedservices - zone_id = local.cool_dns_private_zone.zone_id name = "guac.${local.assessment_account_name_base}.${var.cool_domain}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.guacamole.private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = local.cool_dns_private_zone.zone_id } resource "aws_route53_record" "guacamole_PTR" { provider = aws.provisionassessment - zone_id = aws_route53_zone.private_subnet_reverse[var.private_subnet_cidr_blocks[0]].id # Terraform and/or AWS appends the reverse zone name if you specify # just enough of the record name to "fill in" the rest of the PTR record. # For example, if this record were for the IP 10.11.12.13, going into the @@ -26,10 +25,10 @@ resource "aws_route53_record" "guacamole_PTR" { "%s", element(split(".", aws_instance.guacamole.private_ip), 3) ) - - type = "PTR" - ttl = var.dns_ttl records = [ "guac.${local.assessment_account_name_base}.${var.cool_domain}" ] + ttl = var.dns_ttl + type = "PTR" + zone_id = aws_route53_zone.private_subnet_reverse[var.private_subnet_cidr_blocks[0]].id } diff --git a/guacamole_sg.tf b/guacamole_sg.tf index d2706a47..5654f409 100644 --- a/guacamole_sg.tf +++ b/guacamole_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "guacamole" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Guacamole (desktop gateway)" } + vpc_id = aws_vpc.assessment.id } # Allow egress via SSH to instances that wish to be accessible via @@ -14,12 +13,12 @@ resource "aws_security_group" "guacamole" { resource "aws_security_group_rule" "guacamole_egress_to_hosts_via_ssh" { provider = aws.provisionassessment + from_port = 22 security_group_id = aws_security_group.guacamole.id - type = "egress" - protocol = "tcp" source_security_group_id = aws_security_group.guacamole_accessible.id - from_port = 22 + protocol = "tcp" to_port = 22 + type = "egress" } # Allow egress via VNC to instances that wish to be accessible via @@ -27,12 +26,12 @@ resource "aws_security_group_rule" "guacamole_egress_to_hosts_via_ssh" { resource "aws_security_group_rule" "guacamole_egress_to_hosts_via_vnc" { provider = aws.provisionassessment - security_group_id = aws_security_group.guacamole.id - type = "egress" + from_port = 5901 protocol = "tcp" + security_group_id = aws_security_group.guacamole.id source_security_group_id = aws_security_group.guacamole_accessible.id - from_port = 5901 to_port = 5901 + type = "egress" } # Allow ingress from COOL Shared Services VPN server CIDR block @@ -42,13 +41,13 @@ resource "aws_security_group_rule" "guacamole_egress_to_hosts_via_vnc" { resource "aws_security_group_rule" "guacamole_ingress_from_trusted_via_https" { provider = aws.provisionassessment + cidr_blocks = [local.vpn_server_cidr_block] + # ipv6_cidr_blocks = TBD + from_port = 443 + protocol = "tcp" security_group_id = aws_security_group.guacamole.id + to_port = 443 type = "ingress" - protocol = "tcp" - cidr_blocks = [local.vpn_server_cidr_block] - # ipv6_cidr_blocks = TBD - from_port = 443 - to_port = 443 } # Allow egress to COOL Shared Services via IPA-related ports @@ -58,10 +57,10 @@ resource "aws_security_group_rule" "guacamole_egress_to_cool_via_ipa_ports" { provider = aws.provisionassessment for_each = local.ipa_ports - security_group_id = aws_security_group.guacamole.id - type = "egress" - protocol = each.value.protocol cidr_blocks = [local.cool_shared_services_cidr_block] from_port = each.value.port + protocol = each.value.protocol + security_group_id = aws_security_group.guacamole.id to_port = each.value.port + type = "egress" } diff --git a/kali_cloud_init.tf b/kali_cloud_init.tf index 9cdaaaeb..3de68b5a 100644 --- a/kali_cloud_init.tf +++ b/kali_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "kali_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "kali", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/kali_ec2.tf b/kali_ec2.tf index e42a4293..9e90923b 100644 --- a/kali_ec2.tf +++ b/kali_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "kali" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Kali EC2 instances @@ -39,7 +39,6 @@ resource "aws_instance" "kali" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.kali.name instance_type = "t3.xlarge" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -55,7 +54,17 @@ resource "aws_instance" "kali" { volume_size = 128 volume_type = "gp3" } + subnet_id = aws_subnet.operations.id + tags = { + Name = format("Kali%d", count.index) + } user_data_base64 = data.cloudinit_config.kali_cloud_init_tasks[count.index].rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Kali%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.efs_client.id, @@ -66,15 +75,6 @@ resource "aws_instance" "kali" { aws_security_group.ssm_agent_endpoint_client.id, aws_security_group.sts_endpoint_client.id, ] - tags = { - Name = format("Kali%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Kali%d", count.index) - }) } # The Elastic IP for each Kali instance @@ -82,11 +82,11 @@ resource "aws_eip" "kali" { count = lookup(var.operations_instance_counts, "kali", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("Kali%d EIP", count.index) "Publish Egress" = "True" } + vpc = true } # The EIP association for each Kali instance @@ -94,8 +94,8 @@ resource "aws_eip_association" "kali" { count = lookup(var.operations_instance_counts, "kali", 0) provider = aws.provisionassessment - instance_id = aws_instance.kali[count.index].id allocation_id = aws_eip.kali[count.index].id + instance_id = aws_instance.kali[count.index].id } # CloudWatch alarms for the Kali instances diff --git a/kali_iam.tf b/kali_iam.tf index 0dae33f0..5840af85 100644 --- a/kali_iam.tf +++ b/kali_iam.tf @@ -12,32 +12,32 @@ resource "aws_iam_instance_profile" "kali" { resource "aws_iam_role" "kali_instance_role" { provider = aws.provisionassessment - name = "kali_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "kali_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "kali_assume_delegated_role_policy" { provider = aws.provisionassessment name = "kali_assume_delegated_role_policy" - role = aws_iam_role.kali_instance_role.id policy = data.aws_iam_policy_document.kali_assume_delegated_role_policy_doc.json + role = aws_iam_role.kali_instance_role.id } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_kali" { provider = aws.provisionassessment - role = aws_iam_role.kali_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.kali_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_kali" { provider = aws.provisionassessment - role = aws_iam_role.kali_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.kali_instance_role.id } # Attach a policy that allows the Kali instances to mount and write to @@ -45,8 +45,8 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_kali" { resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_kali" { provider = aws.provisionassessment - role = aws_iam_role.kali_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.kali_instance_role.id } ################################ diff --git a/kali_route53.tf b/kali_route53.tf index 4cd243f9..1c78d76a 100644 --- a/kali_route53.tf +++ b/kali_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "kali_A" { count = lookup(var.operations_instance_counts, "kali", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "kali${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.kali[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/kali_sg.tf b/kali_sg.tf index 077c304f..41c2774c 100644 --- a/kali_sg.tf +++ b/kali_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "kali" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Kali" } + vpc_id = aws_vpc.assessment.id } # Allow egress to PenTest Portal instances via ports 443 and 8080 @@ -14,12 +13,12 @@ resource "aws_security_group_rule" "kali_egress_to_pentestportal_via_web" { for_each = toset(["443", "8080"]) provider = aws.provisionassessment - security_group_id = aws_security_group.kali.id - type = "egress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.kali.id source_security_group_id = aws_security_group.pentestportal.id - from_port = each.key to_port = each.key + type = "egress" } # Allow egress to teamservers via ports 993 and 50050 (IMAP over @@ -28,12 +27,12 @@ resource "aws_security_group_rule" "kali_egress_to_teamserver_via_imaps_and_cs" for_each = toset(["993", "50050"]) provider = aws.provisionassessment - security_group_id = aws_security_group.kali.id - type = "egress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.kali.id source_security_group_id = aws_security_group.teamserver.id - from_port = each.key to_port = each.key + type = "egress" } # Allow egress to Nessus instances via port 8834 (the default port @@ -41,12 +40,12 @@ resource "aws_security_group_rule" "kali_egress_to_teamserver_via_imaps_and_cs" resource "aws_security_group_rule" "kali_egress_to_nessus_via_web_ui" { provider = aws.provisionassessment + from_port = 8834 security_group_id = aws_security_group.kali.id - type = "egress" - protocol = "tcp" source_security_group_id = aws_security_group.nessus.id - from_port = 8834 + protocol = "tcp" to_port = 8834 + type = "egress" } # Allow ingress from anywhere via the allowed ports diff --git a/nessus_assume_role_policy_doc.tf b/nessus_assume_role_policy_doc.tf index d08e458b..4c45d766 100644 --- a/nessus_assume_role_policy_doc.tf +++ b/nessus_assume_role_policy_doc.tf @@ -11,10 +11,10 @@ data "aws_iam_policy_document" "nessus_assume_role_doc" { ] principals { - type = "AWS" identifiers = [ aws_iam_role.nessus_instance_role.arn ] + type = "AWS" } } } diff --git a/nessus_cloud_init.tf b/nessus_cloud_init.tf index a37d7e1f..0405a75c 100644 --- a/nessus_cloud_init.tf +++ b/nessus_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "nessus_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "nessus", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to name the # mime-parts of the user-data. It does not affect the final name for the @@ -72,8 +72,7 @@ data "cloudinit_config" "nessus_cloud_init_tasks" { } part { - filename = "nessus-setup.sh" - content_type = "text/x-shellscript" + filename = "nessus-setup.sh" content = templatefile( "${path.module}/cloud-init/nessus-setup.sh", { aws_region = var.aws_region @@ -83,5 +82,6 @@ data "cloudinit_config" "nessus_cloud_init_tasks" { ssm_key_nessus_admin_username = var.ssm_key_nessus_admin_username ssm_nessus_read_role_arn = aws_iam_role.nessus_parameterstorereadonly_role.arn }) + content_type = "text/x-shellscript" } } diff --git a/nessus_ec2.tf b/nessus_ec2.tf index 574ef6d3..a437a7c9 100644 --- a/nessus_ec2.tf +++ b/nessus_ec2.tf @@ -24,8 +24,8 @@ data "aws_ami" "nessus" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Nessus EC2 instance @@ -51,7 +51,6 @@ resource "aws_instance" "nessus" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.nessus.name instance_type = "m5.large" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -67,7 +66,17 @@ resource "aws_instance" "nessus" { volume_size = 128 volume_type = "gp3" } + subnet_id = aws_subnet.operations.id + tags = { + Name = format("Nessus%d", count.index) + } user_data_base64 = data.cloudinit_config.nessus_cloud_init_tasks[count.index].rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Nessus%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.guacamole_accessible.id, @@ -77,15 +86,6 @@ resource "aws_instance" "nessus" { aws_security_group.ssm_endpoint_client.id, aws_security_group.sts_endpoint_client.id, ] - tags = { - Name = format("Nessus%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Nessus%d", count.index) - }) } # The Elastic IP for each Nessus instance @@ -93,11 +93,11 @@ resource "aws_eip" "nessus" { count = lookup(var.operations_instance_counts, "nessus", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("Nessus%d EIP", count.index) "Publish Egress" = "True" } + vpc = true } # The EIP association for each Nessus instance @@ -105,8 +105,8 @@ resource "aws_eip_association" "nessus" { count = lookup(var.operations_instance_counts, "nessus", 0) provider = aws.provisionassessment - instance_id = aws_instance.nessus[count.index].id allocation_id = aws_eip.nessus[count.index].id + instance_id = aws_instance.nessus[count.index].id } # CloudWatch alarms for the Nessus instances diff --git a/nessus_iam.tf b/nessus_iam.tf index a0e030b7..e08dfea7 100644 --- a/nessus_iam.tf +++ b/nessus_iam.tf @@ -12,32 +12,32 @@ resource "aws_iam_instance_profile" "nessus" { resource "aws_iam_role" "nessus_instance_role" { provider = aws.provisionassessment - name = "nessus_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "nessus_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "nessus_assume_delegated_role_policy" { provider = aws.provisionassessment name = "nessus_assume_delegated_role_policy" - role = aws_iam_role.nessus_instance_role.id policy = data.aws_iam_policy_document.nessus_assume_delegated_role_policy_doc.json + role = aws_iam_role.nessus_instance_role.id } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_nessus" { provider = aws.provisionassessment - role = aws_iam_role.nessus_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.nessus_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_nessus" { provider = aws.provisionassessment - role = aws_iam_role.nessus_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.nessus_instance_role.id } ################################ @@ -52,9 +52,9 @@ data "aws_iam_policy_document" "nessus_assume_delegated_role_policy_doc" { "sts:AssumeRole", "sts:TagSession", ] + effect = "Allow" resources = [ aws_iam_role.nessus_parameterstorereadonly_role.arn ] - effect = "Allow" } } diff --git a/nessus_route53.tf b/nessus_route53.tf index 14e46053..54d20706 100644 --- a/nessus_route53.tf +++ b/nessus_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "nessus_A" { count = lookup(var.operations_instance_counts, "nessus", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "nessus${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.nessus[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/nessus_sg.tf b/nessus_sg.tf index 999b33f0..20189fd3 100644 --- a/nessus_sg.tf +++ b/nessus_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "nessus" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Nessus" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from Debian desktop instances via Nessus web GUI @@ -15,12 +14,12 @@ resource "aws_security_group" "nessus" { resource "aws_security_group_rule" "nessus_ingress_from_debiandesktop_via_web_ui" { provider = aws.provisionassessment - security_group_id = aws_security_group.nessus.id - type = "ingress" + from_port = 8834 protocol = "tcp" + security_group_id = aws_security_group.nessus.id source_security_group_id = aws_security_group.debiandesktop.id - from_port = 8834 to_port = 8834 + type = "ingress" } # Allow ingress from Kali instances via Nessus web GUI @@ -29,12 +28,12 @@ resource "aws_security_group_rule" "nessus_ingress_from_debiandesktop_via_web_ui resource "aws_security_group_rule" "nessus_ingress_from_kali_via_web_ui" { provider = aws.provisionassessment - security_group_id = aws_security_group.nessus.id - type = "ingress" + from_port = 8834 protocol = "tcp" + security_group_id = aws_security_group.nessus.id source_security_group_id = aws_security_group.kali.id - from_port = 8834 to_port = 8834 + type = "ingress" } # Allow ingress from Windows instances via Nessus web GUI @@ -43,12 +42,12 @@ resource "aws_security_group_rule" "nessus_ingress_from_kali_via_web_ui" { resource "aws_security_group_rule" "nessus_ingress_from_windows_via_web_ui" { provider = aws.provisionassessment - security_group_id = aws_security_group.nessus.id - type = "ingress" + from_port = 8834 protocol = "tcp" + security_group_id = aws_security_group.nessus.id source_security_group_id = aws_security_group.windows.id - from_port = 8834 to_port = 8834 + type = "ingress" } # Allow ingress from anywhere via the allowed ports @@ -59,10 +58,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_nessus_via_allowed_ # acceptable form. for_each = { for d in var.inbound_ports_allowed["nessus"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.nessus.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + security_group_id = aws_security_group.nessus.id + protocol = each.value["protocol"] to_port = each.value["to_port"] + type = "ingress" } diff --git a/network_acls.tf b/network_acls.tf index cbab1cbf..c66dc12f 100644 --- a/network_acls.tf +++ b/network_acls.tf @@ -7,14 +7,13 @@ resource "aws_network_acl" "operations" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id subnet_ids = [ aws_subnet.operations.id, ] - tags = { "Name" = "Assessment Operations" } + vpc_id = aws_vpc.assessment.id } # ACLs for the private subnets of the VPC @@ -23,12 +22,11 @@ resource "aws_network_acl" "private" { for_each = toset(var.private_subnet_cidr_blocks) - vpc_id = aws_vpc.assessment.id subnet_ids = [ aws_subnet.private[each.key].id, ] - tags = { "Name" = format("Assessment Private - %s", each.key) } + vpc_id = aws_vpc.assessment.id } diff --git a/operations_acl_rules.tf b/operations_acl_rules.tf index a5d0b7f3..5d797e2c 100644 --- a/operations_acl_rules.tf +++ b/operations_acl_rules.tf @@ -7,13 +7,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_ssh" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.operations.id + cidr_block = aws_subnet.private[each.value].cidr_block egress = false + from_port = 22 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 100 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.private[each.value].cidr_block - from_port = 22 + rule_number = 100 + index(var.private_subnet_cidr_blocks, each.value) to_port = 22 } @@ -26,13 +26,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_winrm" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.operations.id + cidr_block = aws_subnet.private[each.value].cidr_block egress = false + from_port = 5986 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 110 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.private[each.value].cidr_block - from_port = 5986 + rule_number = 110 + index(var.private_subnet_cidr_blocks, each.value) to_port = 5986 } # Disallow ingress from anywhere else via port 5986 (Windows Remote @@ -40,13 +40,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_winrm" { resource "aws_network_acl_rule" "operations_ingress_from_anywhere_else_winrm" { provider = aws.provisionassessment - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = 5986 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 115 rule_action = "deny" - cidr_block = "0.0.0.0/0" - from_port = 5986 + rule_number = 115 to_port = 5986 } @@ -58,26 +58,26 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_vnc" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.operations.id + cidr_block = aws_subnet.private[each.value].cidr_block egress = false + from_port = 5901 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 120 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.private[each.value].cidr_block - from_port = 5901 + rule_number = 120 + index(var.private_subnet_cidr_blocks, each.value) to_port = 5901 } # Disallow ingress from anywhere else via VNC. resource "aws_network_acl_rule" "operations_ingress_from_anywhere_else_vnc" { provider = aws.provisionassessment - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = 5901 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 125 rule_action = "deny" - cidr_block = "0.0.0.0/0" - from_port = 5901 + rule_number = 125 to_port = 5901 } @@ -88,13 +88,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_http" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.operations.id + cidr_block = aws_subnet.private[each.value].cidr_block egress = false + from_port = 80 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 130 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.private[each.value].cidr_block - from_port = 80 + rule_number = 130 + index(var.private_subnet_cidr_blocks, each.value) to_port = 80 } @@ -105,13 +105,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_private_via_https" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.operations.id + cidr_block = aws_subnet.private[each.value].cidr_block egress = false + from_port = 443 + network_acl_id = aws_network_acl.operations.id protocol = "tcp" - rule_number = 140 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.private[each.value].cidr_block - from_port = 443 + rule_number = 140 + index(var.private_subnet_cidr_blocks, each.value) to_port = 443 } @@ -123,13 +123,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_allowed_po provider = aws.provisionassessment for_each = local.union_of_inbound_ports_allowed - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = each.value.from_port + network_acl_id = aws_network_acl.operations.id protocol = each.value.protocol - rule_number = 150 + each.value.index rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = each.value.from_port + rule_number = 150 + each.value.index to_port = each.value.to_port } @@ -142,13 +142,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_ports_1024 provider = aws.provisionassessment for_each = toset(local.tcp_and_udp) - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = 1024 + network_acl_id = aws_network_acl.operations.id protocol = each.value - rule_number = 170 + index(local.tcp_and_udp, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 1024 + rule_number = 170 + index(local.tcp_and_udp, each.value) to_port = 3388 } @@ -162,13 +162,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_ports_3390 provider = aws.provisionassessment for_each = toset(local.tcp_and_udp) - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = 3390 + network_acl_id = aws_network_acl.operations.id protocol = each.value - rule_number = 180 + index(local.tcp_and_udp, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 3390 + rule_number = 180 + index(local.tcp_and_udp, each.value) to_port = 50049 } @@ -180,13 +180,13 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_ports_5005 provider = aws.provisionassessment for_each = toset(local.tcp_and_udp) - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + from_port = 50051 + network_acl_id = aws_network_acl.operations.id protocol = each.value - rule_number = 200 + index(local.tcp_and_udp, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 50051 + rule_number = 200 + index(local.tcp_and_udp, each.value) to_port = 65535 } @@ -196,14 +196,14 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_ports_5005 resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_icmp" { provider = aws.provisionassessment - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = false + icmp_code = -1 + icmp_type = -1 + network_acl_id = aws_network_acl.operations.id protocol = "icmp" - rule_number = 210 rule_action = "allow" - cidr_block = "0.0.0.0/0" - icmp_type = -1 - icmp_code = -1 + rule_number = 210 } # Allow egress to anywhere via any protocol and port @@ -216,12 +216,12 @@ resource "aws_network_acl_rule" "operations_ingress_from_anywhere_via_icmp" { resource "aws_network_acl_rule" "operations_egress_to_anywhere_via_any_port" { provider = aws.provisionassessment - network_acl_id = aws_network_acl.operations.id + cidr_block = "0.0.0.0/0" egress = true + from_port = 0 + network_acl_id = aws_network_acl.operations.id protocol = "-1" - rule_number = 300 rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 0 + rule_number = 300 to_port = 0 } diff --git a/operations_routing.tf b/operations_routing.tf index 0a5d15ab..24fca56e 100644 --- a/operations_routing.tf +++ b/operations_routing.tf @@ -17,8 +17,8 @@ resource "aws_default_route_table" "operations" { resource "aws_route" "cool_operations" { provider = aws.provisionassessment - route_table_id = aws_default_route_table.operations.id destination_cidr_block = local.cool_shared_services_cidr_block + route_table_id = aws_default_route_table.operations.id transit_gateway_id = local.transit_gateway_id } @@ -35,7 +35,7 @@ resource "aws_vpc_endpoint_route_table_association" "s3_operations" { resource "aws_route" "external_operations" { provider = aws.provisionassessment - route_table_id = aws_default_route_table.operations.id destination_cidr_block = "0.0.0.0/0" + route_table_id = aws_default_route_table.operations.id gateway_id = aws_internet_gateway.assessment.id } diff --git a/outputs.tf b/outputs.tf index fc0e0d24..ad3bb82f 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,314 +1,314 @@ output "assessment_private_zone" { - value = aws_route53_zone.assessment_private description = "The private DNS zone for this assessment." + value = aws_route53_zone.assessment_private } output "assessor_workbench_instance_profile" { - value = aws_iam_instance_profile.assessorworkbench description = "The instance profile for the Assessor Workbench instances." + value = aws_iam_instance_profile.assessorworkbench } output "assessor_workbench_instances" { - value = aws_instance.assessorworkbench description = "The Assessor Workbench instances." + value = aws_instance.assessorworkbench } output "assessor_workbench_security_group" { - value = aws_security_group.assessorworkbench description = "The security group for the Assessor Workbench instances." + value = aws_security_group.assessorworkbench } output "aws_region" { - value = var.aws_region description = "The AWS region where this assessment environment lives." + value = var.aws_region } output "certificate_bucket_name" { - value = var.cert_bucket_name description = "The name of the S3 bucket where certificate information is stored for this assessment." + value = var.cert_bucket_name } output "cloudwatch_agent_endpoint_client_security_group" { - value = aws_security_group.cloudwatch_agent_endpoint_client description = "A security group for any instances that run the AWS CloudWatch agent. This security groups allows such instances to communicate with the VPC endpoints that are required by the AWS CloudWatch agent." + value = aws_security_group.cloudwatch_agent_endpoint_client } output "debian_desktop_instance_profile" { - value = aws_iam_instance_profile.debiandesktop description = "The instance profile for the Debian desktop instances." + value = aws_iam_instance_profile.debiandesktop } output "debian_desktop_instances" { - value = aws_instance.debiandesktop description = "The Debian desktop instances." + value = aws_instance.debiandesktop } output "debian_desktop_security_group" { - value = aws_security_group.debiandesktop description = "The security group for the Debian desktop instances." + value = aws_security_group.debiandesktop } output "dynamodb_endpoint_client_security_group" { - value = aws_security_group.dynamodb_endpoint_client description = "A security group for any instances that wish to communicate with the DynamoDB VPC endpoint." + value = aws_security_group.dynamodb_endpoint_client } output "ec2_endpoint_client_security_group" { - value = aws_security_group.ec2_endpoint_client description = "A security group for any instances that wish to communicate with the EC2 VPC endpoint." + value = aws_security_group.ec2_endpoint_client } output "efs_access_points" { - value = aws_efs_access_point.access_point description = "The access points to control file-system access to the EFS file share." + value = aws_efs_access_point.access_point } output "efs_client_security_group" { - value = aws_security_group.efs_client description = "A security group that should be applied to all instances that will mount the EFS file share." + value = aws_security_group.efs_client } output "efs_mount_targets" { - value = aws_efs_mount_target.target description = "The mount targets for the EFS file share." + value = aws_efs_mount_target.target } output "egressassess_instance_profile" { - value = aws_iam_instance_profile.egressassess description = "The instance profile for the Egress-Assess instances." + value = aws_iam_instance_profile.egressassess } output "egressassess_instances" { - value = aws_instance.egressassess description = "The Egress-Assess instances." + value = aws_instance.egressassess } output "egressassess_security_group" { - value = aws_security_group.egressassess description = "The security group for the Egress-Assess instances." + value = aws_security_group.egressassess } output "email_sending_domain_certreadroles" { - value = module.email_sending_domain_certreadrole description = "The IAM roles that allow for reading the certificate for each email-sending domain." + value = module.email_sending_domain_certreadrole } output "gophish_instance_profiles" { - value = aws_iam_instance_profile.gophish description = "The instance profiles for the Gophish instances." + value = aws_iam_instance_profile.gophish } output "gophish_instances" { - value = aws_instance.gophish description = "The Gophish instances." + value = aws_instance.gophish } output "gophish_security_group" { - value = aws_security_group.gophish description = "The security group for the Gophish instances." + value = aws_security_group.gophish } output "guacamole_accessible_security_group" { - value = aws_security_group.guacamole_accessible description = "A security group that should be applied to all instances that are to be accessible from Guacamole." + value = aws_security_group.guacamole_accessible } output "guacamole_server" { - value = aws_instance.guacamole description = "The AWS EC2 instance hosting Guacamole." + value = aws_instance.guacamole } output "kali_instance_profile" { - value = aws_iam_instance_profile.kali description = "The instance profile for the Kali instances." + value = aws_iam_instance_profile.kali } output "kali_instances" { - value = aws_instance.kali description = "The Kali instances." + value = aws_instance.kali } output "kali_security_group" { - value = aws_security_group.kali description = "The security group for the Kali instances." + value = aws_security_group.kali } output "nessus_instance_profile" { - value = aws_iam_instance_profile.nessus description = "The instance profile for the Nessus instances." + value = aws_iam_instance_profile.nessus } output "nessus_instances" { - value = aws_instance.nessus description = "The Nessus instances." + value = aws_instance.nessus } output "nessus_security_group" { - value = aws_security_group.nessus description = "The security group for the Nessus instances." + value = aws_security_group.nessus } output "operations_subnet" { - value = aws_subnet.operations description = "The operations subnet." + value = aws_subnet.operations } output "operations_subnet_acl" { - value = aws_network_acl.operations description = "The access control list (ACL) for the operations subnet." + value = aws_network_acl.operations } output "pentest_portal_instance_profile" { - value = aws_iam_instance_profile.pentestportal description = "The instance profile for the Pentest Portal instances." + value = aws_iam_instance_profile.pentestportal } output "pentest_portal_instances" { - value = aws_instance.pentestportal description = "The Pentest Portal instances." + value = aws_instance.pentestportal } output "pentest_portal_security_group" { - value = aws_security_group.pentestportal description = "The security group for the Pentest Portal instances." + value = aws_security_group.pentestportal } output "private_subnet_cidr_blocks" { - value = var.private_subnet_cidr_blocks description = "The private subnet CIDR blocks. These are used to index into the private_subnets and efs_mount_targets outputs." + value = var.private_subnet_cidr_blocks } output "private_subnet_nat_gateway" { - value = aws_nat_gateway.nat_gw description = "The NAT gateway for the private subnets." + value = aws_nat_gateway.nat_gw } output "private_subnets" { - value = aws_subnet.private description = "The private subnets." + value = aws_subnet.private } output "private_subnet_acls" { - value = aws_network_acl.private description = "The access control lists (ACLs) for the private subnets." + value = aws_network_acl.private } output "read_terraform_state_module" { - value = module.read_terraform_state description = "The IAM policies and role that allow read-only access to the cool-assessment-terraform workspace-specific state in the Terraform state bucket." + value = module.read_terraform_state } output "remote_desktop_url" { - value = "https://${aws_route53_record.guacamole_A.name}" description = "The URL of the remote desktop gateway (Guacamole) for this assessment." + value = "https://${aws_route53_record.guacamole_A.name}" } output "s3_endpoint_client_security_group" { - value = aws_security_group.s3_endpoint_client description = "A security group for any instances that wish to communicate with the S3 VPC endpoint." + value = aws_security_group.s3_endpoint_client } output "samba_client_security_group" { - value = aws_security_group.smb_client description = "The security group that should be applied to all instance types that wish to mount the Samba file share being served by the Samba file share server instances." + value = aws_security_group.smb_client } output "samba_instance_profile" { - value = aws_iam_instance_profile.samba description = "The instance profile for the Samba file share server instances." + value = aws_iam_instance_profile.samba } output "samba_instances" { - value = aws_instance.samba description = "The Samba file share server instances." + value = aws_instance.samba } output "samba_server_security_group" { - value = aws_security_group.smb_server description = "The security group for the Samba file share server instances." + value = aws_security_group.smb_server } output "scanner_security_group" { - value = aws_security_group.scanner description = "A security group that should be applied to all instance types that perform scanning. This security group allows egress to anywhere as well as ingress from anywhere via ICMP." + value = aws_security_group.scanner } output "ssm_agent_endpoint_client_security_group" { - value = aws_security_group.ssm_agent_endpoint_client description = "A security group for any instances that run the AWS SSM agent. This security group allows such instances to communicate with the VPC endpoints that are required by the AWS SSM agent." + value = aws_security_group.ssm_agent_endpoint_client } output "ssm_endpoint_client_security_group" { - value = aws_security_group.ssm_endpoint_client description = "A security group for any instances that wish to communicate with the SSM VPC endpoint." + value = aws_security_group.ssm_endpoint_client } output "ssm_session_role" { - value = module.session_manager.ssm_session_role description = "An IAM role that allows creation of SSM SessionManager sessions to any EC2 instance in this account." + value = module.session_manager.ssm_session_role } output "sts_endpoint_client_security_group" { - value = aws_security_group.sts_endpoint_client description = "A security group for any instances that wish to communicate with the STS VPC endpoint." + value = aws_security_group.sts_endpoint_client } output "teamserver_instance_profiles" { - value = aws_iam_instance_profile.teamserver description = "The instance profiles for the Teamserver instances." + value = aws_iam_instance_profile.teamserver } output "teamserver_instances" { - value = aws_instance.teamserver description = "The Teamserver instances." + value = aws_instance.teamserver } output "teamserver_security_group" { - value = aws_security_group.teamserver description = "The security group for the Teamserver instances." + value = aws_security_group.teamserver } output "terraformer_instances" { - value = aws_instance.terraformer description = "The Terraformer instances." + value = aws_instance.terraformer } output "terraformer_permissions_boundary_policy" { - value = aws_iam_policy.terraformer_permissions_boundary_policy description = "The permissions boundary policy for the Terraformer instances." + value = aws_iam_policy.terraformer_permissions_boundary_policy } output "terraformer_security_group" { - value = aws_security_group.terraformer description = "The security group for the Terraformer instances." + value = aws_security_group.terraformer } output "vpc" { - value = aws_vpc.assessment description = "The VPC for this assessment environment." + value = aws_vpc.assessment } output "vpn_server_cidr_block" { - value = local.vpn_server_cidr_block description = "The CIDR block for the COOL VPN." + value = local.vpn_server_cidr_block } output "windows_instance_profile" { - value = aws_iam_instance_profile.windows description = "The instance profile for the Windows instances." + value = aws_iam_instance_profile.windows } output "windows_instances" { - value = aws_instance.windows description = "The Windows instances." # We are putting a decrypted SSM value in the Terraform state (see # aws_ssm_parameter.vnc_public_ssh_key in locals.tf), so Terraform requires # us to mark this output as sensitive. However, since it's just the public # SSH key used by Guacamole, we are fine with this. sensitive = true + value = aws_instance.windows } output "windows_security_group" { - value = aws_security_group.windows description = "The security group for the Windows instances." + value = aws_security_group.windows } diff --git a/pentestportal_cloud_init.tf b/pentestportal_cloud_init.tf index 852d3e21..26e98506 100644 --- a/pentestportal_cloud_init.tf +++ b/pentestportal_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "pentestportal_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "pentestportal", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/pentestportal_ec2.tf b/pentestportal_ec2.tf index 6f24993d..7484e74b 100644 --- a/pentestportal_ec2.tf +++ b/pentestportal_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "docker" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The pentest portal EC2 instances @@ -39,7 +39,6 @@ resource "aws_instance" "pentestportal" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.pentestportal.name instance_type = "t3.medium" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -55,23 +54,24 @@ resource "aws_instance" "pentestportal" { volume_size = 128 volume_type = "gp3" } - user_data_base64 = data.cloudinit_config.pentestportal_cloud_init_tasks[count.index].rendered - vpc_security_group_ids = [ - aws_security_group.cloudwatch_agent_endpoint_client.id, - aws_security_group.efs_client.id, - aws_security_group.guacamole_accessible.id, - aws_security_group.pentestportal.id, - aws_security_group.ssm_agent_endpoint_client.id, - ] + subnet_id = aws_subnet.operations.id tags = { Name = format("PentestPortal%d", count.index + 1) } + user_data_base64 = data.cloudinit_config.pentestportal_cloud_init_tasks[count.index].rendered # volume_tags does not yet inherit the default tags from the # provider. See hashicorp/terraform-provider-aws#19188 for more # details. volume_tags = merge(data.aws_default_tags.assessment.tags, { Name = format("PentestPortal%d", count.index + 1) }) + vpc_security_group_ids = [ + aws_security_group.cloudwatch_agent_endpoint_client.id, + aws_security_group.efs_client.id, + aws_security_group.guacamole_accessible.id, + aws_security_group.pentestportal.id, + aws_security_group.ssm_agent_endpoint_client.id, + ] } # The Elastic IP for each pentest portal instance @@ -79,11 +79,11 @@ resource "aws_eip" "pentestportal" { count = lookup(var.operations_instance_counts, "pentestportal", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("PentestPortal%d EIP", count.index + 1) "Publish Egress" = "False" } + vpc = true } # The EIP association for each pentest portal instance @@ -91,8 +91,8 @@ resource "aws_eip_association" "pentestportal" { count = lookup(var.operations_instance_counts, "pentestportal", 0) provider = aws.provisionassessment - instance_id = aws_instance.pentestportal[count.index].id allocation_id = aws_eip.pentestportal[count.index].id + instance_id = aws_instance.pentestportal[count.index].id } # CloudWatch alarms for the pentest portal instances diff --git a/pentestportal_iam.tf b/pentestportal_iam.tf index 40ace685..00c59c85 100644 --- a/pentestportal_iam.tf +++ b/pentestportal_iam.tf @@ -12,24 +12,24 @@ resource "aws_iam_instance_profile" "pentestportal" { resource "aws_iam_role" "pentestportal_instance_role" { provider = aws.provisionassessment - name = "pentestportal_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "pentestportal_instance_role_${terraform.workspace}" } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_pentestportal" { provider = aws.provisionassessment - role = aws_iam_role.pentestportal_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.pentestportal_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_pentestportal" { provider = aws.provisionassessment - role = aws_iam_role.pentestportal_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.pentestportal_instance_role.id } # Attach a policy that allows the pentest portal instances to mount and @@ -37,6 +37,6 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_pentestpo resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_pentestportal" { provider = aws.provisionassessment - role = aws_iam_role.pentestportal_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.pentestportal_instance_role.id } diff --git a/pentestportal_route53.tf b/pentestportal_route53.tf index 69494787..11d67e9a 100644 --- a/pentestportal_route53.tf +++ b/pentestportal_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "pentestportal_A" { count = lookup(var.operations_instance_counts, "pentestportal", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "pentestportal${count.index + 1}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.pentestportal[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/pentestportal_sg.tf b/pentestportal_sg.tf index ff438694..3577a0e4 100644 --- a/pentestportal_sg.tf +++ b/pentestportal_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "pentestportal" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "PenTest Portal" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from Kali instances via ports 443 and 8080 @@ -17,12 +16,12 @@ resource "aws_security_group_rule" "pentestportal_ingress_from_kali_via_web" { for_each = toset(["443", "8080"]) provider = aws.provisionassessment - security_group_id = aws_security_group.pentestportal.id - type = "ingress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.pentestportal.id source_security_group_id = aws_security_group.kali.id - from_port = each.key to_port = each.key + type = "ingress" } # Allow ingress from Windows instances via ports 443 and 8080 @@ -33,12 +32,12 @@ resource "aws_security_group_rule" "pentestportal_ingress_from_windows_via_web" for_each = toset(["443", "8080"]) provider = aws.provisionassessment - security_group_id = aws_security_group.pentestportal.id - type = "ingress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.pentestportal.id source_security_group_id = aws_security_group.windows.id - from_port = each.key to_port = each.key + type = "ingress" } # Allow egress to anywhere via HTTP and HTTPS @@ -48,12 +47,12 @@ resource "aws_security_group_rule" "pentestportal_egress_to_anywhere_via_http_an for_each = toset(["80", "443"]) provider = aws.provisionassessment - security_group_id = aws_security_group.pentestportal.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = each.key + protocol = "tcp" + security_group_id = aws_security_group.pentestportal.id to_port = each.key + type = "egress" } # Allow ingress from anywhere via the allowed ports @@ -64,10 +63,10 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_pentestportal_via_a # acceptable form. for_each = { for d in var.inbound_ports_allowed["pentestportal"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.pentestportal.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.pentestportal.id to_port = each.value["to_port"] + type = "ingress" } diff --git a/private_acl_rules.tf b/private_acl_rules.tf index 7017de2b..7f3e6549 100644 --- a/private_acl_rules.tf +++ b/private_acl_rules.tf @@ -28,13 +28,13 @@ resource "aws_network_acl_rule" "private_ingress_from_cool_vpn_services" { format("%s_%s", pair[0], pair[1]) => merge(local.assessment_env_service_ports[pair[0]], { "private_subnet_cidr_block" = pair[1], "index" = index }) } - network_acl_id = aws_network_acl.private[each.value.private_subnet_cidr_block].id + cidr_block = local.vpn_server_cidr_block + from_port = each.value.port egress = false + network_acl_id = aws_network_acl.private[each.value.private_subnet_cidr_block].id protocol = each.value.protocol - rule_number = 100 + each.value.index rule_action = "allow" - cidr_block = local.vpn_server_cidr_block - from_port = each.value.port + rule_number = 100 + each.value.index to_port = each.value.port } # Allow ingress from operations subnet via port 8065. @@ -45,13 +45,13 @@ resource "aws_network_acl_rule" "private_ingress_from_operations_mattermost_web" provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = false + from_port = 8065 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 120 rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 8065 + rule_number = 120 to_port = 8065 } # Allow ingress to first private subnet (where Transit Gateway attachment @@ -64,13 +64,13 @@ resource "aws_network_acl_rule" "private_ingress_to_tg_attachment_via_udp_epheme provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id + cidr_block = each.value egress = false + from_port = 1024 + network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id protocol = "udp" - rule_number = 125 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = each.value - from_port = 1024 + rule_number = 125 + index(var.private_subnet_cidr_blocks, each.value) to_port = 65535 } # Disallow ingress from anywhere else via ports used by services @@ -98,13 +98,13 @@ resource "aws_network_acl_rule" "private_ingress_from_anywhere_else_services" { format("%s_%s", pair[0], pair[1]) => merge(local.assessment_env_service_ports_gte_1024[pair[0]], { "private_subnet_cidr_block" = pair[1], "index" = index }) } - network_acl_id = aws_network_acl.private[each.value.private_subnet_cidr_block].id + cidr_block = "0.0.0.0/0" egress = false + from_port = each.value.port + network_acl_id = aws_network_acl.private[each.value.private_subnet_cidr_block].id protocol = each.value.protocol - rule_number = 130 + each.value.index rule_action = "deny" - cidr_block = "0.0.0.0/0" - from_port = each.value.port + rule_number = 130 + each.value.index to_port = each.value.port } @@ -118,13 +118,13 @@ resource "aws_network_acl_rule" "private_ingress_from_operations_efs" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block + from_port = 2049 egress = false + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 150 rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 2049 + rule_number = 150 to_port = 2049 } # Allow ingress from operations subnet via port 445. @@ -137,13 +137,13 @@ resource "aws_network_acl_rule" "private_ingress_from_operations_smb" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = false + from_port = 445 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 155 rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 445 + rule_number = 155 to_port = 445 } # Disallow ingress from anywhere else via port 2049. @@ -151,13 +151,13 @@ resource "aws_network_acl_rule" "private_ingress_from_anywhere_else_efs" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "0.0.0.0/0" egress = false + from_port = 2049 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 160 rule_action = "deny" - cidr_block = "0.0.0.0/0" - from_port = 2049 + rule_number = 160 to_port = 2049 } @@ -171,13 +171,13 @@ resource "aws_network_acl_rule" "private_ingress_from_anywhere_via_tcp_ephemeral provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "0.0.0.0/0" egress = false + from_port = 1024 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 170 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 1024 + rule_number = 170 + index(var.private_subnet_cidr_blocks, each.value) to_port = 65535 } @@ -206,13 +206,13 @@ resource "aws_network_acl_rule" "private_ingress_to_tg_attachment_via_ipa_ports" format("%s_%s", pair[0], pair[1]) => merge(local.ipa_ports[pair[0]], { "private_subnet_cidr_block" = pair[1], "index" = index }) } - network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id + cidr_block = each.value.private_subnet_cidr_block egress = false + from_port = each.value.port + network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id protocol = each.value.protocol - rule_number = 180 + each.value.index rule_action = "allow" - cidr_block = each.value.private_subnet_cidr_block - from_port = each.value.port + rule_number = 180 + each.value.index to_port = each.value.port } @@ -223,13 +223,13 @@ resource "aws_network_acl_rule" "private_ingress_from_operations_via_https" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = false + from_port = 443 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 200 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 443 + rule_number = 200 + index(var.private_subnet_cidr_blocks, each.value) to_port = 443 } @@ -244,12 +244,12 @@ resource "aws_network_acl_rule" "private_ingress_from_local_vm_ips_via_all_ports provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "172.16.0.0/12" egress = false + network_acl_id = aws_network_acl.private[each.value].id protocol = "all" - rule_number = 210 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "172.16.0.0/12" + rule_number = 210 + index(var.private_subnet_cidr_blocks, each.value) } ##### @@ -264,13 +264,13 @@ resource "aws_network_acl_rule" "private_egress_to_anywhere_via_ssh" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "0.0.0.0/0" egress = true + from_port = 22 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 300 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 22 + rule_number = 300 + index(var.private_subnet_cidr_blocks, each.value) to_port = 22 } @@ -283,13 +283,13 @@ resource "aws_network_acl_rule" "private_egress_to_operations_via_winrm" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = true + from_port = 5986 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 310 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 5986 + rule_number = 310 + index(var.private_subnet_cidr_blocks, each.value) to_port = 5986 } @@ -300,13 +300,13 @@ resource "aws_network_acl_rule" "private_egress_to_anywhere_via_http" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "0.0.0.0/0" egress = true + from_port = 80 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 320 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 80 + rule_number = 320 + index(var.private_subnet_cidr_blocks, each.value) to_port = 80 } @@ -323,13 +323,13 @@ resource "aws_network_acl_rule" "private_egress_to_anywhere_via_https" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "0.0.0.0/0" egress = true + from_port = 443 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 330 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "0.0.0.0/0" - from_port = 443 + rule_number = 330 + index(var.private_subnet_cidr_blocks, each.value) to_port = 443 } @@ -340,13 +340,13 @@ resource "aws_network_acl_rule" "private_egress_to_cool_via_tcp_ephemeral_ports" provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = local.cool_shared_services_cidr_block egress = true + from_port = 1024 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 340 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = local.cool_shared_services_cidr_block - from_port = 1024 + rule_number = 340 + index(var.private_subnet_cidr_blocks, each.value) to_port = 65535 } @@ -357,13 +357,13 @@ resource "aws_network_acl_rule" "private_egress_to_cool_via_udp_ephemeral_ports" provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = local.cool_shared_services_cidr_block egress = true + from_port = 1024 + network_acl_id = aws_network_acl.private[each.value].id protocol = "udp" - rule_number = 350 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = local.cool_shared_services_cidr_block - from_port = 1024 + rule_number = 350 + index(var.private_subnet_cidr_blocks, each.value) to_port = 65535 } @@ -373,13 +373,13 @@ resource "aws_network_acl_rule" "private_egress_to_operations_via_ephemeral_port provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = true + from_port = 1024 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 360 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 1024 + rule_number = 360 + index(var.private_subnet_cidr_blocks, each.value) to_port = 65535 } @@ -390,13 +390,13 @@ resource "aws_network_acl_rule" "private_egress_to_operations_via_ssh" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = true + from_port = 22 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 370 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 22 + rule_number = 370 + index(var.private_subnet_cidr_blocks, each.value) to_port = 22 } @@ -406,13 +406,13 @@ resource "aws_network_acl_rule" "private_egress_to_operations_via_vnc" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = aws_subnet.operations.cidr_block egress = true + from_port = 5901 + network_acl_id = aws_network_acl.private[each.value].id protocol = "tcp" - rule_number = 380 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = aws_subnet.operations.cidr_block - from_port = 5901 + rule_number = 380 + index(var.private_subnet_cidr_blocks, each.value) to_port = 5901 } @@ -426,13 +426,13 @@ resource "aws_network_acl_rule" "private_egress_to_cool_via_ipa_ports" { provider = aws.provisionassessment for_each = local.ipa_ports - network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id + cidr_block = local.cool_shared_services_cidr_block egress = true + from_port = each.value.port + network_acl_id = aws_network_acl.private[var.private_subnet_cidr_blocks[0]].id protocol = each.value.protocol - rule_number = 400 + each.value.index rule_action = "allow" - cidr_block = local.cool_shared_services_cidr_block - from_port = each.value.port + rule_number = 400 + each.value.index to_port = each.value.port } @@ -447,10 +447,10 @@ resource "aws_network_acl_rule" "private_egress_to_local_vm_ips_via_all_ports" { provider = aws.provisionassessment for_each = toset(var.private_subnet_cidr_blocks) - network_acl_id = aws_network_acl.private[each.value].id + cidr_block = "172.16.0.0/12" egress = true + network_acl_id = aws_network_acl.private[each.value].id protocol = "all" - rule_number = 410 + index(var.private_subnet_cidr_blocks, each.value) rule_action = "allow" - cidr_block = "172.16.0.0/12" + rule_number = 410 + index(var.private_subnet_cidr_blocks, each.value) } diff --git a/private_routing.tf b/private_routing.tf index 0d780705..2718caab 100644 --- a/private_routing.tf +++ b/private_routing.tf @@ -30,8 +30,8 @@ resource "aws_route_table" "private_route_table" { resource "aws_route" "cool_private" { provider = aws.provisionassessment - route_table_id = aws_route_table.private_route_table.id destination_cidr_block = local.cool_shared_services_cidr_block + route_table_id = aws_route_table.private_route_table.id transit_gateway_id = local.transit_gateway_id } @@ -48,8 +48,8 @@ resource "aws_vpc_endpoint_route_table_association" "s3_private" { resource "aws_route" "external_private" { provider = aws.provisionassessment - route_table_id = aws_route_table.private_route_table.id destination_cidr_block = "0.0.0.0/0" + route_table_id = aws_route_table.private_route_table.id nat_gateway_id = aws_nat_gateway.nat_gw.id } diff --git a/remote_states.tf b/remote_states.tf index d6bff0cd..54111054 100644 --- a/remote_states.tf +++ b/remote_states.tf @@ -8,12 +8,12 @@ data "terraform_remote_state" "dns_certboto" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-dns-certboto/terraform.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-dns-certboto/terraform.tfstate" } workspace = "production" @@ -23,12 +23,12 @@ data "terraform_remote_state" "dynamic_assessment" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-accounts/dynamic.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-accounts/dynamic.tfstate" } # Note that this workspace is different from all the others. For @@ -42,12 +42,12 @@ data "terraform_remote_state" "images" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-accounts/images.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-accounts/images.tfstate" } workspace = local.workspace_type @@ -57,12 +57,12 @@ data "terraform_remote_state" "images_parameterstore" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-images-parameterstore/terraform.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-images-parameterstore/terraform.tfstate" } workspace = local.workspace_type @@ -72,12 +72,12 @@ data "terraform_remote_state" "master" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-accounts/master.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-accounts/master.tfstate" } workspace = "production" @@ -87,12 +87,12 @@ data "terraform_remote_state" "sharedservices" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-accounts/shared_services.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-accounts/shared_services.tfstate" } workspace = local.workspace_type @@ -102,12 +102,12 @@ data "terraform_remote_state" "sharedservices_networking" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-sharedservices-networking/terraform.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-sharedservices-networking/terraform.tfstate" } workspace = local.workspace_type @@ -117,12 +117,12 @@ data "terraform_remote_state" "terraform" { backend = "s3" config = { - encrypt = true bucket = "cisa-cool-terraform-state" dynamodb_table = "terraform-state-lock" + encrypt = true + key = "cool-accounts/terraform.tfstate" profile = "cool-terraform-backend" region = "us-east-1" - key = "cool-accounts/terraform.tfstate" } workspace = "production" diff --git a/route53.tf b/route53.tf index aa9cf1ee..5f733157 100644 --- a/route53.tf +++ b/route53.tf @@ -2,16 +2,15 @@ resource "aws_route53_zone" "assessment_private" { provider = aws.provisionassessment - name = "${local.private_domain}." + comment = "Terraform Workspace: ${terraform.workspace}" + name = "${local.private_domain}." + tags = { + Name = format("%s Private Zone", local.private_domain) + } vpc { vpc_id = aws_vpc.assessment.id } - - tags = { - Name = format("%s Private Zone", local.private_domain) - } - comment = "Terraform Workspace: ${terraform.workspace}" } diff --git a/s3_endpoint_client_sg.tf b/s3_endpoint_client_sg.tf index 0adf2b6c..29691859 100644 --- a/s3_endpoint_client_sg.tf +++ b/s3_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "s3_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "S3 endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the S3 gateway endpoint. resource "aws_security_group_rule" "egress_to_s3_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.s3_endpoint_client.id - type = "egress" - protocol = "tcp" - prefix_list_ids = [aws_vpc_endpoint.s3.prefix_list_id] from_port = 443 + prefix_list_ids = [aws_vpc_endpoint.s3.prefix_list_id] + protocol = "tcp" + security_group_id = aws_security_group.s3_endpoint_client.id to_port = 443 + type = "egress" } diff --git a/samba_cloud_init.tf b/samba_cloud_init.tf index a7f056f8..931026d5 100644 --- a/samba_cloud_init.tf +++ b/samba_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "samba_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "samba", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/samba_ec2.tf b/samba_ec2.tf index 757fec43..3bc46ad5 100644 --- a/samba_ec2.tf +++ b/samba_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "samba" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Samba EC2 instances @@ -38,15 +38,6 @@ resource "aws_instance" "samba" { ami = data.aws_ami.samba.id iam_instance_profile = aws_iam_instance_profile.samba.name instance_type = "t3.small" - # TODO: For some reason I can't ssh via SSM to the instance unless I - # put it in the first private subnet. I believe this has something - # to do with the NACLs that are in place for that subnet - # specifically. I will figure this out later. I'd definitely - # prefer to put this instance in a different subnet than the - # Guacamole instance. - # - # See cisagov/cool-assessment-terraform#135 for more details. - subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -62,22 +53,31 @@ resource "aws_instance" "samba" { volume_size = 16 volume_type = "gp3" } - user_data_base64 = data.cloudinit_config.samba_cloud_init_tasks[count.index].rendered - vpc_security_group_ids = [ - aws_security_group.cloudwatch_agent_endpoint_client.id, - aws_security_group.efs_client.id, - aws_security_group.smb_server.id, - aws_security_group.ssm_agent_endpoint_client.id, - ] + # TODO: For some reason I can't ssh via SSM to the instance unless I + # put it in the first private subnet. I believe this has something + # to do with the NACLs that are in place for that subnet + # specifically. I will figure this out later. I'd definitely + # prefer to put this instance in a different subnet than the + # Guacamole instance. + # + # See cisagov/cool-assessment-terraform#135 for more details. + subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id tags = { Name = format("Samba%d", count.index) } + user_data_base64 = data.cloudinit_config.samba_cloud_init_tasks[count.index].rendered # volume_tags does not yet inherit the default tags from the # provider. See hashicorp/terraform-provider-aws#19188 for more # details. volume_tags = merge(data.aws_default_tags.assessment.tags, { Name = format("Samba%d", count.index) }) + vpc_security_group_ids = [ + aws_security_group.cloudwatch_agent_endpoint_client.id, + aws_security_group.efs_client.id, + aws_security_group.smb_server.id, + aws_security_group.ssm_agent_endpoint_client.id, + ] } # CloudWatch alarms for the Samba instances diff --git a/samba_iam.tf b/samba_iam.tf index 3ededaf8..8bc5beb9 100644 --- a/samba_iam.tf +++ b/samba_iam.tf @@ -12,24 +12,24 @@ resource "aws_iam_instance_profile" "samba" { resource "aws_iam_role" "samba_instance_role" { provider = aws.provisionassessment - name = "samba_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "samba_instance_role_${terraform.workspace}" } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_samba" { provider = aws.provisionassessment - role = aws_iam_role.samba_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.samba_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_samba" { provider = aws.provisionassessment - role = aws_iam_role.samba_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.samba_instance_role.id } # Attach a policy that allows the Samba instances to mount and write @@ -37,6 +37,6 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_samba" { resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_samba" { provider = aws.provisionassessment - role = aws_iam_role.samba_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.samba_instance_role.id } diff --git a/samba_route53.tf b/samba_route53.tf index cf9a0717..74c660ee 100644 --- a/samba_route53.tf +++ b/samba_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "samba_A" { count = lookup(var.operations_instance_counts, "samba", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "samba${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.samba[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/scanner_sg.tf b/scanner_sg.tf index 55af1efc..f0f00927 100644 --- a/scanner_sg.tf +++ b/scanner_sg.tf @@ -4,11 +4,10 @@ resource "aws_security_group" "scanner" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Scanner" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from anywhere via ICMP @@ -17,12 +16,12 @@ resource "aws_security_group" "scanner" { resource "aws_security_group_rule" "scanner_ingress_from_anywhere_via_icmp" { provider = aws.provisionassessment - security_group_id = aws_security_group.scanner.id - type = "ingress" - protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] from_port = 8 + protocol = "icmp" + security_group_id = aws_security_group.scanner.id to_port = 0 + type = "ingress" } # Allow egress to anywhere via any protocol and port @@ -31,10 +30,10 @@ resource "aws_security_group_rule" "scanner_ingress_from_anywhere_via_icmp" { resource "aws_security_group_rule" "scanner_egress_to_anywhere_via_any_port" { provider = aws.provisionassessment - security_group_id = aws_security_group.scanner.id - type = "egress" - protocol = -1 cidr_blocks = ["0.0.0.0/0"] from_port = -1 + protocol = -1 + security_group_id = aws_security_group.scanner.id to_port = -1 + type = "egress" } diff --git a/smb_client_sg.tf b/smb_client_sg.tf index 4eef0a39..c3ab7ee9 100644 --- a/smb_client_sg.tf +++ b/smb_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "smb_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SMB client" } + vpc_id = aws_vpc.assessment.id } # Allow egress to SMB server instances via port 445 (SMB) resource "aws_security_group_rule" "smb_client_egress_to_smb_server" { provider = aws.provisionassessment - security_group_id = aws_security_group.smb_client.id - type = "egress" + from_port = 445 protocol = "tcp" + security_group_id = aws_security_group.smb_client.id source_security_group_id = aws_security_group.smb_server.id - from_port = 445 to_port = 445 + type = "egress" } diff --git a/smb_server_sg.tf b/smb_server_sg.tf index d521f493..805ffefd 100644 --- a/smb_server_sg.tf +++ b/smb_server_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "smb_server" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SMB server" } + vpc_id = aws_vpc.assessment.id } # Allow ingress from SMB client instances via port 445 (SMB) resource "aws_security_group_rule" "smb_server_ingress_from_smb_client" { provider = aws.provisionassessment - security_group_id = aws_security_group.smb_server.id - type = "ingress" + from_port = 445 protocol = "tcp" + security_group_id = aws_security_group.smb_server.id source_security_group_id = aws_security_group.smb_client.id - from_port = 445 to_port = 445 + type = "ingress" } diff --git a/ssm_agent_endpoint_client_sg.tf b/ssm_agent_endpoint_client_sg.tf index fc838ba7..03fd19ff 100644 --- a/ssm_agent_endpoint_client_sg.tf +++ b/ssm_agent_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "ssm_agent_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SSM agent endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the SSM agent endpoint security group. resource "aws_security_group_rule" "egress_from_ssm_agent_endpoint_client_to_ssm_agent_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ssm_agent_endpoint_client.id - type = "egress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ssm_agent_endpoint_client.id source_security_group_id = aws_security_group.ssm_agent_endpoint.id - from_port = 443 to_port = 443 + type = "egress" } diff --git a/ssm_agent_endpoint_sg.tf b/ssm_agent_endpoint_sg.tf index f70d9fc2..3b0ddd08 100644 --- a/ssm_agent_endpoint_sg.tf +++ b/ssm_agent_endpoint_sg.tf @@ -3,21 +3,20 @@ resource "aws_security_group" "ssm_agent_endpoint" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SSM agent endpoints" } + vpc_id = aws_vpc.assessment.id } # Allow ingress via HTTPS from the SSM endpoint agent client security group. resource "aws_security_group_rule" "ingress_from_ssm_agent_endpoint_client_to_ssm_agent_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ssm_agent_endpoint.id - type = "ingress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ssm_agent_endpoint.id source_security_group_id = aws_security_group.ssm_agent_endpoint_client.id - from_port = 443 to_port = 443 + type = "ingress" } diff --git a/ssm_endpoint_client_sg.tf b/ssm_endpoint_client_sg.tf index 23a1209a..d454b484 100644 --- a/ssm_endpoint_client_sg.tf +++ b/ssm_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "ssm_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SSM endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS to the SSM endpoint security group. resource "aws_security_group_rule" "egress_from_ssm_endpoint_client_to_ssm_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ssm_endpoint_client.id - type = "egress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ssm_endpoint_client.id source_security_group_id = aws_security_group.ssm_endpoint.id - from_port = 443 to_port = 443 + type = "egress" } diff --git a/ssm_endpoint_sg.tf b/ssm_endpoint_sg.tf index 48e60ccc..4dbec165 100644 --- a/ssm_endpoint_sg.tf +++ b/ssm_endpoint_sg.tf @@ -3,21 +3,20 @@ resource "aws_security_group" "ssm_endpoint" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "SSM endpoints" } + vpc_id = aws_vpc.assessment.id } # Allow ingress via HTTPS from the SSM endpoint client security group. resource "aws_security_group_rule" "ingress_from_ssm_endpoint_client_to_ssm_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.ssm_endpoint.id - type = "ingress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.ssm_endpoint.id source_security_group_id = aws_security_group.ssm_endpoint_client.id - from_port = 443 to_port = 443 + type = "ingress" } diff --git a/sts_endpoint_client_sg.tf b/sts_endpoint_client_sg.tf index 84c7df2b..fae47533 100644 --- a/sts_endpoint_client_sg.tf +++ b/sts_endpoint_client_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "sts_endpoint_client" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "STS endpoint client" } + vpc_id = aws_vpc.assessment.id } # Allow egress via HTTPS from the STS endpoint security group. resource "aws_security_group_rule" "egress_from_sts_endpoint_client_to_sts_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.sts_endpoint_client.id - type = "egress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.sts_endpoint_client.id source_security_group_id = aws_security_group.sts_endpoint.id - from_port = 443 to_port = 443 + type = "egress" } diff --git a/sts_endpoint_sg.tf b/sts_endpoint_sg.tf index 718d2daf..bff66069 100644 --- a/sts_endpoint_sg.tf +++ b/sts_endpoint_sg.tf @@ -2,21 +2,20 @@ resource "aws_security_group" "sts_endpoint" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "STS endpoint" } + vpc_id = aws_vpc.assessment.id } # Allow ingress via HTTPS from the STS endpoint client security group. resource "aws_security_group_rule" "ingress_from_sts_endpoint_client_to_sts_endpoint_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.sts_endpoint.id - type = "ingress" + from_port = 443 protocol = "tcp" + security_group_id = aws_security_group.sts_endpoint.id source_security_group_id = aws_security_group.sts_endpoint_client.id - from_port = 443 to_port = 443 + type = "ingress" } diff --git a/subnets.tf b/subnets.tf index a17c3e9d..d6b0a2b9 100644 --- a/subnets.tf +++ b/subnets.tf @@ -4,17 +4,15 @@ # Operations subnet of the VPC resource "aws_subnet" "operations" { - provider = aws.provisionassessment - - vpc_id = aws_vpc.assessment.id - cidr_block = var.operations_subnet_cidr_block - availability_zone = "${var.aws_region}${var.aws_availability_zone}" - + provider = aws.provisionassessment depends_on = [aws_internet_gateway.assessment] + availability_zone = "${var.aws_region}${var.aws_availability_zone}" + cidr_block = var.operations_subnet_cidr_block tags = { Name = "Assessment Operations" } + vpc_id = aws_vpc.assessment.id } # Private subnets of the VPC @@ -23,13 +21,12 @@ resource "aws_subnet" "private" { for_each = toset(var.private_subnet_cidr_blocks) - vpc_id = aws_vpc.assessment.id - cidr_block = each.key availability_zone = "${var.aws_region}${var.aws_availability_zone}" - + cidr_block = each.key tags = { Name = format("Assessment Private - %s", each.key) } + vpc_id = aws_vpc.assessment.id } # The internet gateway for the VPC diff --git a/teamserver_cloud_init.tf b/teamserver_cloud_init.tf index bc274769..08eb1d73 100644 --- a/teamserver_cloud_init.tf +++ b/teamserver_cloud_init.tf @@ -10,8 +10,8 @@ locals { data "cloudinit_config" "teamserver_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "teamserver", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/teamserver_ec2.tf b/teamserver_ec2.tf index e8420c6d..4cbcb359 100644 --- a/teamserver_ec2.tf +++ b/teamserver_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "teamserver" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The teamserver EC2 instances @@ -50,7 +50,6 @@ resource "aws_instance" "teamserver" { associate_public_ip_address = true iam_instance_profile = aws_iam_instance_profile.teamserver[count.index].name instance_type = "t3.large" - subnet_id = aws_subnet.operations.id root_block_device { volume_size = 128 volume_type = "gp3" @@ -66,7 +65,17 @@ resource "aws_instance" "teamserver" { # Require IMDS tokens AKA require the use of IMDSv2 http_tokens = "required" } + subnet_id = aws_subnet.operations.id + tags = { + Name = format("Teamserver%d", count.index) + } user_data_base64 = data.cloudinit_config.teamserver_cloud_init_tasks[count.index].rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Teamserver%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.efs_client.id, @@ -77,15 +86,6 @@ resource "aws_instance" "teamserver" { aws_security_group.sts_endpoint_client.id, aws_security_group.teamserver.id, ] - tags = { - Name = format("Teamserver%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Teamserver%d", count.index) - }) } # The Elastic IP for each teamserver @@ -93,11 +93,11 @@ resource "aws_eip" "teamserver" { count = lookup(var.operations_instance_counts, "teamserver", 0) provider = aws.provisionassessment - vpc = true tags = { Name = format("Teamserver%d EIP", count.index) "Publish Egress" = "True" } + vpc = true } # The EIP association for each teamserver @@ -105,8 +105,8 @@ resource "aws_eip_association" "teamserver" { count = lookup(var.operations_instance_counts, "teamserver", 0) provider = aws.provisionassessment - instance_id = aws_instance.teamserver[count.index].id allocation_id = aws_eip.teamserver[count.index].id + instance_id = aws_instance.teamserver[count.index].id } # CloudWatch alarms for the teamserver instances diff --git a/teamserver_iam.tf b/teamserver_iam.tf index 2ca99c01..cca74eb1 100644 --- a/teamserver_iam.tf +++ b/teamserver_iam.tf @@ -17,8 +17,8 @@ resource "aws_iam_role" "teamserver_instance_role" { provider = aws.provisionassessment - name = "teamserver${count.index}_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "teamserver${count.index}_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "teamserver_assume_delegated_role_policy" { @@ -27,8 +27,8 @@ resource "aws_iam_role_policy" "teamserver_assume_delegated_role_policy" { provider = aws.provisionassessment name = "assume_delegated_role_policy" - role = aws_iam_role.teamserver_instance_role[count.index].id policy = data.aws_iam_policy_document.teamserver_assume_delegated_role_policy_doc[count.index].json + role = aws_iam_role.teamserver_instance_role[count.index].id } # Attach the CloudWatch Agent policy to these roles as well @@ -37,8 +37,8 @@ resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_te provider = aws.provisionassessment - role = aws_iam_role.teamserver_instance_role[count.index].id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.teamserver_instance_role[count.index].id } # Attach the SSM Agent policy to these roles as well @@ -47,8 +47,8 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_teamserve provider = aws.provisionassessment - role = aws_iam_role.teamserver_instance_role[count.index].id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.teamserver_instance_role[count.index].id } # Attach a policy that allows the Teamserver instances to mount and write to @@ -58,8 +58,8 @@ resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_teamserve provider = aws.provisionassessment - role = aws_iam_role.teamserver_instance_role[count.index].id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.teamserver_instance_role[count.index].id } ################################ diff --git a/teamserver_route53.tf b/teamserver_route53.tf index 67d05206..d7655962 100644 --- a/teamserver_route53.tf +++ b/teamserver_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "teamserver_A" { count = lookup(var.operations_instance_counts, "teamserver", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "teamserver${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.teamserver[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/teamserver_sg.tf b/teamserver_sg.tf index 59bfac97..8ff685b5 100644 --- a/teamserver_sg.tf +++ b/teamserver_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "teamserver" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Teamserver" } + vpc_id = aws_vpc.assessment.id } # Allow egress via port 587 (SMTP mail submission) to Gophish instances @@ -14,12 +13,12 @@ resource "aws_security_group" "teamserver" { resource "aws_security_group_rule" "teamserver_egress_to_gophish_via_587" { provider = aws.provisionassessment - security_group_id = aws_security_group.teamserver.id - type = "egress" + from_port = 587 protocol = "tcp" + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.gophish.id - from_port = 587 to_port = 587 + type = "egress" } # Allow ingress from Kali instances via ports 993 and 50050 (IMAP over @@ -28,12 +27,12 @@ resource "aws_security_group_rule" "teamserver_ingress_from_kali_via_imaps_and_c for_each = toset(["993", "50050"]) provider = aws.provisionassessment - security_group_id = aws_security_group.teamserver.id - type = "ingress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.kali.id - from_port = each.key to_port = each.key + type = "ingress" } # Allow ingress from anywhere via the allowed ports @@ -44,12 +43,12 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_teamserver_via_allo # acceptable form. for_each = { for d in var.inbound_ports_allowed["teamserver"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.teamserver.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.teamserver.id to_port = each.value["to_port"] + type = "ingress" } # Allow access between Teamserver and Kali instances on ports @@ -59,23 +58,23 @@ resource "aws_security_group_rule" "teamserver_egress_to_kali_instances_via_5000 provider = aws.provisionassessment for_each = toset(["tcp", "udp"]) - security_group_id = aws_security_group.teamserver.id - type = "egress" + from_port = 5000 protocol = each.key + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.kali.id - from_port = 5000 to_port = 5999 + type = "egress" } resource "aws_security_group_rule" "teamserver_ingress_from_kali_instances_via_5000_to_5999" { provider = aws.provisionassessment for_each = toset(["tcp", "udp"]) - security_group_id = aws_security_group.teamserver.id - type = "ingress" + from_port = 5000 protocol = each.key + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.kali.id - from_port = 5000 to_port = 5999 + type = "ingress" } # Allow access between Teamserver and Windows instances on ports @@ -85,20 +84,20 @@ resource "aws_security_group_rule" "teamserver_ingress_from_kali_instances_via_5 resource "aws_security_group_rule" "teamserver_egress_to_windows_instances_via_5000_to_5999_tcp" { provider = aws.provisionassessment - security_group_id = aws_security_group.teamserver.id - type = "egress" + from_port = 5000 protocol = "tcp" + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.windows.id - from_port = 5000 to_port = 5999 + type = "egress" } resource "aws_security_group_rule" "teamserver_ingress_from_windows_instances_via_5000_to_5999_tcp" { provider = aws.provisionassessment - security_group_id = aws_security_group.teamserver.id - type = "ingress" + from_port = 5000 protocol = "tcp" + security_group_id = aws_security_group.teamserver.id source_security_group_id = aws_security_group.windows.id - from_port = 5000 to_port = 5999 + type = "ingress" } diff --git a/terraformer_cloud_init.tf b/terraformer_cloud_init.tf index bcc48765..197a388f 100644 --- a/terraformer_cloud_init.tf +++ b/terraformer_cloud_init.tf @@ -3,8 +3,8 @@ data "cloudinit_config" "terraformer_cloud_init_tasks" { count = lookup(var.operations_instance_counts, "terraformer", 0) - gzip = true base64_encode = true + gzip = true # Note: The filename parameters in each part below are only used to # name the mime-parts of the user-data. They do not affect the diff --git a/terraformer_ec2.tf b/terraformer_ec2.tf index 4ef2fbcd..9f83aee1 100644 --- a/terraformer_ec2.tf +++ b/terraformer_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "terraformer" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Terraformer EC2 instances @@ -38,15 +38,6 @@ resource "aws_instance" "terraformer" { ami = data.aws_ami.terraformer.id iam_instance_profile = aws_iam_instance_profile.terraformer.name instance_type = "t3.xlarge" - # TODO: For some reason I can't ssh via SSM to the instance unless I - # put it in the first private subnet. I believe this has something - # to do with the NACLs that are in place for that subnet - # specifically. I will figure this out later. I'd definitely - # prefer to put this instance in a different subnet than the - # Guacamole instance. - # - # See cisagov/cool-assessment-terraform#135 for more details. - subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -62,7 +53,25 @@ resource "aws_instance" "terraformer" { volume_size = 128 volume_type = "gp3" } + # TODO: For some reason I can't ssh via SSM to the instance unless I + # put it in the first private subnet. I believe this has something + # to do with the NACLs that are in place for that subnet + # specifically. I will figure this out later. I'd definitely + # prefer to put this instance in a different subnet than the + # Guacamole instance. + # + # See cisagov/cool-assessment-terraform#135 for more details. + subnet_id = aws_subnet.private[var.private_subnet_cidr_blocks[0]].id + tags = { + Name = format("Terraformer%d", count.index) + } user_data_base64 = data.cloudinit_config.terraformer_cloud_init_tasks[count.index].rendered + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Terraformer%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.dynamodb_endpoint_client.id, @@ -73,15 +82,6 @@ resource "aws_instance" "terraformer" { aws_security_group.sts_endpoint_client.id, aws_security_group.terraformer.id, ] - tags = { - Name = format("Terraformer%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Terraformer%d", count.index) - }) } # CloudWatch alarms for the Terraformer instances diff --git a/terraformer_iam.tf b/terraformer_iam.tf index 1fe45c8a..3ec6ec6e 100644 --- a/terraformer_iam.tf +++ b/terraformer_iam.tf @@ -13,32 +13,32 @@ resource "aws_iam_instance_profile" "terraformer" { resource "aws_iam_role" "terraformer_instance_role" { provider = aws.provisionassessment - name = "terraformer_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "terraformer_instance_role_${terraform.workspace}" } resource "aws_iam_role_policy" "terraformer_assume_delegated_role_policy" { provider = aws.provisionassessment name = "terraformer_assume_delegated_role_policy" - role = aws_iam_role.terraformer_instance_role.id policy = data.aws_iam_policy_document.terraformer_assume_delegated_role_policy_doc.json + role = aws_iam_role.terraformer_instance_role.id } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_terraformer" { provider = aws.provisionassessment - role = aws_iam_role.terraformer_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.terraformer_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_terraformer" { provider = aws.provisionassessment - role = aws_iam_role.terraformer_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.terraformer_instance_role.id } # Attach a policy that allows the Terraformer instances to mount and @@ -46,8 +46,8 @@ resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_terraform resource "aws_iam_role_policy_attachment" "efs_mount_policy_attachment_terraformer" { provider = aws.provisionassessment - role = aws_iam_role.terraformer_instance_role.id policy_arn = aws_iam_policy.efs_mount_policy.arn + role = aws_iam_role.terraformer_instance_role.id } ################################ diff --git a/terraformer_policy.tf b/terraformer_policy.tf index f9aa69a1..77dd9ccb 100644 --- a/terraformer_policy.tf +++ b/terraformer_policy.tf @@ -52,8 +52,8 @@ data "aws_iam_policy_document" "terraformer_policy_doc" { # with the "Team" tag. statement { actions = [ - "ec2:RunInstances", "ec2:ModifyNetworkInterfaceAttribute", + "ec2:RunInstances", ] resources = [ # Subnets. The ModifyNetworkInterfaceAttribute doesn't care diff --git a/terraformer_route53.tf b/terraformer_route53.tf index d8c77432..289e58bd 100644 --- a/terraformer_route53.tf +++ b/terraformer_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "terraformer_A" { count = lookup(var.operations_instance_counts, "terraformer", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "terraformer${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.terraformer[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/terraformer_sg.tf b/terraformer_sg.tf index aa07350f..7c426d74 100644 --- a/terraformer_sg.tf +++ b/terraformer_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "terraformer" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Terraformer" } + vpc_id = aws_vpc.assessment.id } # Allow egress anywhere via ssh @@ -16,12 +15,12 @@ resource "aws_security_group" "terraformer" { resource "aws_security_group_rule" "terraformer_egress_anywhere_via_ssh" { provider = aws.provisionassessment - security_group_id = aws_security_group.terraformer.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = 22 + protocol = "tcp" + security_group_id = aws_security_group.terraformer.id to_port = 22 + type = "egress" } # Allow egress anywhere via HTTP @@ -30,12 +29,12 @@ resource "aws_security_group_rule" "terraformer_egress_anywhere_via_ssh" { resource "aws_security_group_rule" "terraformer_egress_anywhere_via_http" { provider = aws.provisionassessment - security_group_id = aws_security_group.terraformer.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.terraformer.id to_port = 80 + type = "egress" } # Allow egress anywhere via HTTPS @@ -44,12 +43,12 @@ resource "aws_security_group_rule" "terraformer_egress_anywhere_via_http" { resource "aws_security_group_rule" "terraformer_egress_anywhere_via_https" { provider = aws.provisionassessment - security_group_id = aws_security_group.terraformer.id - type = "egress" - protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.terraformer.id to_port = 443 + type = "egress" } # Allow egress anywhere via port 5986 (Windows Remote Manager). @@ -59,10 +58,10 @@ resource "aws_security_group_rule" "terraformer_egress_anywhere_via_https" { resource "aws_security_group_rule" "terraformer_egress_to_operations_via_winrm" { provider = aws.provisionassessment - security_group_id = aws_security_group.terraformer.id - type = "egress" - protocol = "tcp" cidr_blocks = [aws_subnet.operations.cidr_block] from_port = 5986 + protocol = "tcp" + security_group_id = aws_security_group.terraformer.id to_port = 5986 + type = "egress" } diff --git a/users_account_assume_role_policy.tf b/users_account_assume_role_policy.tf index 04eaddfe..f91ead20 100644 --- a/users_account_assume_role_policy.tf +++ b/users_account_assume_role_policy.tf @@ -11,10 +11,10 @@ data "aws_iam_policy_document" "users_account_assume_role_doc" { ] principals { - type = "AWS" identifiers = [ local.users_account_id, ] + type = "AWS" } } } diff --git a/variables.tf b/variables.tf index fa73e25f..7f7417e8 100644 --- a/variables.tf +++ b/variables.tf @@ -5,23 +5,23 @@ # ------------------------------------------------------------------------------ variable "assessment_account_name" { - type = string description = "The name of the AWS account for this assessment (e.g. \"env0\")." + type = string } variable "operations_subnet_cidr_block" { - type = string description = "The operations subnet CIDR block for this assessment (e.g. \"10.10.0.0/24\")." + type = string } variable "private_subnet_cidr_blocks" { - type = list(string) description = "The list of private subnet CIDR blocks for this assessment (e.g. [\"10.10.1.0/24\", \"10.10.2.0/24\"])." + type = list(string) } variable "vpc_cidr_block" { - type = string description = "The CIDR block to use this assessment's VPC (e.g. \"10.224.0.0/21\")." + type = string } # ------------------------------------------------------------------------------ @@ -31,45 +31,45 @@ variable "vpc_cidr_block" { # ------------------------------------------------------------------------------ variable "assessor_account_role_arn" { - type = string - description = "The ARN of an IAM role that can be assumed to create, delete, and modify AWS resources in a separate assessor-owned AWS account." default = "arn:aws:iam::123456789012:role/Allow_It" + description = "The ARN of an IAM role that can be assumed to create, delete, and modify AWS resources in a separate assessor-owned AWS account." + type = string } variable "assessmentfindingsbucketwrite_sharedservices_policy_description" { - type = string - description = "The description to associate with the IAM policy that allows assumption of the role in the Shared Services account that is allowed to write to the assessment findings bucket." default = "Allows assumption of the role in the Shared Services account that is allowed to write to the assessment findings bucket." + description = "The description to associate with the IAM policy that allows assumption of the role in the Shared Services account that is allowed to write to the assessment findings bucket." + type = string } variable "assessmentfindingsbucketwrite_sharedservices_policy_name" { - type = string - description = "The name to assign the IAM policy that allows assumption of the role in the Shared Services account that is allowed to write to the assessment findings bucket." default = "SharedServices-AssumeAssessmentFindingsBucketWrite" + description = "The name to assign the IAM policy that allows assumption of the role in the Shared Services account that is allowed to write to the assessment findings bucket." + type = string } variable "assessment_artifact_export_enabled" { - type = bool - description = "Whether or not to enable the export of assessment artifacts to an S3 bucket. If this is set to true, then the following variables should also be configured appropriately: assessment_artifact_export_map, ssm_key_artifact_export_access_key_id, ssm_key_artifact_export_secret_access_key, ssm_key_artifact_export_bucket_name, and ssm_key_artifact_export_region." default = false + description = "Whether or not to enable the export of assessment artifacts to an S3 bucket. If this is set to true, then the following variables should also be configured appropriately: assessment_artifact_export_map, ssm_key_artifact_export_access_key_id, ssm_key_artifact_export_secret_access_key, ssm_key_artifact_export_bucket_name, and ssm_key_artifact_export_region." + type = bool } variable "assessment_artifact_export_map" { - type = map(string) - description = "A map whose keys are assessment types and whose values are the prefixes for what an assessment artifact will be named when it is exported to the S3 bucket contained in the SSM parameter specified by the ssm_key_artifact_export_bucket_name variable (e.g. { \"PenTest\" : \"pentest/PT\", \"Phishing\" : \"phishing/PH\", \"RedTeam\" : \"redteam/RT\" }). Note that prefixes can include a path within the bucket. For example, if the prefix is \"pentest/PT\" and the assessment ID is \"ASMT1234\", then the corresponding artifact will be exported to \"bucket-name/pentest/PT-ASMT1234.tgz\" when the archive-artifact-data-to-bucket.sh script is run." default = {} + description = "A map whose keys are assessment types and whose values are the prefixes for what an assessment artifact will be named when it is exported to the S3 bucket contained in the SSM parameter specified by the ssm_key_artifact_export_bucket_name variable (e.g. { \"PenTest\" : \"pentest/PT\", \"Phishing\" : \"phishing/PH\", \"RedTeam\" : \"redteam/RT\" }). Note that prefixes can include a path within the bucket. For example, if the prefix is \"pentest/PT\" and the assessment ID is \"ASMT1234\", then the corresponding artifact will be exported to \"bucket-name/pentest/PT-ASMT1234.tgz\" when the archive-artifact-data-to-bucket.sh script is run." + type = map(string) } variable "assessment_id" { - type = string - description = "The identifier for this assessment (e.g. \"ASMT1234\")." default = "" + description = "The identifier for this assessment (e.g. \"ASMT1234\")." + type = string } variable "assessment_type" { - type = string - description = "The type of this assessment (e.g. \"PenTest\")." default = "" + description = "The type of this assessment (e.g. \"PenTest\")." + type = string } variable "aws_availability_zone" { @@ -79,58 +79,58 @@ variable "aws_availability_zone" { } variable "aws_region" { - type = string - description = "The AWS region where the non-global resources for this assessment are to be provisioned (e.g. \"us-east-1\")." default = "us-east-1" + description = "The AWS region where the non-global resources for this assessment are to be provisioned (e.g. \"us-east-1\")." + type = string } variable "cert_bucket_name" { - type = string - description = "The name of the AWS S3 bucket where certificates are stored." default = "cisa-cool-certificates" + description = "The name of the AWS S3 bucket where certificates are stored." + type = string } # TODO: This should be able to be pulled from a remote state variable "cool_domain" { - type = string - description = "The domain where the COOL resources reside (e.g. \"cool.cyber.dhs.gov\")." default = "cool.cyber.dhs.gov" + description = "The domain where the COOL resources reside (e.g. \"cool.cyber.dhs.gov\")." + type = string } variable "dns_ttl" { - type = number - description = "The TTL value to use for Route53 DNS records (e.g. 86400). A smaller value may be useful when the DNS records are changing often, for example when testing." default = 60 + description = "The TTL value to use for Route53 DNS records (e.g. 86400). A smaller value may be useful when the DNS records are changing often, for example when testing." + type = number } variable "efs_access_point_gid" { - type = number - description = "The group ID that should be used for file-system access to the EFS share (e.g. 2048). Note that this value should match the GID of any group given ownership of the EFS share mount point." default = 2048 + description = "The group ID that should be used for file-system access to the EFS share (e.g. 2048). Note that this value should match the GID of any group given ownership of the EFS share mount point." + type = number } variable "efs_access_point_root_directory" { - type = string - description = "The non-root path to use as the root directory for the AWS EFS access point that controls EFS access for assessment data sharing." default = "/assessment_share" + description = "The non-root path to use as the root directory for the AWS EFS access point that controls EFS access for assessment data sharing." + type = string } variable "efs_access_point_uid" { - type = number - description = "The user ID that should be used for file-system access to the EFS share (e.g. 2048). Note that this value should match the UID of any user given ownership of the EFS share mount point." default = 2048 + description = "The user ID that should be used for file-system access to the EFS share (e.g. 2048). Note that this value should match the UID of any user given ownership of the EFS share mount point." + type = number } variable "efs_users_group_name" { - type = string - description = "The name of the POSIX group that should have ownership of a mounted EFS share (e.g. \"efs_users\")." default = "efs_users" + description = "The name of the POSIX group that should have ownership of a mounted EFS share (e.g. \"efs_users\")." + type = string } variable "email_sending_domains" { - type = list(string) - description = "The list of domains to send emails from within the assessment environment (e.g. [ \"example.com\" ]). Teamserver and Gophish instances will be deployed with each sequential domain in the list, so teamserver0 and gophish0 will get the first domain, teamserver1 and gophish1 will get the second domain, and so on. If there are more Teamserver or Gophish instances than email-sending domains, the domains in the list will be reused in a wrap-around fashion. For example, if there are three Teamservers and only two email-sending domains, teamserver0 will get the first domain, teamserver1 will get the second domain, and teamserver2 will wrap-around back to using the first domain. Note that all letters in this variable must be lowercase or else an error will be displayed." default = ["example.com"] + description = "The list of domains to send emails from within the assessment environment (e.g. [ \"example.com\" ]). Teamserver and Gophish instances will be deployed with each sequential domain in the list, so teamserver0 and gophish0 will get the first domain, teamserver1 and gophish1 will get the second domain, and so on. If there are more Teamserver or Gophish instances than email-sending domains, the domains in the list will be reused in a wrap-around fashion. For example, if there are three Teamservers and only two email-sending domains, teamserver0 will get the first domain, teamserver1 will get the second domain, and teamserver2 will wrap-around back to using the first domain. Note that all letters in this variable must be lowercase or else an error will be displayed." + type = list(string) validation { # Note that [] actually creates a tuple, which will always compare @@ -146,32 +146,18 @@ variable "email_sending_domains" { } variable "findings_data_bucket_name" { - type = string - description = "The name of the AWS S3 bucket where findings data is to be written. The default value is not a valid string for a bucket name, so findings data cannot be written to any bucket unless a value is specified." default = "" + description = "The name of the AWS S3 bucket where findings data is to be written. The default value is not a valid string for a bucket name, so findings data cannot be written to any bucket unless a value is specified." + type = string } variable "guac_connection_setup_path" { - type = string - description = "The full path to the dbinit directory where initialization files must be stored in order to work properly. (e.g. \"/var/guacamole/dbinit\")" default = "/var/guacamole/dbinit" + description = "The full path to the dbinit directory where initialization files must be stored in order to work properly. (e.g. \"/var/guacamole/dbinit\")" + type = string } variable "inbound_ports_allowed" { - type = object({ - assessorworkbench = list(object({ protocol = string, from_port = number, to_port = number })), - debiandesktop = list(object({ protocol = string, from_port = number, to_port = number })), - egressassess = list(object({ protocol = string, from_port = number, to_port = number })), - gophish = list(object({ protocol = string, from_port = number, to_port = number })), - kali = list(object({ protocol = string, from_port = number, to_port = number })), - nessus = list(object({ protocol = string, from_port = number, to_port = number })), - pentestportal = list(object({ protocol = string, from_port = number, to_port = number })), - samba = list(object({ protocol = string, from_port = number, to_port = number })), - teamserver = list(object({ protocol = string, from_port = number, to_port = number })), - terraformer = list(object({ protocol = string, from_port = number, to_port = number })), - windows = list(object({ protocol = string, from_port = number, to_port = number })), - }) - description = "An object specifying the ports allowed inbound (from anywhere) to the various instance types (e.g. {\"assessorworkbench\" : [], \"debiandesktop\" : [], \"egressassess\" : [], \"gophish\" : [], \"kali\": [{\"protocol\": \"tcp\", \"from_port\": 443, \"to_port\": 443}, {\"protocol\": \"tcp\", \"from_port\": 9000, \"to_port\": 9009}], \"nessus\" : [], \"pentestportal\" : [], \"samba\" : [], \"teamserver\" : [], \"terraformer\" : [], \"windows\" : [], })." default = { "assessorworkbench" : [], "debiandesktop" : [], @@ -185,18 +171,32 @@ variable "inbound_ports_allowed" { "terraformer" : [], "windows" : [], } + description = "An object specifying the ports allowed inbound (from anywhere) to the various instance types (e.g. {\"assessorworkbench\" : [], \"debiandesktop\" : [], \"egressassess\" : [], \"gophish\" : [], \"kali\": [{\"protocol\": \"tcp\", \"from_port\": 443, \"to_port\": 443}, {\"protocol\": \"tcp\", \"from_port\": 9000, \"to_port\": 9009}], \"nessus\" : [], \"pentestportal\" : [], \"samba\" : [], \"teamserver\" : [], \"terraformer\" : [], \"windows\" : [], })." + type = object({ + assessorworkbench = list(object({ protocol = string, from_port = number, to_port = number })), + debiandesktop = list(object({ protocol = string, from_port = number, to_port = number })), + egressassess = list(object({ protocol = string, from_port = number, to_port = number })), + gophish = list(object({ protocol = string, from_port = number, to_port = number })), + kali = list(object({ protocol = string, from_port = number, to_port = number })), + nessus = list(object({ protocol = string, from_port = number, to_port = number })), + pentestportal = list(object({ protocol = string, from_port = number, to_port = number })), + samba = list(object({ protocol = string, from_port = number, to_port = number })), + teamserver = list(object({ protocol = string, from_port = number, to_port = number })), + terraformer = list(object({ protocol = string, from_port = number, to_port = number })), + windows = list(object({ protocol = string, from_port = number, to_port = number })), + }) } variable "nessus_activation_codes" { - type = list(string) - description = "The list of Nessus activation codes (e.g. [\"AAAA-BBBB-CCCC-DDDD\"]). The number of codes in this list should match the number of Nessus instances defined in operations_instance_counts." default = [] + description = "The list of Nessus activation codes (e.g. [\"AAAA-BBBB-CCCC-DDDD\"]). The number of codes in this list should match the number of Nessus instances defined in operations_instance_counts." + type = list(string) } variable "nessus_web_server_port" { - type = number - description = "The port on which the Nessus web server should listen (e.g. 8834)." default = 8834 + description = "The port on which the Nessus web server should listen (e.g. 8834)." + type = number validation { condition = !strcontains(var.nessus_web_server_port, ".") && var.nessus_web_server_port > 0 && var.nessus_web_server_port < 65536 @@ -205,20 +205,6 @@ variable "nessus_web_server_port" { } variable "operations_instance_counts" { - type = object({ - assessorworkbench = number, - debiandesktop = number, - egressassess = number, - gophish = number, - kali = number, - nessus = number, - pentestportal = number, - samba = number, - teamserver = number, - terraformer = number, - windows = number - }) - description = "A map specifying how many instances of each type should be created in the operations subnet (e.g. { \"assessorworkbench\" : 0, \"debiandesktop\" : 0, \"egressassess\" : 0,\"gophish\" : 0, \"kali\": 1, \"nessus\" : 0, \"pentestportal\" : 0, \"samba\" : 0, \"teamserver\" : 0, \"terraformer\" : 0, \"windows\" : 1, })." default = { "assessorworkbench" : 0, "debiandesktop" : 0, @@ -232,48 +218,62 @@ variable "operations_instance_counts" { "terraformer" : 0, "windows" : 1, } + description = "A map specifying how many instances of each type should be created in the operations subnet (e.g. { \"assessorworkbench\" : 0, \"debiandesktop\" : 0, \"egressassess\" : 0,\"gophish\" : 0, \"kali\": 1, \"nessus\" : 0, \"pentestportal\" : 0, \"samba\" : 0, \"teamserver\" : 0, \"terraformer\" : 0, \"windows\" : 1, })." + type = object({ + assessorworkbench = number, + debiandesktop = number, + egressassess = number, + gophish = number, + kali = number, + nessus = number, + pentestportal = number, + samba = number, + teamserver = number, + terraformer = number, + windows = number + }) } variable "private_domain" { - type = string - description = "The local domain to use for this assessment (e.g. \"env0\"). If not provided, `local.private_domain` will be set to the base of the assessment account name. For example, if the account name is \"env0 (Staging)\", `local.private_domain` will default to \"env0\". Note that `local.private_domain` should be used in place of `var.private_domain` throughout this project." default = "" + description = "The local domain to use for this assessment (e.g. \"env0\"). If not provided, `local.private_domain` will be set to the base of the assessment account name. For example, if the account name is \"env0 (Staging)\", `local.private_domain` will default to \"env0\". Note that `local.private_domain` should be used in place of `var.private_domain` throughout this project." + type = string } variable "provisionaccount_role_name" { - type = string - description = "The name of the IAM role that allows sufficient permissions to provision all AWS resources in the assessment account." default = "ProvisionAccount" + description = "The name of the IAM role that allows sufficient permissions to provision all AWS resources in the assessment account." + type = string } variable "provisionassessment_policy_description" { - type = string - description = "The description to associate with the IAM policy that allows provisioning of the resources required in the assessment account." default = "Allows provisioning of the resources required in the assessment account." + description = "The description to associate with the IAM policy that allows provisioning of the resources required in the assessment account." + type = string } variable "provisionassessment_policy_name" { - type = string - description = "The name to assign the IAM policy that allows provisioning of the resources required in the assessment account." default = "ProvisionAssessment" + description = "The name to assign the IAM policy that allows provisioning of the resources required in the assessment account." + type = string } variable "provisionssmsessionmanager_policy_description" { - type = string - description = "The description to associate with the IAM policy that allows sufficient permissions to provision the SSM Document resource and set up SSM session logging in this assessment account." default = "Allows sufficient permissions to provision the SSM Document resource and set up SSM session logging in this assessment account." + description = "The description to associate with the IAM policy that allows sufficient permissions to provision the SSM Document resource and set up SSM session logging in this assessment account." + type = string } variable "provisionssmsessionmanager_policy_name" { - type = string - description = "The name to assign the IAM policy that allows sufficient permissions to provision the SSM Document resource and set up SSM session logging in this assessment account." default = "ProvisionSSMSessionManager" + description = "The name to assign the IAM policy that allows sufficient permissions to provision the SSM Document resource and set up SSM session logging in this assessment account." + type = string } variable "read_terraform_state_role_name" { - type = string - description = "The name to assign the IAM role (as well as the corresponding policy) that allows read-only access to the cool-assessment-terraform state in the S3 bucket where Terraform state is stored. The %s in this name will be replaced by the value of the assessment_account_name variable." default = "ReadCoolAssessmentTerraformTerraformState-%s" + description = "The name to assign the IAM role (as well as the corresponding policy) that allows read-only access to the cool-assessment-terraform state in the S3 bucket where Terraform state is stored. The %s in this name will be replaced by the value of the assessment_account_name variable." + type = string } # This variable is copied over from cisagov/session-manager-tf-module @@ -288,106 +288,106 @@ variable "session_cloudwatch_log_group_name" { } variable "ssm_key_artifact_export_access_key_id" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the AWS access key of the IAM user that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/access_key_id\")." default = "/assessment_artifact_export/access_key_id" + description = "The AWS SSM Parameter Store parameter that contains the AWS access key of the IAM user that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/access_key_id\")." + type = string } variable "ssm_key_artifact_export_bucket_name" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the name of the assessment artifact export bucket (e.g. \"/assessment_artifact_export/bucket\")." default = "/assessment_artifact_export/bucket" + description = "The AWS SSM Parameter Store parameter that contains the name of the assessment artifact export bucket (e.g. \"/assessment_artifact_export/bucket\")." + type = string } variable "ssm_key_artifact_export_region" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the region of the IAM user (specified via ssm_key_artifact_export_access_key_id) that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/region\")." default = "/assessment_artifact_export/region" + description = "The AWS SSM Parameter Store parameter that contains the region of the IAM user (specified via ssm_key_artifact_export_access_key_id) that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/region\")." + type = string } variable "ssm_key_artifact_export_secret_access_key" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the AWS secret access key of the IAM user that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/secret_access_key\")." default = "/assessment_artifact_export/secret_access_key" + description = "The AWS SSM Parameter Store parameter that contains the AWS secret access key of the IAM user that can write to the assessment artifact export bucket (e.g. \"/assessment_artifact_export/secret_access_key\")." + type = string } variable "ssm_key_nessus_admin_password" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the password of the Nessus admin user (e.g. \"/nessus/assessment/admin_password\")." default = "/nessus/assessment/admin_password" + description = "The AWS SSM Parameter Store parameter that contains the password of the Nessus admin user (e.g. \"/nessus/assessment/admin_password\")." + type = string } variable "ssm_key_nessus_admin_username" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the username of the Nessus admin user (e.g. \"/nessus/assessment/admin_username\")." default = "/nessus/assessment/admin_username" + description = "The AWS SSM Parameter Store parameter that contains the username of the Nessus admin user (e.g. \"/nessus/assessment/admin_username\")." + type = string } variable "ssm_key_samba_username" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the username of the Samba user (e.g. \"/samba/username\")." default = "/samba/username" + description = "The AWS SSM Parameter Store parameter that contains the username of the Samba user (e.g. \"/samba/username\")." + type = string } variable "ssm_key_vnc_ssh_public_key" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the SSH public key that corresponds to the private SSH key of the VNC user (e.g. \"/vnc/ssh/ed25519_public_key\")." default = "/vnc/ssh/ed25519_public_key" + description = "The AWS SSM Parameter Store parameter that contains the SSH public key that corresponds to the private SSH key of the VNC user (e.g. \"/vnc/ssh/ed25519_public_key\")." + type = string } variable "ssm_key_vnc_username" { - type = string - description = "The AWS SSM Parameter Store parameter that contains the username of the VNC user (e.g. \"/vnc/username\")." default = "/vnc/username" + description = "The AWS SSM Parameter Store parameter that contains the username of the VNC user (e.g. \"/vnc/username\")." + type = string } variable "tags" { - type = map(string) - description = "Tags to apply to all AWS resources created" default = {} + description = "Tags to apply to all AWS resources created" + type = map(string) } variable "terraformer_permissions_boundary_policy_description" { - type = string - description = "The description to associate with the IAM permissions boundary policy attached to the Terraformer instance role in order to protect the foundational resources deployed in this account." default = "The IAM permissions boundary policy attached to the Terraformer instance role in order to protect the foundational resources deployed in this account." + description = "The description to associate with the IAM permissions boundary policy attached to the Terraformer instance role in order to protect the foundational resources deployed in this account." + type = string } variable "terraformer_permissions_boundary_policy_name" { - type = string - description = "The name to assign the IAM permissions boundary policy attached to the Terraformer instance role in order to protect the foundational resources deployed in this account." default = "TerraformerPermissionsBoundary" + description = "The name to assign the IAM permissions boundary policy attached to the Terraformer instance role in order to protect the foundational resources deployed in this account." + type = string } variable "terraformer_role_description" { - type = string - description = "The description to associate with the IAM role (and policy) that allows Terraformer instances to create appropriate AWS resources in this account." default = "Allows Terraformer instances to create appropriate AWS resources in this account." + description = "The description to associate with the IAM role (and policy) that allows Terraformer instances to create appropriate AWS resources in this account." + type = string } variable "terraformer_role_name" { - type = string - description = "The name to assign the IAM role (and policy) that allows Terraformer instances to create appropriate AWS resources in this account." default = "Terraformer" + description = "The name to assign the IAM role (and policy) that allows Terraformer instances to create appropriate AWS resources in this account." + type = string } variable "valid_assessment_id_regex" { - type = string - description = "A regular expression that specifies valid assessment identifiers (e.g. \"^ASMT[[:digit:]]{4}$\")." default = "" + description = "A regular expression that specifies valid assessment identifiers (e.g. \"^ASMT[[:digit:]]{4}$\")." + type = string } variable "valid_assessment_types" { - type = list(string) - description = "A list of valid assessment types (e.g. [\"PenTest\", \"Phishing\", \"RedTeam\"]). If this list is empty (i.e. []), then any value used for assessment_type will trigger a validation error." # Set the default value to [""] instead of [] to match the default value of # var.assessment_type, which is "". This is done to avoid a validation error # when the default values of both variables are used. - default = [""] + default = [""] + description = "A list of valid assessment types (e.g. [\"PenTest\", \"Phishing\", \"RedTeam\"]). If this list is empty (i.e. []), then any value used for assessment_type will trigger a validation error." + type = list(string) } variable "windows_with_docker" { - type = bool - description = "A boolean to control the instance type used when creating Windows instances to allow Docker Desktop support. Windows instances require the `metal` instance type to run Docker Desktop because of nested virtualization, but if Docker Desktop is not needed then other instance types are fine." default = false + description = "A boolean to control the instance type used when creating Windows instances to allow Docker Desktop support. Windows instances require the `metal` instance type to run Docker Desktop because of nested virtualization, but if Docker Desktop is not needed then other instance types are fine." + type = bool } diff --git a/vpc_flow_logs.tf b/vpc_flow_logs.tf index fd90e469..f85f8683 100644 --- a/vpc_flow_logs.tf +++ b/vpc_flow_logs.tf @@ -8,7 +8,7 @@ module "vpc_flow_logs" { aws = aws.provisionassessment } - vpc_name = local.assessment_account_name_base - vpc_id = aws_vpc.assessment.id logs_retention = "365" + vpc_id = aws_vpc.assessment.id + vpc_name = local.assessment_account_name_base } diff --git a/windows_ec2.tf b/windows_ec2.tf index a67b54d0..ea7db211 100644 --- a/windows_ec2.tf +++ b/windows_ec2.tf @@ -19,8 +19,8 @@ data "aws_ami" "windows" { values = ["ebs"] } - owners = [local.images_account_id] most_recent = true + owners = [local.images_account_id] } # The Windows EC2 instances @@ -40,7 +40,6 @@ resource "aws_instance" "windows" { availability_zone = "${var.aws_region}${var.aws_availability_zone}" iam_instance_profile = aws_iam_instance_profile.windows.name instance_type = var.windows_with_docker ? "c5n.metal" : "t3.xlarge" - subnet_id = aws_subnet.operations.id # AWS Instance Meta-Data Service (IMDS) options metadata_options { # Enable IMDS (this is the default value) @@ -56,6 +55,10 @@ resource "aws_instance" "windows" { volume_type = "gp3" volume_size = 200 } + subnet_id = aws_subnet.operations.id + tags = { + Name = format("Windows%d", count.index) + } user_data = templatefile( "${path.module}/ec2launch/windows-setup.tpl.yml", { @@ -67,6 +70,12 @@ resource "aws_instance" "windows" { vnc_public_ssh_key = data.aws_ssm_parameter.vnc_public_ssh_key.value, } ) + # volume_tags does not yet inherit the default tags from the + # provider. See hashicorp/terraform-provider-aws#19188 for more + # details. + volume_tags = merge(data.aws_default_tags.assessment.tags, { + Name = format("Windows%d", count.index) + }) vpc_security_group_ids = [ aws_security_group.cloudwatch_agent_endpoint_client.id, aws_security_group.guacamole_accessible.id, @@ -75,15 +84,6 @@ resource "aws_instance" "windows" { aws_security_group.ssm_agent_endpoint_client.id, aws_security_group.windows.id, ] - tags = { - Name = format("Windows%d", count.index) - } - # volume_tags does not yet inherit the default tags from the - # provider. See hashicorp/terraform-provider-aws#19188 for more - # details. - volume_tags = merge(data.aws_default_tags.assessment.tags, { - Name = format("Windows%d", count.index) - }) } # CloudWatch alarms for the Windows instances diff --git a/windows_iam.tf b/windows_iam.tf index 73ed02b9..92d4307f 100644 --- a/windows_iam.tf +++ b/windows_iam.tf @@ -12,22 +12,22 @@ resource "aws_iam_instance_profile" "windows" { resource "aws_iam_role" "windows_instance_role" { provider = aws.provisionassessment - name = "windows_instance_role_${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.ec2_service_assume_role_doc.json + name = "windows_instance_role_${terraform.workspace}" } # Attach the CloudWatch Agent policy to this role as well resource "aws_iam_role_policy_attachment" "cloudwatch_agent_policy_attachment_windows" { provider = aws.provisionassessment - role = aws_iam_role.windows_instance_role.id policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + role = aws_iam_role.windows_instance_role.id } # Attach the SSM Agent policy to this role as well resource "aws_iam_role_policy_attachment" "ssm_agent_policy_attachment_windows" { provider = aws.provisionassessment - role = aws_iam_role.windows_instance_role.id policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.windows_instance_role.id } diff --git a/windows_route53.tf b/windows_route53.tf index 8e1cca58..4dad4ad7 100644 --- a/windows_route53.tf +++ b/windows_route53.tf @@ -3,9 +3,9 @@ resource "aws_route53_record" "windows_A" { count = lookup(var.operations_instance_counts, "windows", 0) provider = aws.provisionassessment - zone_id = aws_route53_zone.assessment_private.zone_id name = "windows${count.index}.${aws_route53_zone.assessment_private.name}" - type = "A" - ttl = var.dns_ttl records = [aws_instance.windows[count.index].private_ip] + ttl = var.dns_ttl + type = "A" + zone_id = aws_route53_zone.assessment_private.zone_id } diff --git a/windows_sg.tf b/windows_sg.tf index b8fa4606..88ba640c 100644 --- a/windows_sg.tf +++ b/windows_sg.tf @@ -2,11 +2,10 @@ resource "aws_security_group" "windows" { provider = aws.provisionassessment - vpc_id = aws_vpc.assessment.id - tags = { Name = "Windows" } + vpc_id = aws_vpc.assessment.id } # Allow egress to PenTest Portal instances via ports 443 and 8080 @@ -14,12 +13,12 @@ resource "aws_security_group_rule" "windows_egress_to_pentestportal_via_web" { for_each = toset(["443", "8080"]) provider = aws.provisionassessment - security_group_id = aws_security_group.windows.id - type = "egress" + from_port = each.key protocol = "tcp" + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.pentestportal.id - from_port = each.key to_port = each.key + type = "egress" } # Allow egress to Nessus instances via port 8834 (the default port @@ -27,12 +26,12 @@ resource "aws_security_group_rule" "windows_egress_to_pentestportal_via_web" { resource "aws_security_group_rule" "windows_egress_to_nessus_via_web_ui" { provider = aws.provisionassessment - security_group_id = aws_security_group.windows.id - type = "egress" + from_port = 8834 protocol = "tcp" + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.nessus.id - from_port = 8834 to_port = 8834 + type = "egress" } # Allow ingress from anywhere via the allowed ports @@ -43,12 +42,12 @@ resource "aws_security_group_rule" "ingress_from_anywhere_to_windows_via_allowed # acceptable form. for_each = { for d in var.inbound_ports_allowed["windows"] : format("%s_%d_%d", d.protocol, d.from_port, d.to_port) => d } - security_group_id = aws_security_group.windows.id - type = "ingress" - protocol = each.value["protocol"] cidr_blocks = ["0.0.0.0/0"] from_port = each.value["from_port"] + protocol = each.value["protocol"] + security_group_id = aws_security_group.windows.id to_port = each.value["to_port"] + type = "ingress" } # Allow unfettered access between Windows and Kali instances @@ -56,23 +55,23 @@ resource "aws_security_group_rule" "windows_egress_to_kali_instances" { provider = aws.provisionassessment for_each = toset(["tcp", "udp"]) - security_group_id = aws_security_group.windows.id - type = "egress" + from_port = 0 protocol = each.key + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.kali.id - from_port = 0 to_port = 65535 + type = "egress" } resource "aws_security_group_rule" "windows_ingress_from_kali_instances" { provider = aws.provisionassessment for_each = toset(["tcp", "udp"]) - security_group_id = aws_security_group.windows.id - type = "ingress" + from_port = 0 protocol = each.key + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.kali.id - from_port = 0 to_port = 65535 + type = "ingress" } # Allow access between Windows and Teamserver instances on ports @@ -82,20 +81,20 @@ resource "aws_security_group_rule" "windows_ingress_from_kali_instances" { resource "aws_security_group_rule" "windows_egress_to_teamserver_instances_via_5000_to_5999_tcp" { provider = aws.provisionassessment - security_group_id = aws_security_group.windows.id - type = "egress" + from_port = 5000 protocol = "tcp" + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.teamserver.id - from_port = 5000 to_port = 5999 + type = "egress" } resource "aws_security_group_rule" "windows_ingress_from_teamserver_instances_via_5000_to_5999_tcp" { provider = aws.provisionassessment - security_group_id = aws_security_group.windows.id - type = "ingress" + from_port = 5000 protocol = "tcp" + security_group_id = aws_security_group.windows.id source_security_group_id = aws_security_group.teamserver.id - from_port = 5000 to_port = 5999 + type = "ingress" }