8

Is it possible to use IAM Roles Authentication to an API Gateway endpoint via Cloudfront?

Here is a basic use case:

  • /api -> API Gateway
  • /* -> S3 HTML/Javascript

I've been able to get this working without Authentication similar to the following setup: https://www.codeengine.com/articles/process-form-aws-api-gateway-lambda/

How do you add CloudFront in front of API Gateway

Of particular note is to NOT forward the HOST header from Cloudfront...

When I enable IAM Authentication in the API, I receive the following response:

OPTIONS: 200

POST: 403

{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'POST\n/test/create\n\naccept:application/json\ncontent-type:application/json\nhost:*****.execute-api.ap-****-2.amazonaws.com\nx-amz-date:20170328T044253Z\n\naccept;content-type;host;x-amz-date\na57656a9def890d9de2b637789f7e5917f4b2823765ae0122087d08f89a97380'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20170328T044253Z\n20170328/ap-southeast-2/execute-api/aws4_request\ncae7fd6b4cabd593ad2ec6ce7091c94dc8cba306e81da95d567873eea8f981a5'\n"}

The host in the Canonical String is that of the API Gateway...

asked Mar 28, 2017 at 4:59
3
  • 1
    You'll need to sign the request as if you were going to send it directly to API Gateway... then change the hostname in the request after signing. It's counter-intuitive, perhaps, but since CloudFront is going to change it back, again, this seems like the only solution. Commented Mar 28, 2017 at 13:00
  • Possible duplicate of Does API Gateway behind CloudFront not support AWS_IAM authentication? Commented Jan 22, 2019 at 16:23
  • @HungryTuna That question was asked Feb 15 '18... Can you please elaborate? Commented Jan 22, 2019 at 23:08

3 Answers 3

3

As noted in comments, you'll run into difficulties due to the change of the HOST header. In theory signing the request with the API Gateway exceute-api domain as the value of HOST in theory should work, assuming CloudFront is not changing the payload in any other way.

answered Mar 28, 2017 at 16:11
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Bob, I can confirm that by manually setting the Host header to that of the API gateway endpoint, prior to signing, sig4 authentication works via Cloudfront.
CloudFront generates it's own x-amz-date request header and headers that begin with X-Amz- cannot be forwarded to your origin so although the Host header can be changed for signing, the request will fail if the client request is not within the same second as the origin request as the value for the x-amz-date request header will not match the header's value at the time of signing the request. The 'date' header will not work as an alternative to 'x-amz-date' as the sigV4 process only checks for a 'date' header if there is no 'x-amz-date' provided.
@KennethRoryDunn Unless you forward all headers (using "*" in CloudFormation / serverless .yml Headers ForwardValues). But in this case you lose caching.
@ElFitz, Headers that begin with X-Amz-* are part of the list of headers CloudFront cannot forward to the origin. docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/…
@KennethRoryDunn Maybe that's what the documentation says. But setting to forwarding all headers, using *, I could nonetheless authenticate via Cognito Federated Identities with an API Gateway behind Cloudfront. Cloudfront caching wouldn't work anymore, because it used the headers, one of which is a timestamp, and another a signature based on that timestamp, as caching keys, but the forwarding did work. You can't specifically forward X-Amz-* headers, but forwarding all headers will get those forwarded as well.
1

If you want to support API Gateway that has AWS_IAM authentication when it is behind CloudFront, your best option is to create a custom domain and use it in your CloudFront distribution and then set the same custom domain in your API Gateway and make sure to map it to your API Gateway resource.

Also Make sure to configure the Origin request policy to use a predefined policy called "AllViwer" which is basically a Policy to forward all parameters in viewer requests.

This a YML file that basically creates a CloudFront Distribution with a custom domain and assigns "AllViwer" in Origin request policy, and uses a custom caching policy that only consider the URL along with the query strings as part of the caching key.

Conditions:
 IsProd:
 Fn::Equals:
 - ${self:provider.stage}
 - prod
 
Resources:
 CloudFrontCachePolicy:
 Type: AWS::CloudFront::CachePolicy
 Properties:
 CachePolicyConfig:
 Name: "QueryStringOnlyCachePolicy"
 Comment: "Cache policy that considers only the query string"
 MinTTL: 86400 # 1 day
 MaxTTL: 31536000 # 1 year
 DefaultTTL: 7884000 # 3 months
 ParametersInCacheKeyAndForwardedToOrigin:
 CookiesConfig:
 CookieBehavior: "none"
 HeadersConfig:
 HeaderBehavior: "none"
 QueryStringsConfig:
 QueryStringBehavior: "all"
 EnableAcceptEncodingGzip: false
 CloudFrontDistribution:
 Type: AWS::CloudFront::Distribution
 Properties:
 DistributionConfig:
 Enabled: true
 HttpVersion: "http2"
 Aliases:
 Fn::If:
 - IsProd
 - ["anything.yourdomain.app"]
 - ["anything.dev.yourdomain.app"]
 Comment: My Service Service CloudFront Distribution
 DefaultCacheBehavior:
 TargetOriginId: MyApiGatewayServiceOrigin
 ViewerProtocolPolicy: "redirect-to-https"
 AllowedMethods: ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
 OriginRequestPolicyId: "216adef6-5c7f-47e4-b989-5492eafa07d3" # Predefined Managed Policy called "AllViewer"
 CachePolicyId:
 Ref: CloudFrontCachePolicy
 Origins: 
 - Id: MyApiGatewayServiceOrigin 
 DomainName:
 Fn::Join:
 - ""
 - - Ref: HttpApi
 - ".execute-api."
 - Ref: AWS::Region
 - ".amazonaws.com"
 CustomOriginConfig:
 OriginProtocolPolicy: "https-only"
 OriginSSLProtocols:
 - "TLSv1.2"
 ViewerCertificate:
 AcmCertificateArn:
 Fn::If:
 - IsProd
 - "arn:aws:acm:region:account-id:certificate/prod-certificate-id"
 - "arn:aws:acm:region:account-id:certificate/dev-certificate-id"
 MinimumProtocolVersion: "TLSv1.2_2021"
 SslSupportMethod: "sni-only"
answered Sep 5, 2023 at 11:39

Comments

0

This is a difficult one to debug, I wrote a blog here going into more detail on the solution, hope it helps someone else. Thanks @Bob for putting me on the right track: https://www.rehanvdm.com/serverless/cloudfront-reverse-proxy-api-gateway-to-prevent-cors/index.html

answered Oct 23, 2020 at 3:09

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.