我有一个 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 本身。由于我无法深入了解集成以了解它在做什么,因此我不确定我做错了什么。有谁知道问题是什么以及如何解决它?
实际上,这个设置存在一些问题。首先,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 协商。