3

We are trying to put some custom headers to our cloud fount distribution, but its not working as expected. Following is the scenario.

We have a s3 bucket named "example-images" with all static contents, s3 bucket is exposed for public access. Has following CORS policy

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <ExposeHeader>Access-Control-Allow-Credentials</ExposeHeader>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

We are able to access s3 contents via URL following the sample output using curl.

x-amz-id-2: xxxxxxxxxxxx
x-amz-request-id: xxxxxxxxxxxx
Date: Wed, 05 Oct 2016 04:10:26 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Expose-Headers: Access-Control-Allow-Credentials
Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
Last-Modified: Mon, 26 Sep 2016 13:12:58 GMT
ETag: "xxxxxxxxxxxxxx"
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 1630
Server: AmazonS3

Then we created a cloudfront distribution for serving these contents.

As per documentation we have following values set in "Default Origin"

  • Restrict bucket access = no
  • Oringnal custom headers Some custom header

    e.g. "Access-Control-Allow-Credentials = true"

In CF distribution "Behavior" we have following settings.

  • Protocol policy = http & https
  • Allow http methods = GET, HEAD, OPTIONS.
  • Forward Headers = whitelist
  • Whitelist headers = Origin
  • Object Caching = Use origin cache headers

Still we are not able to view custom headers when using CF Distribution url.

curl -sI -H "Origin: example.com" -H "Access-Control-Request-Method: GET" -H "Access-Control-Allow-Credentials: GET"
HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 1630
Connection: keep-alive
Date: Wed, 05 Oct 2016 04:03:20 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Expose-Headers: Access-Control-Allow-Credentials
Last-Modified: Mon, 26 Sep 2016 13:12:58 GMT
ETag: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
Age: 1231
X-Cache: Hit from cloudfront
Via: 1.1 xxxxxxxx.cloudfront.net (CloudFront)
X-Amz-Cf-Id: xxxxxxxxxxxxxxxxxxxxx

Can you help us indentifying what setting we are missing here.

vasco.debian
  • 306
  • 2
  • 13
  • Can you add a consise problem statement to your question? eg maybe your key question is "We are not able to add a custom header to our Cloudfront distribution, which uses S3 as it's source". – Tim Oct 05 '16 at 04:40
  • If you need custom attributes attached to your objects, it should be trivial to parse out the x-amz-meta- in your client application. – Peter Nduati Oct 05 '16 at 06:14
  • 3
    Custom Origin Headers are headers CloudFront injects into the *request* before sending to the origin -- not into the *response*. The client (user agent/browser, curl in this case) can't see them -- that's by design. – Michael - sqlbot Oct 05 '16 at 13:16

2 Answers2

3

You can add custom headers to the response from CloudFront / S3 using a Lambda@Edge function. The lambda code runs within the local edge locations, but needs to be created and maintained in the us-east-1 region.

The example code here uses nodeJS 6.10 to add the x-frame-options response header, but you can add any header that is not restricted by AWS.

'use strict'; 
 exports.handler = (event, context, callback) => {
   const response = event.Records[0].cf.response; 
   const headers = response.headers; 
   response.headers['x-frame-options'] = [{"key":"X-Frame-Options","value":"SAMEORIGIN"}]; 
   console.log(response.headers); 
   callback(null, response);
 }; 

Create a definitive version of the Lambda, then set the Lambda Version's trigger configuration as the CloudFront origin-response Event type for your path pattern behavior.

The example code logs events to CloudWatch logs service for debugging purposes. If you don't already have one you will need to setup a lambda execution IAM role that allows a policy allowing CloudWatch logs actions to be assumed by edgelambda.amazonaws.com and lambda.amazonaws.com.

Basic Lambda Execution Policy allowing logs to be written to CloudWatch:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*",
            "Effect": "Allow"
        }
    ]
}

Trust Relationship allowing Lambda and Lambda@Edge to assume the role :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "edgelambda.amazonaws.com",
          "lambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

It would be better if AWS simply allowed the custom headers to be set using the CloudFront GUI but until then this solution should satisfy your requirement.

shonky linux user
  • 1,163
  • 10
  • 15
0

I just implemented a header manipulation at the viewer response. It simply adds a header to the response sent by CloudFront.

You need to use the new tool named "CloudFront Function". https://console.aws.amazon.com/cloudfront/v3/home#/functions/ It's running directly on the 218+ PoP of the CDN, not only on the regional one. The function needs to be written in JS.

Here the AWS blog post comparing lambda@edge and CloudFront Function: https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/

From AWS blog post the most use case are:

  • HTTP header manipulation
  • Cache-key manipulations and normalization
  • URL rewrites and redirects
  • Access authorization

My function is simple:

    function handler(event) {
      var response = event.response;
      var headers = response.headers;

      // Set custom header
      headers['custom-header'] = {value: '*'}; 

      // Return the response to viewers 
      return response;
    }

All you have to do is to attach it to your distribution, and it just works.

remi
  • 1