Bypassing need for x-amz-cf-id header inclusion in S3 auth in cloudfront

danielgormly picture danielgormly · Mar 7, 2018 · Viewed 7.7k times · Source

I have a not completely orthodox CF->S3 setup. The relevant components here are:

  1. Cloudfront distribution with origin s3.ap-southeast-2.amazonaws.com

  2. Lambda@Edge function (Origin Request) that adds a S3 authorisation (version 2) query string (Signed using the S3 policy the function uses).

The request returned from Lambda is completely correct. If I log the uri, host and query string I get the file I am requesting. However, if I access it through the Cloudfront link directly, the request fails because it no longer uses the AWSAccessKeyID, instead it opts to use x-amz-cf-id (but uses the same Signature, Amz-Security-Token etc). CORRECTION: it may not replace, but be required in addition to.

I know this is the case because I have returned both the StringToSign and the SignatureProvided. These both match the Lambda response except for the AWSAccessKeyID which has been replaced with the x-amz-cf-id.

This is a very specific question obviously. I may have to look at remodelling this architecture but I would prefer not to. There are several requirements which has led me down this not completely regular setup.

Answer

Tamás Sallai picture Tamás Sallai · Aug 7, 2019

I believe the AWSAccessKeyID => x-amz-cf-id replacement is the result of two mechanisms:

First, you need to configure CloudFront to forward the query parameters to the origin. Without that, it will strip all parameters. If you use S3 signed URLs, make sure to also cache based on all parameters as otherwise you'll end up without any access control.

Second, CloudFront attaches the x-amz-cf-id to the requests that are not going to an S3 origin. You can double-check at the CloudFront console the origin type and you need to make sure it is reported as S3. I have a blog post describing it in detail.

But adding the S3 signature to all the requests with Lambda@Edge defeats the purpose. If you want to keep the bucket private and only allow CloudFront to access it then use the Origin Access Identity, that is precisely for the use-case.