From 0d2a111501ae2ff637af27508786430244000c33 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 10 Feb 2025 12:46:36 +0000 Subject: [PATCH 01/11] Add VPC endpoint, security group, SG rules --- vpc/main.tf | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/vpc/main.tf b/vpc/main.tf index 0117a3656..6c55ad003 100644 --- a/vpc/main.tf +++ b/vpc/main.tf @@ -318,3 +318,76 @@ module "logs" { source = "../logs" name_prefix = var.arg_name } + +#///////// Secret Rotation Lambda VPC Endpoint //////// + +resource "aws_vpc_endpoint" "secret-rotation-vpc-endpoint" { + vpc_id = aws_vpc.vpc.id + service_name = "com.amazonaws.${local.region}.secretsmanager" + vpc_endpoint_type = "Interface" + private_dns_enabled = true + subnet_ids = data.aws_subnets.private-subnets.ids + security_group_ids = [ + aws_security_group.rds-vpc-endpoint-sg.id, + aws_security_group.vpc-core-sg.id + ] + tags = merge( + local.tags, + { + Name = "${var.arg_name}-secret-rotation-endpoint" + } + ) +} + +resource "aws_security_group" "secret-rotation-vpc-endpoint-sg" { + name = "${var.arg_name}-secret-rotation-endpoint-sg" + description = "A security group for the secret rotation lambda to access ECS service" + vpc_id = aws_vpc.vpc.id + tags = merge( + local.tags, + { + Name = "${var.arg_name}-secret-rotation-endpoint-sg" + } + ) +} + +resource "aws_security_group_rule" "secret-rotation-ingress-fargate" { + type = "ingress" + description = "Ingress from Fargate containers" + from_port = 443 + to_port = 443 + protocol = "tcp" + source_security_group_id = aws_security_group.vpc-core-sg.id + security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id +} + +resource "aws_security_group_rule" "secret-rotation-ingress-lambda-to-ecs-service" { + type = "ingress" + description = "Ingress from Lambda Functions to Fargate containers" + from_port = 443 + to_port = 443 + protocol = "tcp" + source_security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id + security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id +} + +resource "aws_security_group_rule" "secret-rotation-egress-lambda-to-ecs-service" { + type = "egress" + description = "Egress to Lambda Functions from Fargate containers" + from_port = 443 + to_port = 443 + protocol = "tcp" + source_security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id + security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id +} + +# If applied to the service then may be too permissive??? +resource "aws_security_group_rule" "secret-rotation-egress-https" { + type = "egress" + description = "Egress for HTTPS" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id +} From 67e3a69d2ef756301ef2a03ec2228ebb6183d201 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 10 Feb 2025 12:47:45 +0000 Subject: [PATCH 02/11] Add secret rotation lambda VPC config block --- application-load-balancer/main.tf | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 3690ca077..7e347d9a3 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -16,6 +16,13 @@ data "aws_subnets" "public-subnets" { } } +data "aws_subnets" "private-subnets" { + filter { + name = "tag:Name" + values = ["${var.vpc_name}-private-*"] + } +} + resource "aws_lb" "this" { # checkov:skip=CKV2_AWS_20: Redirects for HTTP requests into HTTPS happens on the CDN # checkov:skip=CKV2_AWS_28: WAF is outside of terraform-platform-modules @@ -373,6 +380,13 @@ data "archive_file" "lambda" { ] } +data "aws_security_group" "secret_rotation_endpoint_sg" { + name = "${var.vpc_name}-secret-rotation-endpoint-sg" +} + +data "aws_security_group" "ecs_service_sg" { + name = "${var.application}-${var.environment}-EnvironmentSecurityGroup" +} # Secrets Manager Rotation Lambda Function resource "aws_lambda_function" "origin-secret-rotate-function" { @@ -414,7 +428,13 @@ resource "aws_lambda_function" "origin-secret-rotate-function" { layers = ["arn:aws:lambda:eu-west-2:763451185160:layer:python-requests:1"] source_code_hash = data.archive_file.lambda.output_base64sha256 - tags = local.tags + + vpc_config { + security_group_ids = [data.aws_security_group.secret_rotation_endpoint_sg.id, data.aws_security_group.ecs_service_sg.id] + subnet_ids = data.aws_subnets.private-subnets.ids + } + + tags = local.tags } # Lambda Permission for Secrets Manager Rotation From 2d8fb1f4fc94ef0758a744c04270ca73ccaf96bc Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Tue, 11 Feb 2025 16:07:53 +0000 Subject: [PATCH 03/11] Add vpc config to secret rotation lambda, add permissions required --- application-load-balancer/main.tf | 49 ++++++++++++++++----- vpc/main.tf | 73 ------------------------------- 2 files changed, 37 insertions(+), 85 deletions(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 7e347d9a3..049460a2b 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -1,3 +1,7 @@ +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + data "aws_ssm_parameter" "slack_token" { name = "/codebuild/slack_oauth_token" } @@ -23,6 +27,10 @@ data "aws_subnets" "private-subnets" { } } +data "aws_security_group" "vpc_base_sg" { + name = "${data.aws_vpc.vpc.tags["Name"]}-base-sg" +} + resource "aws_lb" "this" { # checkov:skip=CKV2_AWS_20: Redirects for HTTP requests into HTTPS happens on the CDN # checkov:skip=CKV2_AWS_28: WAF is outside of terraform-platform-modules @@ -181,8 +189,6 @@ output "alb-arn" { ## This section configures WAF on ALB to attach security token. -data "aws_caller_identity" "current" {} - # Random password for the secret value resource "random_password" "origin-secret" { length = 32 @@ -353,8 +359,34 @@ data "aws_iam_policy_document" "origin_verify_rotate_policy" { aws_kms_key.origin_verify_secret_key.arn ] } + + statement { + effect = "Allow" + actions = [ + "ec2:CreateNetworkInterface", + "ec2:DeleteNetworkInterface", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:AttachNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ] + + resources = concat( + [ + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${aws_security_group.alb-security-group["http"].id}" + ], + [for subnet_id in data.aws_subnets.private-subnets.ids : + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/${subnet_id}" + ] + ) + } } + resource "aws_iam_role_policy" "origin_secret_rotate_policy" { name = "OriginVerifyRotatePolicy" role = aws_iam_role.origin-secret-rotate-execution-role.name @@ -380,14 +412,6 @@ data "archive_file" "lambda" { ] } -data "aws_security_group" "secret_rotation_endpoint_sg" { - name = "${var.vpc_name}-secret-rotation-endpoint-sg" -} - -data "aws_security_group" "ecs_service_sg" { - name = "${var.application}-${var.environment}-EnvironmentSecurityGroup" -} - # Secrets Manager Rotation Lambda Function resource "aws_lambda_function" "origin-secret-rotate-function" { # Precedence in the Postgres Lambda to skip first 2 checks @@ -430,8 +454,9 @@ resource "aws_lambda_function" "origin-secret-rotate-function" { source_code_hash = data.archive_file.lambda.output_base64sha256 vpc_config { - security_group_ids = [data.aws_security_group.secret_rotation_endpoint_sg.id, data.aws_security_group.ecs_service_sg.id] - subnet_ids = data.aws_subnets.private-subnets.ids + security_group_ids = [aws_security_group.alb-security-group["http"].id, data.aws_security_group.vpc_base_sg.id] + subnet_ids = tolist(data.aws_subnets.private-subnets.ids) + } tags = local.tags diff --git a/vpc/main.tf b/vpc/main.tf index 6c55ad003..0117a3656 100644 --- a/vpc/main.tf +++ b/vpc/main.tf @@ -318,76 +318,3 @@ module "logs" { source = "../logs" name_prefix = var.arg_name } - -#///////// Secret Rotation Lambda VPC Endpoint //////// - -resource "aws_vpc_endpoint" "secret-rotation-vpc-endpoint" { - vpc_id = aws_vpc.vpc.id - service_name = "com.amazonaws.${local.region}.secretsmanager" - vpc_endpoint_type = "Interface" - private_dns_enabled = true - subnet_ids = data.aws_subnets.private-subnets.ids - security_group_ids = [ - aws_security_group.rds-vpc-endpoint-sg.id, - aws_security_group.vpc-core-sg.id - ] - tags = merge( - local.tags, - { - Name = "${var.arg_name}-secret-rotation-endpoint" - } - ) -} - -resource "aws_security_group" "secret-rotation-vpc-endpoint-sg" { - name = "${var.arg_name}-secret-rotation-endpoint-sg" - description = "A security group for the secret rotation lambda to access ECS service" - vpc_id = aws_vpc.vpc.id - tags = merge( - local.tags, - { - Name = "${var.arg_name}-secret-rotation-endpoint-sg" - } - ) -} - -resource "aws_security_group_rule" "secret-rotation-ingress-fargate" { - type = "ingress" - description = "Ingress from Fargate containers" - from_port = 443 - to_port = 443 - protocol = "tcp" - source_security_group_id = aws_security_group.vpc-core-sg.id - security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id -} - -resource "aws_security_group_rule" "secret-rotation-ingress-lambda-to-ecs-service" { - type = "ingress" - description = "Ingress from Lambda Functions to Fargate containers" - from_port = 443 - to_port = 443 - protocol = "tcp" - source_security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id - security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id -} - -resource "aws_security_group_rule" "secret-rotation-egress-lambda-to-ecs-service" { - type = "egress" - description = "Egress to Lambda Functions from Fargate containers" - from_port = 443 - to_port = 443 - protocol = "tcp" - source_security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id - security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id -} - -# If applied to the service then may be too permissive??? -resource "aws_security_group_rule" "secret-rotation-egress-https" { - type = "egress" - description = "Egress for HTTPS" - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - security_group_id = aws_security_group.secret-rotation-vpc-endpoint-sg.id -} From e0869501d478b99250f8c3358358791bc61445f2 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Wed, 12 Feb 2025 20:34:38 +0000 Subject: [PATCH 04/11] Add terraform tests for lambda vpc config block --- application-load-balancer/main.tf | 1 - .../tests/unit.tftest.hcl | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 049460a2b..6ddd1dd9b 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -456,7 +456,6 @@ resource "aws_lambda_function" "origin-secret-rotate-function" { vpc_config { security_group_ids = [aws_security_group.alb-security-group["http"].id, data.aws_security_group.vpc_base_sg.id] subnet_ids = tolist(data.aws_subnets.private-subnets.ids) - } tags = local.tags diff --git a/application-load-balancer/tests/unit.tftest.hcl b/application-load-balancer/tests/unit.tftest.hcl index 3609478bd..3d23dc831 100644 --- a/application-load-balancer/tests/unit.tftest.hcl +++ b/application-load-balancer/tests/unit.tftest.hcl @@ -27,6 +27,7 @@ override_data { values = { id = "vpc-00112233aabbccdef" cidr_block = "10.0.0.0/16" + tags = { "Name" : "vpc-name" } } } override_data { @@ -35,6 +36,14 @@ override_data { ids = ["subnet-000111222aaabbb01"] } } + +override_data { + target = data.aws_subnets.private-subnets + values = { + ids = ["subnet-aaa111", "subnet-bbb222"] + } +} + override_data { target = data.aws_route53_zone.domain-root values = { @@ -56,6 +65,12 @@ override_data { json = "{\"Sid\": \"LambdaExecutionRolePolicy\"}" } } +override_data { + target = data.aws_security_group.vpc_base_sg + values = { + id = "!abcd5f" + } +} variables { @@ -500,6 +515,40 @@ run "waf_and_rotate_lambda" { error_message = "Invalid SLACK_CHANNEL environment variable for aws_lambda_function.origin-secret-rotate-function" } + assert { + condition = aws_lambda_function.origin-secret-rotate-function.environment[0].variables.WAF_SLEEP_DURATION == "75" + error_message = "WAF_SLEEP_DURATION should be 75" + } + + assert { + condition = contains(aws_lambda_function.origin-secret-rotate-function.vpc_config[0].security_group_ids, data.aws_security_group.vpc_base_sg.id) + error_message = "Security group should include VPC base security group" + } + + # Requires executing run block with 'apply' to evaluate despite configuring with an override block + # assert { + # condition = contains(aws_lambda_function.origin-secret-rotate-function.vpc_config[0].security_group_ids, aws_security_group.alb-security-group["http"].id) + # error_message = "Security group should include ALB HTTP security group" + # } + + assert { + condition = length(aws_lambda_function.origin-secret-rotate-function.vpc_config[0].subnet_ids) == 2 + error_message = "Lambda function should be associated with 2 subnets" + } + + assert { + condition = contains(aws_lambda_function.origin-secret-rotate-function.vpc_config[0].subnet_ids, "subnet-aaa111") + error_message = "Lambda function should be in subnet-aaa111" + } + + assert { + condition = contains(aws_lambda_function.origin-secret-rotate-function.vpc_config[0].subnet_ids, "subnet-bbb222") + error_message = "Lambda function should be in subnet-bbb222" + } + + + # ---- End of testing LAMBDA FUNCTION ----- + assert { condition = aws_lambda_permission.rotate-function-invoke-permission.statement_id == "AllowSecretsManagerInvocation" error_message = "Invalid statement_id for aws_lambda_permission.rotate-function-invoke-permission" @@ -520,6 +569,7 @@ run "waf_and_rotate_lambda" { error_message = "Invalid principal for aws_lambda_permission.rotate-function-invoke-permission" } + # ---- End of testing LAMBDA PERMISSIONS ----- assert { condition = aws_iam_role.origin-secret-rotate-execution-role.name == "${var.application}-${var.environment}-origin-secret-rotate-role" From cbd8a2de5ace4321db3d0ecc8c928e3b4435b02e Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Fri, 14 Feb 2025 07:16:27 -0800 Subject: [PATCH 05/11] Add terraform tests for labda data policy --- application-load-balancer/main.tf | 2 +- .../tests/unit.tftest.hcl | 162 +++++++++++++++++- 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 6ddd1dd9b..7c526eb32 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -369,7 +369,7 @@ data "aws_iam_policy_document" "origin_verify_rotate_policy" { "ec2:DescribeNetworkInterfaces", "ec2:AttachNetworkInterface", "ec2:DescribeSubnets", - "ec2:DescribeSecurityGroups", + "ec2:DescribeSecurityGroups" ] resources = concat( diff --git a/application-load-balancer/tests/unit.tftest.hcl b/application-load-balancer/tests/unit.tftest.hcl index 3d23dc831..90da21d75 100644 --- a/application-load-balancer/tests/unit.tftest.hcl +++ b/application-load-balancer/tests/unit.tftest.hcl @@ -581,8 +581,168 @@ run "waf_and_rotate_lambda" { error_message = "Invalid assume_role_policy for aws_iam_role.origin-secret-rotate-execution-role" } - # Cannot assert against the arn in a plan. Requires an apply to evaluate. + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[0].effect == "Allow" + error_message = "First statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[0].actions == toset(["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams"]) + error_message = "First statement actions incorrect" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[0].resources) == "arn:aws:logs:eu-west-2:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/*origin-secret-rotate*" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[1].effect == "Allow" + error_message = "Second statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[1].actions == toset(["secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue", "secretsmanager:PutSecretValue", "secretsmanager:UpdateSecretVersionStage"]) + error_message = "Second statement actions incorrect" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[1].resources) == "arn:aws:secretsmanager:eu-west-2:${data.aws_caller_identity.current.account_id}:secret:${var.application}-${var.environment}-origin-verify-header-secret-*" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[2].effect == "Allow" + error_message = "Third statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[2].actions == toset(["secretsmanager:GetRandomPassword"]) + error_message = "Third statement action should be: secretsmanager:GetRandomPassword" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[2].resources) == "*" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[3].effect == "Allow" + error_message = "Fourth statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[3].actions == toset(["cloudfront:GetDistribution", "cloudfront:GetDistributionConfig", "cloudfront:ListDistributions", "cloudfront:UpdateDistribution"]) + error_message = "Fourth statement actions incorrect" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[3].resources) == "arn:aws:cloudfront::${var.dns_account_id}:distribution/*" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[4].effect == "Allow" + error_message = "Fifth statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[4].actions == toset(["wafv2:*"]) + error_message = "Fifth statement action should be: wafv2:*" + } + + # Requires executing run block with 'apply' to evaluate + # assert { + # condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[4].resources) == aws_wafv2_web_acl.waf-acl.arn + # error_message = "Unexpected resources" + # } + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[5].effect == "Allow" + error_message = "Sixth statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[5].actions == toset(["wafv2:UpdateWebACL"]) + error_message = "Sixth statement action should be: wafv2:UpdateWebACL" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[5].resources) == "arn:aws:wafv2:eu-west-2:${data.aws_caller_identity.current.account_id}:regional/managedruleset/*/*" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[6].effect == "Allow" + error_message = "Seventh statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[6].actions == toset(["sts:AssumeRole"]) + error_message = "Seventh statement action should be: sts:AssumeRole" + } + + assert { + condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[6].resources) == "arn:aws:iam::${var.dns_account_id}:role/dbt_platform_cloudfront_token_rotation" + error_message = "Unexpected resources" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[7].effect == "Allow" + error_message = "Eighth statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[7].actions == toset(["kms:Decrypt", "kms:DescribeKey", "kms:Encrypt", "kms:GenerateDataKey"]) + error_message = "Eighth statement actions incorrect" + } + + # Requires executing run block with 'apply' to evaluate + # assert { + # condition = one(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[7].resources) == aws_kms_key.origin_verify_secret_key.arn + # error_message = "Unexpected resources" + # } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].effect == "Allow" + error_message = "Ninth statement effect should be: Allow" + } + + assert { + condition = data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].actions == toset([ + "ec2:CreateNetworkInterface", + "ec2:DeleteNetworkInterface", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:AttachNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ]) + error_message = "Ninth statement actions incorrect" + } + + # Requires executing run block with 'apply' to evaluate "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${aws_security_group.alb-security-group["http"].id} + assert { + condition = alltrue( + concat( + [ + contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*"), + contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*"), + contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}"), + ], + [for subnet_id in data.aws_subnets.private-subnets.ids : + contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/${subnet_id}") + ] + ) + ) + error_message = "Missing expected resources in IAM policy" + } + + # ---- End of testing LAMBDA DATA POLICY PERMISSIONS ----- assert { condition = aws_secretsmanager_secret_rotation.origin-verify-rotate-schedule.rotation_rules[0].automatically_after_days == 7 From ccf691a4d5164d0f0351c47b2bb4146683c15cb0 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Wed, 19 Feb 2025 09:01:16 +0000 Subject: [PATCH 06/11] add resource permissons to interact with VPC for rotation lambda --- application-load-balancer/main.tf | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 7c526eb32..89fad89b7 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -372,17 +372,13 @@ data "aws_iam_policy_document" "origin_verify_rotate_policy" { "ec2:DescribeSecurityGroups" ] - resources = concat( - [ - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*", - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*", - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}", - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${aws_security_group.alb-security-group["http"].id}" - ], - [for subnet_id in data.aws_subnets.private-subnets.ids : - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/${subnet_id}" - ] - ) + resources = [ + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${aws_security_group.alb-security-group["http"].id}", + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/*" + ] } } From 72e07788f39472dd4345d9910019745503a74a6a Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Wed, 19 Feb 2025 09:09:08 +0000 Subject: [PATCH 07/11] Update ALB tests for VPC resources --- .../tests/unit.tftest.hcl | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/application-load-balancer/tests/unit.tftest.hcl b/application-load-balancer/tests/unit.tftest.hcl index 90da21d75..1d89a7956 100644 --- a/application-load-balancer/tests/unit.tftest.hcl +++ b/application-load-balancer/tests/unit.tftest.hcl @@ -724,24 +724,31 @@ run "waf_and_rotate_lambda" { # Requires executing run block with 'apply' to evaluate "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${aws_security_group.alb-security-group["http"].id} assert { condition = alltrue( - concat( - [ - contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*"), - contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*"), - contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}"), - ], - [for subnet_id in data.aws_subnets.private-subnets.ids : - contains(data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/${subnet_id}") - ] - ) + [ + contains( + data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:network-interface/*" + ), + contains( + data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:instance/*" + ), + contains( + data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:security-group/${data.aws_security_group.vpc_base_sg.id}" + ), + contains( + data.aws_iam_policy_document.origin_verify_rotate_policy.statement[8].resources, + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/*" + ) + ] ) error_message = "Missing expected resources in IAM policy" } + + + # ---- End of testing LAMBDA DATA POLICY PERMISSIONS ----- assert { From 3de793637b4be51ba1d14d5547e9e145d45c9f0b Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 24 Feb 2025 13:47:36 +0000 Subject: [PATCH 08/11] Add correct redis plan name --- extensions/locals.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/locals.tf b/extensions/locals.tf index 0f9c5bcf8..cab308976 100644 --- a/extensions/locals.tf +++ b/extensions/locals.tf @@ -1,8 +1,8 @@ locals { plans = { - opensearch = yamldecode(file("${path.module}/../opensearch/plans.yml")) - postgres = yamldecode(file("${path.module}/../postgres/plans.yml")) - elasticache-redis = yamldecode(file("${path.module}/../elasticache-redis/plans.yml")) + opensearch = yamldecode(file("${path.module}/../opensearch/plans.yml")) + postgres = yamldecode(file("${path.module}/../postgres/plans.yml")) + redis = yamldecode(file("${path.module}/../elasticache-redis/plans.yml")) } # So we don't hit a Parameter Store limit, filter environment config for extensions so it only includes the defaults (`"*"`) and the current environment From 8c8babe5035ada214d487e861cc7f02fcb5e98d9 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 24 Feb 2025 15:48:28 +0000 Subject: [PATCH 09/11] Add lambda function required VPC permissions --- application-load-balancer/main.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 89fad89b7..1b95c00a9 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -389,6 +389,10 @@ resource "aws_iam_role_policy" "origin_secret_rotate_policy" { policy = data.aws_iam_policy_document.origin_verify_rotate_policy.json } +resource "aws_iam_role_policy_attachment" "lambda_vpc_access" { + role = aws_iam_role.origin-secret-rotate-execution-role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" +} # This file needs to exist, but it's not directly used in the Terraform so... From cb838f0a8f0be0f763ffa197fa316f25ebf72f3b Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 24 Feb 2025 16:37:04 +0000 Subject: [PATCH 10/11] Add empty set index for role like other resources --- application-load-balancer/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-load-balancer/main.tf b/application-load-balancer/main.tf index 9bfe4e8e5..54bc78618 100644 --- a/application-load-balancer/main.tf +++ b/application-load-balancer/main.tf @@ -392,7 +392,7 @@ resource "aws_iam_role_policy" "origin_secret_rotate_policy" { } resource "aws_iam_role_policy_attachment" "lambda_vpc_access" { - role = aws_iam_role.origin-secret-rotate-execution-role.name + role = aws_iam_role.origin-secret-rotate-execution-role[""].name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" } From 7f8dc43cb05493133b92863120993ea0b74b7346 Mon Sep 17 00:00:00 2001 From: Tony Griffin Date: Mon, 24 Feb 2025 17:36:42 +0000 Subject: [PATCH 11/11] Add correct tag to SSM parameter to allow copiloy service to access it --- vpc/main.tf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vpc/main.tf b/vpc/main.tf index 0117a3656..efa016aab 100644 --- a/vpc/main.tf +++ b/vpc/main.tf @@ -118,7 +118,13 @@ resource "aws_ssm_parameter" "combined_nat_gateway_eips" { name = "/${var.arg_name}/EGRESS_IPS" type = "String" value = join(",", local.nat_gateway_eips) - tags = local.tags + tags = merge( + local.tags, + { + "copilot-application" = "__all__" + } + ) + }