如何在API Gateway前面添加CloudFront

46
API 网关(APIG)虽然使用了 CloudFront(CF),但不支持 CDN 边缘缓存。当我将 CF 分发配置为使用 APIG 作为自定义源时,会出现权限拒绝错误。
如何配置 CF 来解决此问题?
6个回答

81

在API网关(APIG)通过其内部使用CloudFront(CF)支持边缘缓存之前,我想出了一个解决方法。

确实可以将CF分发放在APIG的前面,窍门是强制使用"Viewer Protocol Policy"的HTTPS only选项并且不要转发HOST标头,因为APIG需要SNI。

我将我的CF的"默认缓存行为设置"设置为不转发任何标头,并强制使用"Viewer Protocol Policy"的"HTTPS Only"选项。它有效了。希望这能帮助其他人。

以下是一个CloudFormation资源对象,它具有所有所需的配置(注意:我使用<stage>--<app name>约定作为StackName):

CloudFront:  
  Type: AWS::CloudFront::Distribution
  Properties:
    DistributionConfig:
      Enabled: true
      IPV6Enabled: true
      HttpVersion: http2
      Comment: !Join [ '--', [!Ref 'AWS::StackName', ' Cloud Front']]
      Aliases: [!Ref CloudFrontCname]
      ViewerCertificate:
        AcmCertificateArn: !Ref AcmCertificateArn
        SslSupportMethod: sni-only
        MinimumProtocolVersion: TLSv1.1_2016
      Origins:
      - Id: APIGOrigin
        DomainName: !Sub
          - ${apigId}.execute-api.${AWS::Region}.amazonaws.com
          - { apigId: !Ref ApiGatewayLambdaProxy }
        OriginPath: !Sub
          - /${Stage}
          - { Stage: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] }
        CustomOriginConfig:
          # HTTPPort: 80
          HTTPSPort: 443
          OriginProtocolPolicy: https-only
        OriginCustomHeaders:
          - HeaderName: 'Verify-From-Cf'
            HeaderValue: !Ref VerifyFromCfHeaderVal
      DefaultCacheBehavior:
        AllowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
        CachedMethods: ["GET", "HEAD", "OPTIONS"]
        ForwardedValues:
          Headers:
          - Access-Control-Request-Headers
          - Access-Control-Request-Method
          - Origin
          - Authorization
          # - Host APIG needs to use SNI
          QueryString: true
        TargetOriginId: APIGOrigin
        ViewerProtocolPolicy: https-only
        Compress: true
        DefaultTTL: 0
      CustomErrorResponses:
      - ErrorCachingMinTTL: 0
        ErrorCode: 400
      - ErrorCachingMinTTL: 1
        ErrorCode: 403
      - ErrorCachingMinTTL: 5
        ErrorCode: 500
DNSARecord:    
  Type: AWS::Route53::RecordSet
  Properties:
    Comment: !Ref 'AWS::StackName'
    Name: !Ref CloudFrontCname
    Type: A
    HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']]
    AliasTarget:
      HostedZoneId: !Ref Route53HostedZoneId
      DNSName: !GetAtt CloudFront.DomainName
DNSAAAARecord:    
  Type: AWS::Route53::RecordSet
  Properties:
    Comment: !Ref 'AWS::StackName'
    Name: !Ref CloudFrontCname
    Type: AAAA
    HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']]
    AliasTarget:
      HostedZoneId: !Ref Route53HostedZoneId
      DNSName: !GetAtt C

2018年末的更新

  • CloudFormation终于支持设置SSL协议版本:MinimumProtocolVersion:TLSv1.1_2016
  • 我已将此(以及许多其他)最佳实践纳入了一个开源项目中:aws-blueprint

1
你能用路径模式吗?我有一个CloudFront分发设置,其中包含两个源:一个S3存储桶用于我的静态文件和一个APIG API;但是我的APIG的PathPattern没有触发。我认为这是因为我没有匹配文件名或类型,而是匹配了特定的路径。 - Josh Wulf
12
API网关在幕后使用CloudFront。如果你将另一个CloudFront分配指向你的API网关端点,CloudFront会阻止请求,因为它具有反向回路检查。也就是说,你不能将CloudFront指向CloudFront。在AWS re:Invent 2015上,我被一位AWS工程师告知这种反向回路检查会得到放宽,以允许你将CloudFront分配指向API网关。但我还没有验证它是否有效。 - Jamey
3
首先,是的,只需删除主机头就可以使此功能正常工作。我很好奇是否有人尝试过将ApiGateway设置为IAM身份验证。我遇到了签名错误。它期望的是针对host=apigateway host而不是host=cloudfront host的签名。 - Atif
18
我无法形容知道不要转发主机标头可以节省多少时间,但这确实可以帮助人们节约时间。 - Guy S
手动为 API 网关端点设置 CloudFront 似乎会生成随机的 500 响应(约为 20%)。即使 TTL 为 0,这些请求也无法到达 API 端点。其他人也能看到这个问题吗? - Luis Cazacu
显示剩余7条评论

13

补充之前的回答:

行为路径模式必须与“真实”路径匹配是非常重要的。


如果API终端节点是<id>.execute-api.<region>.amazonaws.com/stage-name/my-api

而原始域+路径是<id>.execute-api.<region>.amazonaws.com/stage-name

则行为路径模式必须是my-apimy-api/*my-api/something等。


我不知道为什么,但我认为路径模式可以用作别名,例如:

https://www.example.com/random-name(路径模式random-name)解析为在源中设置的域+路径,例如<id>.execute-api.<region>.amazonaws.com/stage-name

但事实并非如此。


1
在我的情况下,我使用了一个匹配路径模式的API网关资源来实现。API网关:<id>.execute-api.<region>.amazonaws.com/stage-name/api/endpoint1,源+路径:<id>.execute-api.<region>.amazonaws.com/stage-name,路径模式:/api/*,最终端点:https://<id>.cloudfront.net/api/endpoint1,虽然与此无关,但我也发现这个回答对于传递标头非常有用:https://dev59.com/i6bja4cB1Zd3GeqPnN80#47380572 - Cadell Christo
2
这是 AWS CDK 中的一个实现:https://github.com/cadbox1/backend-frontend-aws-cdk/blob/master/lib/lambda-cdk-stack.ts - Cadell Christo
这真的很有帮助,谢谢! 我不得不从我的CloudFront行为配置中删除originPath,并确保我的pathPattern与API网关中的阶段名称匹配。 - Dakota Hipp

4
如果API网关返回403错误,并提示以下信息:

Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header.

也可能是由于原始终端点不正确所致。"API Gateway将所有对不存在路径的错误视为403权限被拒绝错误,而不是404未找到错误。"(参见支持线程)。
我遇到了这个错误,以为我没有正确转发授权标头,但实际上是我错误地配置了原始路径。

如果调用方没有列出权限,通常会将“找不到资源”视为“访问被拒绝”。否则,您就会泄露有关资源名称的信息,这对攻击者具有潜在价值。 - jarmod

3
我想在这里重申一下,针对那些正在遵循 AWS 高级支持知识中心指南的人,
“如何使用自己的 CloudFront 分发设置 API Gateway?” https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudfront-distribution/ 如果您正在使用 AWS 控制台来设置 CloudFront 分发,则根本原因是您将“基于选定请求标头缓存”设置为“全部”。

enter image description here

将其设置为None或在白名单中排除Host头部将解决问题。

0

随着2017年11月API Gateway区域端点的推出,我相信现在最优的做法是在其上使用CloudFront分发。有关从边缘优化API转移到区域API并设置CloudFront分发的详细说明,请参见:

AWS API Gateway应防止使用TLS v1


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接