diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f94e143c..215c7876 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,7 +9,8 @@ "ghcr.io/devcontainers/features/aws-cli:1": {}, "ghcr.io/devcontainers/features/terraform:1": {}, "ghcr.io/devcontainers/features/docker-from-docker:1.5.0": {}, - "ghcr.io/devcontainers-contrib/features/aws-cdk:2": {} + "ghcr.io/devcontainers-contrib/features/aws-cdk:2": {}, + "ghcr.io/dhoeric/features/tfsec:1": {} }, "customizations": { "vscode": { diff --git a/infrastructure/cloud/environments/dev/dev.tfvars b/infrastructure/cloud/environments/dev/dev.tfvars index 8cfc13c2..1b6c8ec1 100644 --- a/infrastructure/cloud/environments/dev/dev.tfvars +++ b/infrastructure/cloud/environments/dev/dev.tfvars @@ -1,5 +1,5 @@ region = "ca-central-1" test_s3_bucket_name = "jasper-test-s3-bucket-dev" web_subnet_names = ["Web_Dev_aza_net", "Web_Dev_azb_net"] -# api_subnet_names = ["App_Dev_aza_net", "App_Dev_azb_net"] -# db_subnet_names = ["Data_Dev_aza_net", "Data_Dev_azb_net"] +app_subnet_names = ["App_Dev_aza_net", "App_Dev_azb_net"] +data_subnet_names = ["Data_Dev_aza_net", "Data_Dev_azb_net"] diff --git a/infrastructure/cloud/environments/dev/variables.tf b/infrastructure/cloud/environments/dev/variables.tf index 6bd35f71..ee068f95 100644 --- a/infrastructure/cloud/environments/dev/variables.tf +++ b/infrastructure/cloud/environments/dev/variables.tf @@ -33,12 +33,12 @@ variable "web_subnet_names" { type = list(string) } -# variable "api_subnet_names" { -# description = "List of Subnets for API" -# type = list(string) -# } - -# variable "db_subnet_names" { -# description = "List of Subnets for Database" -# type = list(string) -# } +variable "app_subnet_names" { + description = "List of Subnets for App" + type = list(string) +} + +variable "data_subnet_names" { + description = "List of Subnets for Data" + type = list(string) +} diff --git a/infrastructure/cloud/environments/dev/webapp.tf b/infrastructure/cloud/environments/dev/webapp.tf index 0d8d3f0d..c8336e92 100644 --- a/infrastructure/cloud/environments/dev/webapp.tf +++ b/infrastructure/cloud/environments/dev/webapp.tf @@ -18,14 +18,14 @@ module "storage" { } module "networking" { - source = "../../modules/networking" - environment = var.environment - app_name = var.app_name - region = var.region - vpc_id = var.vpc_id - web_subnet_names = var.web_subnet_names - # api_subnet_names = var.api_subnet_names - # db_subnet_names = var.db_subnet_names + source = "../../modules/networking" + environment = var.environment + app_name = var.app_name + region = var.region + vpc_id = var.vpc_id + web_subnet_names = var.web_subnet_names + app_subnet_names = var.app_subnet_names + data_subnet_names = var.data_subnet_names } module "container" { @@ -35,7 +35,7 @@ module "container" { region = var.region ecs_execution_role_arn = module.security.ecs_execution_role_arn subnet_ids = module.networking.web_subnets_ids - sg_id = module.networking.ecs_sg_id + ecs_sg_id = module.networking.ecs_sg_id lb_tg_arn = module.networking.lb_tg_arn ecs_web_td_log_group_name = module.monitoring.ecs_web_td_log_group_name ecs_api_td_log_group_name = module.monitoring.ecs_api_td_log_group_name diff --git a/infrastructure/cloud/modules/container/ecs.tf b/infrastructure/cloud/modules/container/ecs.tf index 2e0527da..800fad56 100644 --- a/infrastructure/cloud/modules/container/ecs.tf +++ b/infrastructure/cloud/modules/container/ecs.tf @@ -51,7 +51,7 @@ resource "aws_ecs_service" "ecs_web_service" { network_configuration { subnets = var.subnet_ids - security_groups = [var.sg_id] + security_groups = [var.ecs_sg_id] assign_public_ip = true } @@ -102,7 +102,7 @@ resource "aws_ecs_service" "ecs_api_service" { network_configuration { subnets = var.subnet_ids - security_groups = [var.sg_id] + security_groups = [var.ecs_sg_id] assign_public_ip = true } diff --git a/infrastructure/cloud/modules/container/variables.tf b/infrastructure/cloud/modules/container/variables.tf index 4d775e11..4edc75c4 100644 --- a/infrastructure/cloud/modules/container/variables.tf +++ b/infrastructure/cloud/modules/container/variables.tf @@ -23,7 +23,7 @@ variable "subnet_ids" { type = list(string) } -variable "sg_id" { +variable "ecs_sg_id" { description = "Load Balancer Security Group ID" type = string } diff --git a/infrastructure/cloud/modules/networking/alb.tf b/infrastructure/cloud/modules/networking/alb.tf index 8ebcdbba..04b7c063 100644 --- a/infrastructure/cloud/modules/networking/alb.tf +++ b/infrastructure/cloud/modules/networking/alb.tf @@ -1,7 +1,7 @@ resource "aws_lb" "lb" { name = "${var.app_name}-lb-${var.environment}" subnets = local.web_subnets - security_groups = [aws_security_group.sg.id] + security_groups = [aws_security_group.lb_sg.id] internal = true load_balancer_type = "application" enable_http2 = true diff --git a/infrastructure/cloud/modules/networking/outputs.tf b/infrastructure/cloud/modules/networking/outputs.tf index 45f61ce9..5c25d098 100644 --- a/infrastructure/cloud/modules/networking/outputs.tf +++ b/infrastructure/cloud/modules/networking/outputs.tf @@ -1,7 +1,3 @@ -output "sg_id" { - value = aws_security_group.sg.id -} - output "lb_tg_arn" { value = aws_lb_target_group.lb_target_group.arn } diff --git a/infrastructure/cloud/modules/networking/securitygroup.tf b/infrastructure/cloud/modules/networking/securitygroup.tf index c53849ff..21eeb80e 100644 --- a/infrastructure/cloud/modules/networking/securitygroup.tf +++ b/infrastructure/cloud/modules/networking/securitygroup.tf @@ -1,59 +1,146 @@ -# Load Balancer Security Group -resource "aws_security_group" "sg" { +# +# Load Balancer Security Group +# +resource "aws_security_group" "lb_sg" { name = "${var.app_name}-lb-sg-${var.environment}" vpc_id = data.aws_vpc.vpc.id - description = "May change once Network Architecture has been finalized." - - ingress { - description = "Allow inbound HTTP traffic on port 80" - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } + description = "Security Group for the Application Load Balancer" - ingress { - description = "Accept traffic on port 8080" - from_port = 8080 - to_port = 8080 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + tags = { + Name = "${var.app_name}_lb_sg_${var.environment}" } +} - egress { - description = "Unrestricted" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } +# Load Balancer Ingress Rules. This will change once we get the public load balancer details from cloud team. +resource "aws_vpc_security_group_ingress_rule" "lb_sg_ingress_http_allow_80" { + security_group_id = aws_security_group.lb_sg.id + description = "Allow inbound HTTP traffic on port 80" + ip_protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_ipv4 = "0.0.0.0/0" +} - tags = { - Name = "${var.app_name}_sg_${var.environment}" - } +resource "aws_vpc_security_group_ingress_rule" "lb_sg_ingress_http_allow_8080" { + security_group_id = aws_security_group.lb_sg.id + description = "Allow inbound HTTP traffic on port 8080" + ip_protocol = "tcp" + from_port = 8080 + to_port = 8080 + cidr_ipv4 = "0.0.0.0/0" } +# Load Balancer Egress Rules +resource "aws_vpc_security_group_egress_rule" "lb_sg_egress_allow_to_ecs_sg" { + security_group_id = aws_security_group.lb_sg.id + referenced_security_group_id = aws_security_group.ecs_sg.id + description = "Allow all outbound traffic to ECS SG from Load Balancer SG" + ip_protocol = "-1" +} -# ECS Security Group +# +# ECS Security Group +# resource "aws_security_group" "ecs_sg" { name = "${var.app_name}-ecs-sg-${var.environment}" vpc_id = data.aws_vpc.vpc.id - description = "May change once Network Architecture has been finalized." - - ingress { - description = "Accept traffic on port 8080 and from specific Security Group" - from_port = 8080 - to_port = 8080 - protocol = "tcp" - cidr_blocks = null - security_groups = [aws_security_group.sg.id] + description = "Security Group for ECS services" + + tags = { + Name = "${var.app_name}_ecs_sg_${var.environment}" } +} + +# ECS Ingress Rules +# Remove ecs_sg_ingress_allow_icmp and ecs_sg_ingress_allow_ssh once the JASPER +# is publicly accessible. These ingress rules is for tesing SG-SG connectivity using +# EC2 Instance and EC2 Instance Connect Endpoint +resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_from_lb_sg" { + security_group_id = aws_security_group.ecs_sg.id + referenced_security_group_id = aws_security_group.lb_sg.id + description = "Allow all inbound traffic from ECS SG" + ip_protocol = -1 +} + +resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_from_lambda_sg" { + security_group_id = aws_security_group.ecs_sg.id + referenced_security_group_id = aws_security_group.lambda_sg.id + description = "Allow all inbound traffic from Lambda SG" + ip_protocol = -1 +} + +resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_icmp" { + security_group_id = aws_security_group.ecs_sg.id + description = "Allow inbound ICMP traffic to ECS SG to allow pinging the Lambda SG" + ip_protocol = "icmp" + from_port = -1 + to_port = -1 + cidr_ipv4 = "0.0.0.0/0" +} + +resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_ssh" { + security_group_id = aws_security_group.ecs_sg.id + description = "Allow inbound SSH traffic to ECS SG" + ip_protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_ipv4 = data.aws_vpc.vpc.cidr_block +} + +# ECS Egress Rules +resource "aws_vpc_security_group_egress_rule" "ecs_sg_egress_allow_to_anywhere" { + security_group_id = aws_security_group.ecs_sg.id + description = "Unrestricted" + ip_protocol = "-1" + cidr_ipv4 = "0.0.0.0/0" +} + +# +# Lambda Security Group +# +resource "aws_security_group" "lambda_sg" { + name = "${var.app_name}-lambda-sg-${var.environment}" + vpc_id = data.aws_vpc.vpc.id + description = "Security Group for Lambda functions" - egress { - description = "Unrestricted" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + tags = { + Name = "${var.app_name}_lambda_sg_${var.environment}" } } + +# Lambda Ingress Rules +# Remove lambda_sg_ingress_allow_icmp and lambda_sg_ingress_allow_ssh once the JASPER +# is publicly accessible. These ingress rules is for tesing SG-SG connectivity using +# EC2 Instance and EC2 Instance Connect Endpoint +resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_from_ecs_sg" { + security_group_id = aws_security_group.lambda_sg.id + referenced_security_group_id = aws_security_group.ecs_sg.id + description = "Allow all inbound traffic from ECS SG" + ip_protocol = -1 +} + +resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_icmp" { + security_group_id = aws_security_group.lambda_sg.id + description = "Allow inbound ICMP traffic to Lambda SG to allow pinging the ECS SG" + ip_protocol = "icmp" + from_port = -1 + to_port = -1 + cidr_ipv4 = "0.0.0.0/0" +} + +resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_ssh" { + security_group_id = aws_security_group.lambda_sg.id + description = "Allow inbound SSH traffic to Lambda SG" + ip_protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_ipv4 = data.aws_vpc.vpc.cidr_block +} + +# Lambda Egress Rules +resource "aws_vpc_security_group_egress_rule" "lambda_sg_egress_allow_to_anywhere" { + security_group_id = aws_security_group.lambda_sg.id + description = "Unrestricted" + ip_protocol = "-1" + cidr_ipv4 = "0.0.0.0/0" +} diff --git a/infrastructure/cloud/modules/networking/subnets.tf b/infrastructure/cloud/modules/networking/subnets.tf new file mode 100644 index 00000000..237d4a27 --- /dev/null +++ b/infrastructure/cloud/modules/networking/subnets.tf @@ -0,0 +1,49 @@ +data "aws_subnets" "all_subnets" { + filter { + name = "vpc-id" + values = [data.aws_vpc.vpc.id] + } +} + +data "aws_subnet" "subnets" { + for_each = toset(data.aws_subnets.all_subnets.ids) + id = each.key +} + +locals { + temp_web_subnets = { + for tag_value in var.web_subnet_names : + tag_value => [ + for subnet in data.aws_subnet.subnets : + subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + ] + } + + web_subnets = flatten([ + for subnets in local.temp_web_subnets : subnets + ]) + + temp_app_subnets = { + for tag_value in var.app_subnet_names : + tag_value => [ + for subnet in data.aws_subnet.subnets : + subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + ] + } + + app_subnets = flatten([ + for subnets in local.temp_app_subnets : subnets + ]) + + temp_data_subnets = { + for tag_value in var.data_subnet_names : + tag_value => [ + for subnet in data.aws_subnet.subnets : + subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + ] + } + + data_subnets = flatten([ + for subnets in local.temp_data_subnets : subnets + ]) +} diff --git a/infrastructure/cloud/modules/networking/variables.tf b/infrastructure/cloud/modules/networking/variables.tf index f6ebefa2..f3b53b07 100644 --- a/infrastructure/cloud/modules/networking/variables.tf +++ b/infrastructure/cloud/modules/networking/variables.tf @@ -23,12 +23,12 @@ variable "web_subnet_names" { type = list(string) } -# variable "api_subnet_names" { -# description = "List of Subnets for API" -# type = list(string) -# } +variable "app_subnet_names" { + description = "List of Subnets for App" + type = list(string) +} -# variable "db_subnet_names" { -# description = "List of Subnets for Database" -# type = list(string) -# } +variable "data_subnet_names" { + description = "List of Subnets for Data" + type = list(string) +} diff --git a/infrastructure/cloud/modules/networking/vpc.tf b/infrastructure/cloud/modules/networking/vpc.tf index 5341afa9..34f36776 100644 --- a/infrastructure/cloud/modules/networking/vpc.tf +++ b/infrastructure/cloud/modules/networking/vpc.tf @@ -1,68 +1,3 @@ data "aws_vpc" "vpc" { id = var.vpc_id } - -data "aws_subnets" "all_subnets" { - filter { - name = "vpc-id" - values = [data.aws_vpc.vpc.id] - } -} - -data "aws_subnet" "subnets" { - for_each = toset(data.aws_subnets.all_subnets.ids) - id = each.key -} - -locals { - temp_web_subnets = { - for tag_value in var.web_subnet_names : - tag_value => [ - for subnet in data.aws_subnet.subnets : - subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value - ] - } - - web_subnets = flatten([ - for subnets in local.temp_web_subnets : subnets - ]) - - # api_subnets = { - # for tag_value in var.api_subnet_names : - # tag_value => [ - # for subnet in data.aws_subnet.all_subnets : - # subnet.id if contains(subnet.tags["Name"], tag_value) - # ] - # } - - # db_subnets = { - # for tag_value in var.db_subnet_names : - # tag_value => [ - # for subnet in data.aws_subnet.all_subnets : - # subnet.id if contains(subnet.tags["Name"], tag_value) - # ] - # } -} - -# resource "aws_internet_gateway" "igw" { -# vpc_id = data.aws_vpc.vpc.id -# tags = { -# Name = "${var.app_name}_igw_${var.environment}" -# } -# } - -# resource "aws_route_table" "rt" { -# vpc_id = data.aws_vpc.vpc.id -# } - -# resource "aws_route" "route" { -# route_table_id = aws_route_table.rt.id -# destination_cidr_block = "0.0.0.0/0" -# gateway_id = aws_internet_gateway.igw.id -# } - -# resource "aws_route_table_association" "rt_assoc" { -# count = length(var.web_subnet_names) -# subnet_id = local.web_subnets[count.index] -# route_table_id = aws_route_table.rt.id -# }