如何在AWS WAF/ALB中自动插入源IP的GeoIP地域至自定义请求头?
解决方案:将请求源IP的GeoIP地域信息插入自定义请求头
首先明确:AWS WAF v2的custom_request_handling中的insert_header确实仅支持静态字符串值,无法直接引用GeoIP这类动态变量,你提供的Terraform配置写法无法实现需求。以下是两种可行的实现方式:
方案1:CloudFront + Lambda@Edge(适用于使用CloudFront作为CDN的场景)
CloudFront内置了请求源IP的GeoIP信息,可以通过Lambda@Edge在Viewer Request阶段获取这些信息,并插入自定义请求头后转发到后端。
Terraform配置示例
- 编写Lambda函数代码(用于插入GeoIP头):
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] # 从CloudFront内置变量中获取国家代码 country_code = request['headers'].get('cloudfront-viewer-country', [{'value': 'UNKNOWN'}])[0]['value'] # 插入自定义请求头 request['headers']['x-country-code'] = [{'key': 'X-Country-Code', 'value': country_code}] return request
- Terraform配置Lambda@Edge和CloudFront:
# 创建Lambda函数(需部署在us-east-1区域) resource "aws_lambda_function" "geoip_header_insert" { filename = "geoip-handler.zip" function_name = "GeoIP-Header-Insert" role = aws_iam_role.lambda_edge_role.arn handler = "index.lambda_handler" runtime = "python3.12" source_code_hash = filebase64sha256("geoip-handler.zip") publish = true # 必须发布版本才能用于Lambda@Edge } # Lambda@Edge所需的IAM角色 resource "aws_iam_role" "lambda_edge_role" { name = "Lambda-Edge-GeoIP-Role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] } } ] }) } resource "aws_iam_role_policy_attachment" "lambda_edge_basic_execution" { role = aws_iam_role.lambda_edge_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } # CloudFront分发配置,关联Lambda@Edge resource "aws_cloudfront_distribution" "my_distribution" { # 其他CloudFront基础配置(略) default_cache_behavior { # 其他缓存行为配置(略) lambda_function_association { event_type = "viewer-request" lambda_arn = "${aws_lambda_function.geoip_header_insert.arn}:${aws_lambda_function.geoip_header_insert.version}" include_body = false } } }
方案2:ALB + Lambda函数(适用于直接使用ALB暴露服务的场景)
ALB本身不支持直接插入GeoIP信息,但可以通过Lambda函数集成,在请求到达目标组前处理并插入自定义头。
Terraform配置示例
- Lambda函数代码(需引入GeoIP库,如maxminddb,或调用AWS Location服务获取IP地理信息):
import maxminddb import os # 假设已将GeoIP数据库打包到Lambda部署包中 GEOIP_DB_PATH = os.path.join(os.path.dirname(__file__), 'GeoLite2-Country.mmdb') def lambda_handler(event, context): # 获取请求源IP source_ip = event['requestContext']['elb']['sourceIp'] # 查询GeoIP信息 with maxminddb.open_database(GEOIP_DB_PATH) as reader: try: response = reader.get(source_ip) country_code = response['country']['iso_code'] if response else 'UNKNOWN' except: country_code = 'UNKNOWN' # 修改请求头 event['headers']['x-country-code'] = country_code # 返回修改后的请求,转发到目标组 return { 'statusCode': 200, 'headers': event['headers'], 'body': event['body'], 'isBase64Encoded': event['isBase64Encoded'] }
- Terraform配置ALB与Lambda集成:
# 创建Lambda函数 resource "aws_lambda_function" "geoip_header_insert_alb" { filename = "geoip-alb-handler.zip" function_name = "GeoIP-ALB-Header-Insert" role = aws_iam_role.alb_lambda_role.arn handler = "index.lambda_handler" runtime = "python3.12" source_code_hash = filebase64sha256("geoip-alb-handler.zip") } # ALB Lambda集成所需IAM角色 resource "aws_iam_role" "alb_lambda_role" { name = "ALB-Lambda-GeoIP-Role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } } ] }) } resource "aws_iam_role_policy_attachment" "alb_lambda_basic_execution" { role = aws_iam_role.alb_lambda_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } # 允许ALB调用Lambda resource "aws_lambda_permission" "alb_invoke_lambda" { statement_id = "AllowALBInvoke" action = "lambda:InvokeFunction" function_name = aws_lambda_function.geoip_header_insert_alb.function_name principal = "elasticloadbalancing.amazonaws.com" source_arn = aws_lb_target_group.my_target_group.arn } # 配置Lambda类型的目标组 resource "aws_lb_target_group" "my_target_group" { name = "geoip-target-group" port = 80 protocol = "HTTP" vpc_id = aws_vpc.my_vpc.id target_type = "lambda" lambda_function { arn = aws_lambda_function.geoip_header_insert_alb.arn } } # ALB监听器指向该目标组 resource "aws_lb_listener" "my_listener" { load_balancer_arn = aws_lb.my_alb.arn port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = aws_lb_target_group.my_target_group.arn } }
注意:方案2中使用的GeoIP数据库需自行下载(如MaxMind的GeoLite2)并打包到Lambda部署包中,也可改用AWS Location Service的IP地理定位API获取信息。
内容的提问来源于stack exchange,提问作者Orabïg
相关产品推荐
相关产品推荐

