cn-terraform / terraform-aws-ecs-alb

AWS ALB Terraform Module for ECS
https://registry.terraform.io/modules/cn-terraform/ecs-alb
Apache License 2.0
28 stars 29 forks source link

Can't forward HTTP and HTTPS traffic to the same container port #14

Open rzimmerman opened 2 years ago

rzimmerman commented 2 years ago

I'm running into an issue when I try to forward both HTTP and HTTPS to the same container port. As an example:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region = "us-west-1"
}

module "base-network" {
  source                                      = "cn-terraform/networking/aws"
  version                                     = "2.0.13"
  name_prefix                                 = "test-networking"
  vpc_cidr_block                              = "192.168.0.0/16"
  availability_zones                          = ["us-west-1a", "us-west-1b"]
  public_subnets_cidrs_per_availability_zone  = ["192.168.0.0/19", "192.168.32.0/19"]
  private_subnets_cidrs_per_availability_zone = ["192.168.128.0/19", "192.168.160.0/19"]
}

module "ecs-fargate" {
  source  = "cn-terraform/ecs-fargate/aws"
  version = "2.0.28"

  name_prefix = "demo-http-https-port"

  assign_public_ip = false

  container_image = "mendhak/http-https-echo:23"
  container_name = "demo-http-https-port"

  vpc_id = module.base-network.vpc_id
  private_subnets_ids = module.base-network.private_subnets_ids
  public_subnets_ids = module.base-network.public_subnets_ids

  environment = [
    {
      name = "HTTP_PORT"
      value = "3000"
    }
  ]

  port_mappings = [
    {
      containerPort = 3000
      hostPort = 3000
      protocol = "tcp"
    }
  ]

  lb_http_ports = {
    default_http = {
      listener_port     = 80
      target_group_port = 3000
    }
  }

  lb_https_ports = {
    default_https = {
      listener_port     = 443
      target_group_port = 3000
    }
  }
  default_certificate_arn = "MY_CERT_ARN"

}

When I apply this configuration, I see:

│ Error: [WARN] A duplicate Security Group rule was found on (sg-xxxxxxxxxx). This may be
│ a side effect of a now-fixed Terraform issue causing two security groups with
│ identical attributes but different source_security_group_ids to overwrite each
│ other in the state. See https://github.com/hashicorp/terraform/pull/2376 for more
│ information and instructions for recovery. Error: InvalidPermission.Duplicate: the specified rule "peer: sg-xxxxxxxxxx, TCP, from port: 3000, to port: 3000, ALLOW" already exists
│   status code: 400, request id: xxxxxxxxx
│
│   with module.ecs-fargate.module.ecs-fargate-service.aws_security_group_rule.ingress_through_https["3000"],
│   on .terraform/modules/ecs-fargate.ecs-fargate-service/main.tf line 161, in resource "aws_security_group_rule" "ingress_through_https":
│  161: resource "aws_security_group_rule" "ingress_through_https" {
│

I believe this is because this module creates an ingress rule for HTTP and HTTPS traffic (one each) that wind up being identical (main.tf:116):

resource "aws_security_group_rule" "ingress_through_http" {
  for_each          = var.http_ports
  security_group_id = aws_security_group.lb_access_sg.id
  type              = "ingress"
  from_port         = each.value.listener_port
  to_port           = each.value.listener_port
  protocol          = "tcp"
  cidr_blocks       = var.http_ingress_cidr_blocks
  prefix_list_ids   = var.http_ingress_prefix_list_ids
}

resource "aws_security_group_rule" "ingress_through_https" {
  for_each          = var.https_ports
  security_group_id = aws_security_group.lb_access_sg.id
  type              = "ingress"
  from_port         = each.value.listener_port
  to_port           = each.value.listener_port
  protocol          = "tcp"
  cidr_blocks       = var.https_ingress_cidr_blocks
  prefix_list_ids   = var.https_ingress_prefix_list_ids
}

There might be some way to deduplicate these rules or make them different in some semantic way to allow both rules to exist.

JaredDarling commented 1 year ago

@rzimmerman There is an aspect of this where you are asking the machine to do something technically invalid, as it's accepted that HTTP/HTTPS are barriers of separation between things and trying to force them into the same place is bound to cause errors.

I think what you are looking for here is a redirect rule for HTTP:

lb_http_ports = {
  redir = {
    type = "redirect"
    listener_port = 80
    target_group_port = 3000
    port = 443
    protocol = "HTTPS"
  }
}