API Gateway V2 与应用程序负载均衡器的私有集成始终返回 503

问题描述 投票:0回答:1

我有一个 API Gateway V2,通过 Terraform 中定义的 VPC 链接连接到应用程序负载均衡器,如下所示:

resource "aws_apigatewayv2_vpc_link" "alb_connection" {
  name               = "${local.full_name}-vpc-link"
  security_group_ids = [aws_security_group.alb_sg.id]
  subnet_ids         = data.aws_subnets.web.ids
  tags               = module.network_label.tags
}

resource "aws_cognito_resource_server" "alb_connection" {
  identifier = "${var.environment}.${var.area}.${var.application}"
  name       = "${var.environment}-${local.full_name}-rs"

  dynamic "scope" {
    for_each = var.scopes

    content {
      scope_name        = scope.value
      scope_description = "Allow ${scope.value} access to ${local.full_name}/${var.area}"
    }
  }

  user_pool_id = one(data.aws_cognito_user_pools.selected.ids)
}

resource "aws_apigatewayv2_authorizer" "alb_connection" {
  name             = "http-api-request-authorizer"
  api_id           = data.aws_apigatewayv2_api.gateway.id
  authorizer_type  = "REQUEST"
  identity_sources = ["$request.header.Authorization"]
  authorizer_uri   = data.aws_lambda_function.authorizer.invoke_arn

  enable_simple_responses           = true
  authorizer_payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "alb_connection" {
  api_id    = data.aws_apigatewayv2_api.gateway.id
  route_key = "ANY /${var.area}/{proxy+}"
  target    = "integrations/${aws_apigatewayv2_integration.alb_connection.id}"

  authorizer_id      = aws_apigatewayv2_authorizer.alb_connection.id
  authorization_type = "CUSTOM"
  # authorization_scopes = ["${aws_cognito_resource_server.alb_connection.identifier}/${each.value}"]
}

resource "aws_apigatewayv2_integration" "alb_connection" {
  api_id                 = data.aws_apigatewayv2_api.gateway.id
  description            = "Integration connecting ${var.api_gateway_name} to ${local.full_name}"
  integration_type       = "HTTP_PROXY"
  integration_uri        = aws_lb_listener.this.arn
  payload_format_version = "1.0" # Required for HTTP_PROXY integrations (private integrations)

  integration_method = "ANY"
  connection_type    = "VPC_LINK"
  connection_id      = aws_apigatewayv2_vpc_link.alb_connection.id

  request_parameters = {
    "append:header.authforintegration" = "$context.authorizer.authorizerResponse"
  }

  response_parameters {
    status_code = 403
    mappings = {
      "append:header.auth" = "$context.authorizer.authorizerResponse"
    }
  }
}

resource "aws_lb" "alb" {

  # Set the basic fields of the load balancer
  name                       = "${local.full_name}-alb-${random_string.id.result}"
  internal                   = true
  load_balancer_type         = "application"
  enable_deletion_protection = false
  security_groups            = [aws_security_group.alb_sg.id]
  subnets                    = data.aws_subnets.web.ids

  # Setup logging for access
  access_logs {
    bucket  = data.aws_s3_bucket.audit.id
    prefix  = local.full_path
    enabled = true
  }

  # Setup connection logs
  connection_logs {
    bucket  = data.aws_s3_bucket.audit.id
    prefix  = local.full_path
    enabled = true
  }

  tags = module.alb_group_label.tags

  # Ensure a new instance of this load balancer is created before the old one is destroyed to avoid downtime
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_s3_bucket_policy" "allow_log_access" {
  bucket = data.aws_s3_bucket.audit.id
  policy = data.aws_iam_policy_document.alb_access_policy.json
}

resource "aws_lb_target_group" "target_group" {
  name        = "${local.full_name}-tg-${random_string.id.result}"
  target_type = "ip"
  protocol    = "HTTP"
  port        = var.service_port
  vpc_id      = data.aws_vpc.main.id

  tags = module.alb_group_label.tags

  slow_start = var.slow_start
  health_check {
    enabled             = true
    port                = var.service_port
    interval            = 30
    timeout             = 5
    protocol            = "HTTP"
    path                = var.health_check
    matcher             = "200"
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }

  # Ensure a new instance of this target group is created before the old one is destroyed to avoid downtime
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb_listener" "this" {
  load_balancer_arn = aws_lb.alb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = aws_acm_certificate.ssl_certificate.arn
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.target_group.arn
  }
  tags = module.alb_group_label.tags

  # Need to ensure that the listener is destroyed before the target group or we won't be able to destroy the target
  # group at all.
  lifecycle {
    create_before_destroy = true
    replace_triggered_by = [
      aws_lb_target_group.target_group
    ]
  }
}

我已经能够被部署了。当我查看 API 网关时,我可以看到路由、授权者和集成,并且它们似乎相互指向。 VPC 链接也会出现。此外,我的 ALB 目标是显示绿色和健康。向我的授权者发出的请求会返回正确的响应。我可以看到这样显示的 API 网关日志。

但是,我提出的任何请求实际上都没有涉及到 ALB 本身。我的 S3 存储桶中没有存储任何访问或连接日志,也没有任何 VPC 流日志表明移动到 ALB 本身。由于我无法深入了解集成以了解它在做什么,因此我不确定我做错了什么。有谁知道问题是什么以及如何解决它?

amazon-web-services terraform aws-api-gateway aws-application-load-balancer
1个回答
0
投票

实际上,这个设置存在一些问题。首先,ALB 和 VPC 链接是在同一个安全组中创建的,该安全组没有允许其向自身发送请求的规则。解决方案是将 VPC 链路和 ALB 的安全组分开,并确保每个安全组都有权限向链下发送请求:

# Create the security group for the load balancer
resource "aws_security_group" "alb_sg" {
  #checkov:skip=CKV2_AWS_5: "Ensure that Security Groups are attached to another resource"
  name        = "${local.full_name}-alb-sg"
  description = "Security group for the ${local.full_name} load balancer"
  vpc_id      = data.aws_vpc.main.id
  tags        = module.security_group_label.tags
}

# Allow HTTP inbound traffic from the VPC link to our load balancer
resource "aws_vpc_security_group_ingress_rule" "api_gateway_ingress" {
  security_group_id = aws_security_group.alb_sg.id
  description       = "Allow the load balancer to receive HTTP requests from the API Gateway"
  from_port         = 443
  to_port           = 443
  ip_protocol       = "tcp"
  cidr_ipv4         = data.aws_vpc.main.cidr_block
  tags              = module.security_group_label.tags
}

# Allow HTTP outbound traffic from our load balancer to our service
resource "aws_vpc_security_group_egress_rule" "allow_service_access" {
  security_group_id            = aws_security_group.alb_sg.id
  description                  = "Allow the load balancer to forward requests to the ${local.full_name} service"
  from_port                    = var.service_port
  to_port                      = var.service_port
  ip_protocol                  = "tcp"
  referenced_security_group_id = aws_security_group.service_sg.id
  tags                         = module.security_group_label.tags
}

# Create the security group for the VPC link
resource "aws_security_group" "vpc_link_sg" {
  #checkov:skip=CKV2_AWS_5: "Ensure that Security Groups are attached to another resource"
  name        = "${local.full_name}-vpc-link-sg"
  description = "Security group for the ${local.full_name} VPC link"
  vpc_id      = data.aws_vpc.main.id
  tags        = module.security_group_label.tags
}

# Allow HTTP inbound traffic from the internet to our load balancer
resource "aws_vpc_security_group_ingress_rule" "vpc_link_ingress" {
  security_group_id = aws_security_group.vpc_link_sg.id
  description       = "Allow the VPC link to receive HTTP requests from the API Gateway"
  from_port         = 443
  to_port           = 443
  ip_protocol       = "tcp"
  cidr_ipv4         = data.aws_vpc.main.cidr_block
  tags              = module.security_group_label.tags
}

# Allow HTTP traffic down to the load balancer from the VPC link
resource "aws_vpc_security_group_egress_rule" "vpc_link_access" {
  security_group_id            = aws_security_group.vpc_link_sg.id
  description                  = "Allow the VPC link to forward requests to the ${local.full_name} load balancer"
  from_port                    = 443
  to_port                      = 443
  ip_protocol                  = "tcp"
  referenced_security_group_id = aws_security_group.alb_sg.id
  tags                         = module.security_group_label.tags
}

但是,仅此还不足以解决问题。 API 网关集成和 ALB 之间仍然存在 TLS 不匹配的情况。为了解决这个问题,我在 API 网关集成中添加了一个块:

tls_config {
  server_name_to_verify = local.service_discovery_domain
}

映射到我们有权访问的公共 DNS 主机的子域。然后,我们在具有相同域名的私有 DNS 主机的子域中添加 CNAME 记录:

resource "aws_route53_record" "alb" {
  zone_id = data.aws_route53_zone.private_dns.zone_id
  name    = local.service_discovery_domain
  type    = "CNAME"
  ttl     = 300
  records = [aws_lb.alb.dns_name]
}

这使得 AWS 能够正确地将请求从 API 网关路由到 ALB,并成功进行 TLS 协商。

© www.soinside.com 2019 - 2024. All rights reserved.