Lambda@Edge多区域S3访问报错AuthorizationHeaderMalformed求助
我有两个S3存储桶,分别部署在eu-west-1和us-east-1区域,通过Lambda@Edge在源请求阶段实现动态路由:根据Lambda执行的边缘区域,将请求转发到对应区域的S3存储桶。对应区域的服务器通过curl请求同路径文件(文件内容为区域标识)时,eu-west-1区域的请求正常,但us-east-1区域的请求失败,报错如下:
$ curl -L cdn.mywebsite.cloud/welcome.html <?xml version="1.0" encoding="UTF-8"?> <Error><Code>AuthorizationHeaderMalformed</Code><Message>The authorization header is malformed; the region 'eu-west-1' is wrong; expecting 'us-east-1'</Message><Region>us-east-1</Region><RequestId>N93PW0DBYKD9J801</RequestId><HostId>PVi9MrrxoPmWMIV48Ao/eNogihE/TFFAGvPjkwvRfBZPHLl4Myys5SQ/3vxY1WYuLXmIBt5dJGE=</HostId></Error>
Lambda@Edge逻辑代码如下:
us_bucket = "cdn-origin-bucket-us-east-1-XXXX.s3.us-east-1.amazonaws.com" eu_bucket = "cdn-origin-bucket-eu-west-1-XXXX.s3.eu-west-1.amazonaws.com" # ap_bucket = "mybucket-ap.amazonaws.com" default_bucket = "cdn-origin-bucket-us-east-1-XXXX.s3.us-east-1.amazonaws.com" # Regions Mapping regions_mapping = { # NA "us-east-1": us_bucket, "us-east-2": us_bucket, "us-west-1": us_bucket, "us-west-2": us_bucket, "ca-central-1": us_bucket, # EU "eu-central-1": eu_bucket, "eu-central-1": eu_bucket, # 存在重复键,属于代码冗余 "eu-west-1": eu_bucket, "eu-west-2": eu_bucket, "eu-west-3": eu_bucket, "eu-north-1": eu_bucket, } def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] # Identify edge region lambda_region = context.invoked_function_arn.split(':')[3] # Get S3 bucket based on regions mapping domain_name = regions_mapping.get(lambda_region, default_bucket) # Update origin request object request['origin']['s3']['domainName'] = domain_name request['origin']['s3']['region'] = lambda_region request['headers']['host'] = [{'key': 'host', 'value': domain_name}] return request
我已参考AWS官方多区域Active-Active架构指南,但问题仍未解决,希望能排查原因,可提供更多配置细节。
问题根源
报错显示授权头使用了错误的区域(eu-west-1),但请求的是us-east-1的存储桶。核心原因是:CloudFront会预先为原始配置的S3存储桶生成签名授权头,当你在Lambda@Edge中动态切换到不同区域的S3存储桶时,原有授权头的区域信息与目标存储桶不匹配,导致S3拒绝请求。
另外代码中regions_mapping重复定义了"eu-central-1": eu_bucket,虽不影响当前问题,但建议清理冗余代码。
解决步骤
禁用CloudFront自动签名:
当使用Lambda@Edge动态路由多区域S3存储桶时,不能依赖CloudFront自动生成的S3授权头。需将CloudFront的源配置改为自定义源(而非默认的S3源类型),这样CloudFront不会自动添加授权头;若内容允许公开访问,也可直接设置S3存储桶为公有读权限。若需私有访问,需在Lambda@Edge中使用IAM角色生成目标存储桶的签名授权头(注意Lambda@Edge角色需具备跨区域S3访问权限)。
修正Lambda代码的区域设置:
当前代码中request['origin']['s3']['region']使用的是Lambda执行的边缘区域,但正确的做法是使用目标存储桶所在的区域(而非边缘区域)。修改代码如下:us_bucket = "cdn-origin-bucket-us-east-1-XXXX.s3.us-east-1.amazonaws.com" eu_bucket = "cdn-origin-bucket-eu-west-1-XXXX.s3.eu-west-1.amazonaws.com" default_bucket = "cdn-origin-bucket-us-east-1-XXXX.s3.us-east-1.amazonaws.com" # 存储桶与对应区域的映射 bucket_region_mapping = { us_bucket: "us-east-1", eu_bucket: "eu-west-1" } regions_mapping = { # NA "us-east-1": us_bucket, "us-east-2": us_bucket, "us-west-1": us_bucket, "us-west-2": us_bucket, "ca-central-1": us_bucket, # EU "eu-central-1": eu_bucket, "eu-west-1": eu_bucket, "eu-west-2": eu_bucket, "eu-west-3": eu_bucket, "eu-north-1": eu_bucket, } def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] lambda_region = context.invoked_function_arn.split(':')[3] domain_name = regions_mapping.get(lambda_region, default_bucket) # 获取目标存储桶对应的正确区域 target_region = bucket_region_mapping.get(domain_name, "us-east-1") request['origin']['s3']['domainName'] = domain_name request['origin']['s3']['region'] = target_region request['headers']['host'] = [{'key': 'host', 'value': domain_name}] return request清除CloudFront缓存:
修改Lambda代码和CloudFront配置后,需手动清除CloudFront缓存,确保新配置生效。
额外检查点
- 确认存储桶权限:若使用自定义源,需确保存储桶允许CloudFront IP范围访问,或Lambda角色具备目标存储桶的访问权限。
- 确认Lambda@Edge角色权限:需允许Lambda访问CloudFront请求上下文,以及跨区域S3存储桶的访问权限。
内容的提问来源于stack exchange,提问作者foroozf001

