Home > Enterprise >  AWS Network Load Balancer healthchecks not behaving as expected
AWS Network Load Balancer healthchecks not behaving as expected

Time:06-16

I have an issue where the Network Load Balancer healthcheck is showing targets as unhealthy. The targets are AWS Fargate instances.

The healthcheck port and application port is 8080. Everything works perfectly if I place the ECS Fargate cluster service to a security group with 0.0.0.0/0 to port 8080 open. Healthchecks are successful and the application works.

The problem is that I do not want the application to be accessed from the public internet. So when I try to restrict access with ip-addresses I get issues. Because the healthcheck port and application access all happen on port 8080 I have to restrict it so that the healthcheck works only from the private ip-address of the Network load balancer.

According to AWS documentation this should be possible: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-register-targets.html#target-security-groups According to this documentation it should be possible to add the VPC CIDR to the security group rules. I did that and the healthchecks fail. I also tried to add the private ip-addresses of the network load balancer in to the security group and they fail as well.

I am completely out of ideas.

Could someone verify that this is even possible to limit healthcheck traffic to the private ip address range of the Network Load Balancer?

Here is most of the Terraform code:

## security groups. This is the security group that the Fargate cluster service is placed in

resource "aws_security_group" "nlb_sg_health_test" {
  vpc_id = aws_vpc.test_vpc.id

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = [
      "10.167.0.0/16"
    ]
    description = "Allow connection from NLB to app healthcheck"
  }

  egress {
    protocol    = "-1"
    from_port   = 0
    to_port     = 0
    cidr_blocks = ["0.0.0.0/0"]
  }

}

resource "aws_vpc" "test_vpc" {
    cidr_block                       = "10.167.0.0/16"

    tags = {
     "Name" = "${var.prefix}"
   }
}

## private subnets

resource "aws_subnet" "test_eu_west_private_1a" {
  cidr_block              = "10.167.10.0/24"
  map_public_ip_on_launch = false
  vpc_id                  = aws_vpc.test_vpc.id
  availability_zone       = "${data.aws_region.current.name}a"

  tags = {
   "Name" = "${var.prefix} private eu-west-1a"
 }
}


resource "aws_subnet" "test_eu_west_private_1b" {
  cidr_block              = "10.167.20.0/24"
  map_public_ip_on_launch = false
  vpc_id                  = aws_vpc.test_vpc.id
  availability_zone       = "${data.aws_region.current.name}b"

  tags = {
   "Name" = "${var.prefix} private eu-west-1b"
 }
}

resource "aws_route_table" "test_private" {
  vpc_id = aws_vpc.test_vpc.id

  tags = {
   "Name" = "${var.prefix} private a"
 }
}

resource "aws_route_table_association" "test_main_a" {
  subnet_id      = aws_subnet.test_eu_west_private_1a.id
  route_table_id = aws_route_table.test_private.id
}

resource "aws_route_table_association" "test_main_b" {
  subnet_id      = aws_subnet.test_eu_west_private_1b.id
  route_table_id = aws_route_table.test_private.id
}

## public subnets

resource "aws_subnet" "test_eu_west_public_1a" {
  cidr_block              = "10.167.1.0/24"
  map_public_ip_on_launch = true
  vpc_id                  = aws_vpc.test_vpc.id
  availability_zone       = "${data.aws_region.current.name}a"

  tags = {
   "Name" = "${var.prefix} public eu-west-1a"
 }
}


resource "aws_subnet" "test_eu_west_public_1b" {
  cidr_block              = "10.167.2.0/24"
  map_public_ip_on_launch = true
  vpc_id                  = aws_vpc.test_vpc.id
  availability_zone       = "${data.aws_region.current.name}b"

  tags = {
   "Name" = "${var.prefix} public eu-west-1b"
 }
}

resource "aws_route_table" "test_public" {
  vpc_id = aws_vpc.test_vpc.id

  tags = {
   "Name" = "${var.prefix} public a"
 }
}

resource "aws_route_table_association" "test_public_a" {
  subnet_id      = aws_subnet.test_eu_west_public_1a.id
  route_table_id = aws_route_table.test_public.id
}

resource "aws_route_table_association" "test_public_b" {
  subnet_id      = aws_subnet.test_eu_west_public_1b.id
  route_table_id = aws_route_table.test_public.id
}


resource "aws_internet_gateway" "test_igw" {
  vpc_id = aws_vpc.test_vpc.id

  tags = {
    Name = "${var.prefix} IGW"
  }
}

resource "aws_route" "public_internet_access_a" {
  route_table_id            = aws_route_table.test_public.id
  destination_cidr_block    = "0.0.0.0/0"
  gateway_id                = aws_internet_gateway.test_igw.id
}

resource "aws_route" "public_internet_access_b" {
  route_table_id            = aws_route_table.test_public.id
  destination_cidr_block    = "0.0.0.0/0"
  gateway_id                = aws_internet_gateway.test_igw.id
}

## NLB

resource "aws_lb_target_group" "test_http_nlb_target_group" {
  name     = "${var.prefix}-target-group-http"
  port     = 8080
  protocol = "TCP"
  vpc_id   = aws_vpc.test_vpc.id
  target_type = "ip"
  preserve_client_ip = true
}

resource "aws_lb_listener" "test_https_nlb_listener" {
  load_balancer_arn = aws_lb.test_nlb.arn
  port              = 443
  protocol          = "TLS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = aws_acm_certificate.app.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.test_http_nlb_target_group.arn
  }
}

resource "aws_lb" "test_nlb" {
  name               = "${var.prefix}-nlb"
  load_balancer_type = "network"

  subnet_mapping {
    subnet_id     = aws_subnet.test_eu_west_public_1a.id
    allocation_id = aws_eip.nlb_public_ip_a.id
  }

  subnet_mapping {
    subnet_id     = aws_subnet.test_eu_west_public_1b.id
    allocation_id = aws_eip.nlb_public_ip_b.id
  }
}

## ECS

resource "aws_ecs_service" "main" {
  name            = "${var.prefix}"
  cluster         = aws_ecs_cluster.test_cluster.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 1
  launch_type     = "FARGATE"
  enable_execute_command = true
  health_check_grace_period_seconds = 300

  network_configuration {
    security_groups  = [
      aws_security_group.nlb_sg_health_test.id
    ]
    subnets          = [
      aws_subnet.test_eu_west_private_1a.id,
      aws_subnet.test_eu_west_private_1b.id
    ]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.test_http_nlb_target_group.id
    container_name   = var.test_frontend_container_name
    container_port   = 8080
  }
}

CodePudding user response:

Have you tried adding the security group of the load balancer in the inbound roules of your fargate cluster security group?

CodePudding user response:

From AWS documentation

For Scheme, choose Internet-facing or Internal. An internet-facing load balancer routes requests from clients to targets over the internet. An internal load balancer routes requests to targets using private IP addresses.

  • Related