diff --git a/infrastructure/modules/networking/.terraform.lock.hcl b/infrastructure/modules/networking/.terraform.lock.hcl deleted file mode 100644 index e15fbddce1..0000000000 --- a/infrastructure/modules/networking/.terraform.lock.hcl +++ /dev/null @@ -1,45 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "6.17.0" - constraints = "~> 6.0" - hashes = [ - "h1:65zxvr7oxROr5hqTWQtoS5HsGOBwUko7douoc9Azptc=", - "zh:157063d66cd4b5fc650f20f56127e19c9da5d135f4231f9ca0c19a1c0bf6e29d", - "zh:2050dc03304b42204e6c58bbb1a2afd4feeac7db55d7c06be77c6b1e2ab46a0f", - "zh:2a7f7751eef636ca064700cc4574b9b54a2596d9e2e86b91c45127410d9724c6", - "zh:335fd7bb44bebfc4dd1db1c013947e1dde2518c6f2d846aac13b7314414ce461", - "zh:545c248d2eb601a7b45a34313096cae0a5201ccf31e7fd99428357ef800051e0", - "zh:57d19883a6367c245e885856a1c5395c4c743c20feff631ea4ec7b5e16826281", - "zh:66d4f080b8c268d65e8c4758ed57234e5a19deff6073ffc3753b9a4cc177b54e", - "zh:6ad50de35970f15e1ed41d39742290c1be80600b7df3a9fbb4c02f353b9586cf", - "zh:7af42fa531e4dcb3ddb09f71ca988e90626abbf56a45981c2a6c01d0b364a51b", - "zh:9a6a535a879314a9137ec9d3e858b7c490a962050845cf62620ba2bf4ae916a8", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:ca213e0262c8f686fcd40e3fc84d67b8eea1596de988c13d4a8ecd4522ede669", - "zh:cc4132f682e9bf17c0649928ad92af4da07ffe7bccfe615d955225cdcf9e7f09", - "zh:dfe6de43496d2e2b6dff131fef6ada1e15f1fbba3d47235c751564d22003d05e", - "zh:e37d035fa02693a3d47fe636076cce50b6579b6adc0a36a7cf0456a2331c99ec", - ] -} - -provider "registry.terraform.io/hashicorp/random" { - version = "3.7.2" - constraints = "~> 3.0" - hashes = [ - "h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=", - "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", - "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", - "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", - "zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3", - "zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212", - "zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34", - "zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967", - "zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d", - "zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62", - "zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0", - ] -} diff --git a/infrastructure/modules/networking/main.tf b/infrastructure/modules/networking/main.tf index c52728da0f..88e1169fbb 100644 --- a/infrastructure/modules/networking/main.tf +++ b/infrastructure/modules/networking/main.tf @@ -1,14 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = "1.14.0" required_providers { aws = { source = "hashicorp/aws" - version = "~> 6.0" - } - random = { - source = "hashicorp/random" - version = "~> 3.0" + version = "6.22.0" } } } @@ -34,6 +30,32 @@ data "aws_iam_policy_document" "flow_logs_policy" { } } +module "nacl" { + source = "./modules/nacl" + + common_tags = var.common_tags + environment = var.environment + private_subnet_ids = aws_subnet.private[*].id + project_name = var.project_name + public_subnet_ids = aws_subnet.public[*].id + vpc_cidr = var.vpc_cidr + vpc_id = aws_vpc.main.id +} + +module "vpc_endpoint" { + source = "./modules/vpc-endpoint" + + aws_region = var.aws_region + common_tags = var.common_tags + environment = var.environment + private_route_table_id = aws_route_table.private.id + private_subnet_ids = aws_subnet.private[*].id + project_name = var.project_name + public_route_table_id = aws_route_table.public.id + vpc_cidr = var.vpc_cidr + vpc_id = aws_vpc.main.id +} + resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true diff --git a/infrastructure/modules/networking/modules/nacl/main.tf b/infrastructure/modules/networking/modules/nacl/main.tf new file mode 100644 index 0000000000..afada4e631 --- /dev/null +++ b/infrastructure/modules/networking/modules/nacl/main.tf @@ -0,0 +1,128 @@ +terraform { + required_version = "1.14.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.22.0" + } + } +} + +resource "aws_network_acl" "private" { + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-private-nacl" + }) + vpc_id = var.vpc_id +} + +resource "aws_network_acl" "public" { + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-public-nacl" + }) + vpc_id = var.vpc_id +} + +resource "aws_network_acl_association" "private" { + count = length(var.private_subnet_ids) + network_acl_id = aws_network_acl.private.id + subnet_id = var.private_subnet_ids[count.index] +} + +resource "aws_network_acl_association" "public" { + count = length(var.public_subnet_ids) + network_acl_id = aws_network_acl.public.id + subnet_id = var.public_subnet_ids[count.index] +} + +resource "aws_network_acl_rule" "private_inbound_ephemeral" { + cidr_block = "0.0.0.0/0" + from_port = 1024 + network_acl_id = aws_network_acl.private.id + protocol = "tcp" + rule_action = "allow" + rule_number = 100 + to_port = 65535 +} + +resource "aws_network_acl_rule" "private_inbound_https" { + cidr_block = var.vpc_cidr + from_port = 443 + network_acl_id = aws_network_acl.private.id + protocol = "tcp" + rule_action = "allow" + rule_number = 110 + to_port = 443 +} + +resource "aws_network_acl_rule" "private_inbound_postgres" { + cidr_block = var.vpc_cidr + from_port = 5432 + network_acl_id = aws_network_acl.private.id + protocol = "tcp" + rule_action = "allow" + rule_number = 120 + to_port = 5432 +} + +resource "aws_network_acl_rule" "private_inbound_redis" { + cidr_block = var.vpc_cidr + from_port = 6379 + network_acl_id = aws_network_acl.private.id + protocol = "tcp" + rule_action = "allow" + rule_number = 130 + to_port = 6379 +} + +resource "aws_network_acl_rule" "private_outbound_all" { + cidr_block = "0.0.0.0/0" + egress = true + from_port = 0 + network_acl_id = aws_network_acl.private.id + protocol = "-1" + rule_action = "allow" + rule_number = 100 + to_port = 0 +} + +resource "aws_network_acl_rule" "public_inbound_ephemeral" { + cidr_block = "0.0.0.0/0" + from_port = 1024 + network_acl_id = aws_network_acl.public.id + protocol = "tcp" + rule_action = "allow" + rule_number = 100 + to_port = 65535 +} + +resource "aws_network_acl_rule" "public_inbound_http" { + cidr_block = "0.0.0.0/0" + from_port = 80 + network_acl_id = aws_network_acl.public.id + protocol = "tcp" + rule_action = "allow" + rule_number = 110 + to_port = 80 +} + +resource "aws_network_acl_rule" "public_inbound_https" { + cidr_block = "0.0.0.0/0" + from_port = 443 + network_acl_id = aws_network_acl.public.id + protocol = "tcp" + rule_action = "allow" + rule_number = 120 + to_port = 443 +} + +resource "aws_network_acl_rule" "public_outbound_all" { + cidr_block = "0.0.0.0/0" + egress = true + from_port = 0 + network_acl_id = aws_network_acl.public.id + protocol = "-1" + rule_action = "allow" + rule_number = 100 + to_port = 0 +} diff --git a/infrastructure/modules/networking/modules/nacl/variables.tf b/infrastructure/modules/networking/modules/nacl/variables.tf new file mode 100644 index 0000000000..de606def10 --- /dev/null +++ b/infrastructure/modules/networking/modules/nacl/variables.tf @@ -0,0 +1,34 @@ +variable "common_tags" { + description = "Common tags to apply to all resources" + type = map(string) +} + +variable "environment" { + description = "Environment name" + type = string +} + +variable "private_subnet_ids" { + description = "List of private subnet IDs" + type = list(string) +} + +variable "project_name" { + description = "Project name" + type = string +} + +variable "public_subnet_ids" { + description = "List of public subnet IDs" + type = list(string) +} + +variable "vpc_cidr" { + description = "VPC CIDR block" + type = string +} + +variable "vpc_id" { + description = "VPC ID" + type = string +} diff --git a/infrastructure/modules/networking/modules/vpc-endpoint/main.tf b/infrastructure/modules/networking/modules/vpc-endpoint/main.tf new file mode 100644 index 0000000000..0cf8bfcd82 --- /dev/null +++ b/infrastructure/modules/networking/modules/vpc-endpoint/main.tf @@ -0,0 +1,108 @@ +terraform { + required_version = "1.14.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.22.0" + } + } +} + +resource "aws_security_group" "vpc_endpoints" { + description = "Security group for VPC endpoints" + name = "${var.project_name}-${var.environment}-vpc-endpoints-sg" + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-vpc-endpoints-sg" + }) + vpc_id = var.vpc_id +} + +resource "aws_security_group_rule" "vpc_endpoints_ingress_https" { + cidr_blocks = [var.vpc_cidr] + description = "Allow HTTPS from VPC" + from_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.vpc_endpoints.id + to_port = 443 + type = "ingress" +} + +resource "aws_vpc_endpoint" "cloudwatch_logs" { + private_dns_enabled = true + security_group_ids = [aws_security_group.vpc_endpoints.id] + service_name = "com.amazonaws.${var.aws_region}.logs" + subnet_ids = var.private_subnet_ids + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-cloudwatch-logs-endpoint" + }) + vpc_endpoint_type = "Interface" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint" "ecr_api" { + private_dns_enabled = true + security_group_ids = [aws_security_group.vpc_endpoints.id] + service_name = "com.amazonaws.${var.aws_region}.ecr.api" + subnet_ids = var.private_subnet_ids + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-ecr-api-endpoint" + }) + vpc_endpoint_type = "Interface" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint" "ecr_dkr" { + private_dns_enabled = true + security_group_ids = [aws_security_group.vpc_endpoints.id] + service_name = "com.amazonaws.${var.aws_region}.ecr.dkr" + subnet_ids = var.private_subnet_ids + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-ecr-dkr-endpoint" + }) + vpc_endpoint_type = "Interface" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint" "s3" { + service_name = "com.amazonaws.${var.aws_region}.s3" + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-s3-endpoint" + }) + vpc_endpoint_type = "Gateway" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint" "secretsmanager" { + private_dns_enabled = true + security_group_ids = [aws_security_group.vpc_endpoints.id] + service_name = "com.amazonaws.${var.aws_region}.secretsmanager" + subnet_ids = var.private_subnet_ids + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-secretsmanager-endpoint" + }) + vpc_endpoint_type = "Interface" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint" "ssm" { + private_dns_enabled = true + security_group_ids = [aws_security_group.vpc_endpoints.id] + service_name = "com.amazonaws.${var.aws_region}.ssm" + subnet_ids = var.private_subnet_ids + tags = merge(var.common_tags, { + Name = "${var.project_name}-${var.environment}-ssm-endpoint" + }) + vpc_endpoint_type = "Interface" + vpc_id = var.vpc_id +} + +resource "aws_vpc_endpoint_route_table_association" "s3_private" { + route_table_id = var.private_route_table_id + vpc_endpoint_id = aws_vpc_endpoint.s3.id +} + +resource "aws_vpc_endpoint_route_table_association" "s3_public" { + route_table_id = var.public_route_table_id + vpc_endpoint_id = aws_vpc_endpoint.s3.id +} diff --git a/infrastructure/modules/networking/modules/vpc-endpoint/outputs.tf b/infrastructure/modules/networking/modules/vpc-endpoint/outputs.tf new file mode 100644 index 0000000000..c33d9b1626 --- /dev/null +++ b/infrastructure/modules/networking/modules/vpc-endpoint/outputs.tf @@ -0,0 +1,4 @@ +output "security_group_id" { + description = "Security group ID for VPC endpoints" + value = aws_security_group.vpc_endpoints.id +} diff --git a/infrastructure/modules/networking/modules/vpc-endpoint/variables.tf b/infrastructure/modules/networking/modules/vpc-endpoint/variables.tf new file mode 100644 index 0000000000..22607624f6 --- /dev/null +++ b/infrastructure/modules/networking/modules/vpc-endpoint/variables.tf @@ -0,0 +1,44 @@ +variable "aws_region" { + description = "AWS region" + type = string +} + +variable "common_tags" { + description = "Common tags to apply to all resources" + type = map(string) +} + +variable "environment" { + description = "Environment name" + type = string +} + +variable "private_route_table_id" { + description = "Private route table ID" + type = string +} + +variable "private_subnet_ids" { + description = "List of private subnet IDs" + type = list(string) +} + +variable "project_name" { + description = "Project name" + type = string +} + +variable "public_route_table_id" { + description = "Public route table ID" + type = string +} + +variable "vpc_cidr" { + description = "VPC CIDR block" + type = string +} + +variable "vpc_id" { + description = "VPC ID" + type = string +} diff --git a/infrastructure/modules/networking/outputs.tf b/infrastructure/modules/networking/outputs.tf index ca4f0573cb..f748588f70 100644 --- a/infrastructure/modules/networking/outputs.tf +++ b/infrastructure/modules/networking/outputs.tf @@ -12,3 +12,8 @@ output "private_subnet_ids" { description = "A list of private subnet IDs" value = aws_subnet.private[*].id } + +output "vpc_endpoint_security_group_id" { + description = "Security group ID for VPC endpoints" + value = module.vpc_endpoint.security_group_id +} diff --git a/infrastructure/modules/networking/variables.tf b/infrastructure/modules/networking/variables.tf index 617ee55f29..06dae08bc8 100644 --- a/infrastructure/modules/networking/variables.tf +++ b/infrastructure/modules/networking/variables.tf @@ -1,3 +1,8 @@ +variable "aws_region" { + description = "The AWS region" + type = string +} + variable "availability_zones" { description = "A list of availability zones for the VPC" type = list(string) diff --git a/infrastructure/modules/security/.terraform.lock.hcl b/infrastructure/modules/security/.terraform.lock.hcl deleted file mode 100644 index e15fbddce1..0000000000 --- a/infrastructure/modules/security/.terraform.lock.hcl +++ /dev/null @@ -1,45 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "6.17.0" - constraints = "~> 6.0" - hashes = [ - "h1:65zxvr7oxROr5hqTWQtoS5HsGOBwUko7douoc9Azptc=", - "zh:157063d66cd4b5fc650f20f56127e19c9da5d135f4231f9ca0c19a1c0bf6e29d", - "zh:2050dc03304b42204e6c58bbb1a2afd4feeac7db55d7c06be77c6b1e2ab46a0f", - "zh:2a7f7751eef636ca064700cc4574b9b54a2596d9e2e86b91c45127410d9724c6", - "zh:335fd7bb44bebfc4dd1db1c013947e1dde2518c6f2d846aac13b7314414ce461", - "zh:545c248d2eb601a7b45a34313096cae0a5201ccf31e7fd99428357ef800051e0", - "zh:57d19883a6367c245e885856a1c5395c4c743c20feff631ea4ec7b5e16826281", - "zh:66d4f080b8c268d65e8c4758ed57234e5a19deff6073ffc3753b9a4cc177b54e", - "zh:6ad50de35970f15e1ed41d39742290c1be80600b7df3a9fbb4c02f353b9586cf", - "zh:7af42fa531e4dcb3ddb09f71ca988e90626abbf56a45981c2a6c01d0b364a51b", - "zh:9a6a535a879314a9137ec9d3e858b7c490a962050845cf62620ba2bf4ae916a8", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:ca213e0262c8f686fcd40e3fc84d67b8eea1596de988c13d4a8ecd4522ede669", - "zh:cc4132f682e9bf17c0649928ad92af4da07ffe7bccfe615d955225cdcf9e7f09", - "zh:dfe6de43496d2e2b6dff131fef6ada1e15f1fbba3d47235c751564d22003d05e", - "zh:e37d035fa02693a3d47fe636076cce50b6579b6adc0a36a7cf0456a2331c99ec", - ] -} - -provider "registry.terraform.io/hashicorp/random" { - version = "3.7.2" - constraints = "~> 3.0" - hashes = [ - "h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=", - "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", - "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", - "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", - "zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3", - "zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212", - "zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34", - "zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967", - "zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d", - "zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62", - "zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0", - ] -} diff --git a/infrastructure/modules/security/main.tf b/infrastructure/modules/security/main.tf index cdfd08592b..62b43ead05 100644 --- a/infrastructure/modules/security/main.tf +++ b/infrastructure/modules/security/main.tf @@ -1,14 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = "1.14.0" required_providers { aws = { source = "hashicorp/aws" - version = "~> 6.0" - } - random = { - source = "hashicorp/random" - version = "~> 3.0" + version = "6.22.0" } } } @@ -107,6 +103,26 @@ resource "aws_security_group" "redis" { } } +resource "aws_security_group_rule" "ecs_to_vpc_endpoints" { + description = "Allow HTTPS to VPC endpoints" + from_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.ecs.id + source_security_group_id = var.vpc_endpoint_sg_id + to_port = 443 + type = "egress" +} + +resource "aws_security_group_rule" "lambda_to_vpc_endpoints" { + description = "Allow HTTPS to VPC endpoints" + from_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.lambda.id + source_security_group_id = var.vpc_endpoint_sg_id + to_port = 443 + type = "egress" +} + resource "aws_security_group_rule" "rds_from_ecs" { count = var.create_rds_proxy ? 0 : 1 description = "PostgreSQL from ECS" diff --git a/infrastructure/modules/security/variables.tf b/infrastructure/modules/security/variables.tf index 2f5204e539..f889d43a37 100644 --- a/infrastructure/modules/security/variables.tf +++ b/infrastructure/modules/security/variables.tf @@ -36,6 +36,11 @@ variable "redis_port" { type = number } +variable "vpc_endpoint_sg_id" { + description = "Security group ID for VPC endpoints" + type = string +} + variable "vpc_id" { description = "The ID of the VPC" type = string diff --git a/infrastructure/staging/main.tf b/infrastructure/staging/main.tf index a8cc0d8898..9dce352ed2 100644 --- a/infrastructure/staging/main.tf +++ b/infrastructure/staging/main.tf @@ -68,6 +68,7 @@ module "ecs" { module "networking" { source = "../modules/networking" + aws_region = var.aws_region availability_zones = var.availability_zones common_tags = local.common_tags environment = var.environment @@ -95,13 +96,14 @@ module "parameters" { module "security" { source = "../modules/security" - common_tags = local.common_tags - create_rds_proxy = var.create_rds_proxy - db_port = var.db_port - environment = var.environment - project_name = var.project_name - redis_port = var.redis_port - vpc_id = module.networking.vpc_id + common_tags = local.common_tags + create_rds_proxy = var.create_rds_proxy + db_port = var.db_port + environment = var.environment + project_name = var.project_name + redis_port = var.redis_port + vpc_endpoint_sg_id = module.networking.vpc_endpoint_security_group_id + vpc_id = module.networking.vpc_id } module "storage" {