Amazon Cloudfront: Origin request policy (tutorial)

Published on: Sun Nov 20 2022

Series

Content

Introduction

When working with Amazon Cloudfront, the properties in the request are not automatically passed to your origin server.

This is by design, and one way to customize what gets passed over is using the origin request policy.

In this tutorial, we will go through the steps on how to set this up using terraform!

For this tutorial, we will be building on our code in Amazon Cloudfront: Create a distribution + Lambda (tutorial).

Let’s jump right into it

Before you start

Before you start going through the tutorial, make sure you are using the respository from the previous tutorial.

The respository can be found here - amazon-cloudfront-create-distribution.

It will be the base from which we will build from!

A Review

Here is a big picture overview of some theory and background before we go into the nitty gritty details.

Origin request policy

The origin request will always include:

  • URL of the request
  • request body
  • select HTTP headers (Host , User-Agent , X-Amz-Cf-Id )

These are the only properties included.

So, if you want additional properties, we need to use the origin request policy to add them in.

The Architecture

The architecture is similar to our previous tutorial. The only difference is we are customizing our cloudfront configurations here.

Then, by inspecting the logs, we can see the origin request policy in action!

Illustration of the sandbox architecture with Cloudfront and Lambda
Illustration of the sandbox architecture with Cloudfront and Lambda

The steps

  1. The Viewer (client) request comes in

  2. Cloudfront forwards the request to the origin server (Lambda URL + Lambda)

  3. The logs from the request are added into Cloudwatch Logs

  4. The Lambda function returns a response

  5. The Viewer (client) receives the response

Cloudfront origin request policy

In our example, let’s setup the policy to pass over a custom header X-Very-Secret-Client-Token to our origin server.

1. Add the origin request policy resource

In terraform, there is a resource just for this

Add the following changes:

// infra/main.tf

resource "aws_cloudfront_origin_request_policy" "custom" {
  name    = "custom-origin-request-policy"
  comment = "Custom origin request policy"
  headers_config {
    headers {
      items = ["X-Very-Secret-Client-Token"]
    }
  }
}

Helpful Reference

2. Add a header behavior to this resource

The header behavior defines how cloudfront should treat the client request headers.

For our example, we will be using “whitelist” (or allowlist) which is limited to the header values we provide.

However, there are a few options, be sure to check the terraform resource documentation.

Add the following changes:

// infra/main.tf

resource "aws_cloudfront_origin_request_policy" "custom" {
  name    = "custom-origin-request-policy"
  comment = "Custom origin request policy"
  headers_config {
    header_behavior = "whitelist"
    headers {
      items = ["X-Very-Secret-Client-Token"]
    }
  }
}

Helpful Reference

Like the header config, these fields are a required field so we will just set it to none for now.

The values are defined in the same way as the headers, check terraform documentions for more information.

// infra/main.tf

resource "aws_cloudfront_origin_request_policy" "custom" {
  name    = "custom-origin-request-policy"
  comment = "Custom origin request policy"
  headers_config {
    header_behavior = "whitelist"
    headers {
      items = ["X-Very-Secret-Client-Token"]
    }
  }
  cookies_config {
    cookie_behavior = "none"
  }
  query_strings_config {
    query_string_behavior = "none"
  }
}

4. Attach origin request policy to cache the behavior

In our case we are attaching to the default cache behavior, which would to all requests.

// infra/main.tf

resource "aws_cloudfront_distribution" "cf_distribution" {
  origin {
    # This is required because "domain_name" needs to be in a specific format
    domain_name = replace(replace(aws_lambda_function_url.test.function_url, "https://", ""), "/", "")
    origin_id = module.lambda_origin.lambda[0].function_name

    custom_origin_config {
      https_port = 443
      http_port = 80
      origin_protocol_policy = "https-only"
      origin_ssl_protocols = ["TLSv1.2"]
    }
  }

  default_cache_behavior {
    allowed_methods          = ["GET", "HEAD"]
    cached_methods           = ["GET", "HEAD"]
    target_origin_id         = module.lambda_origin.lambda[0].function_name
    viewer_protocol_policy   = "redirect-to-https"
    min_ttl                  = local.min_ttl
    default_ttl              = local.default_ttl
    max_ttl                  = local.max_ttl
    origin_request_policy_id = aws_cloudfront_origin_request_policy.custom.id
    # A cache policy is required when attaching, we’ll use a managed policy id for now
    cache_policy_id          = "658327ea-f89d-4fab-a63d-7e88639e58f6"
  }

  price_class = var.cf_price_class
  enabled = true
  is_ipv6_enabled     = true
  comment             = "origin request policy test"
  default_root_object = "index.html"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  tags = {
    Environment = "production"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

5. Apply the infrastructure

Now we have all our definition in Terraform, all that is left is to generate it.

Run the following:

// This will re-generate the assets
pnpm run generate-assets --filter "@function/*"

export AWS_ACCESS_KEY_ID=<your-key>
export AWS_SECRET_ACCESS_KEY=<your-secret>
export AWS_DEFAULT_REGION=us-east-1

terraform init
terraform plan
terraform apply -auto-approve

If the infrastructure applied successfully then you should see something like this:

Illustration of the Terraform outputs
Illustration of the Terraform outputs

Testing it out

1. Make a request

Curl:

curl -vvv -H "X-Very-Secret-Client-Token: secret-token" "<cf_distribution_domain_url>"

Postman:

Illustration of using Postman for the request
Illustration of using Postman for the request

2. Check the logs

Apart from the response from cloudfront, be sure to check the logs of your lambda function (our origin server).

Make you are able to see it as it will be useful later when we start to integrate the origin request policies.

Here is an example of the event that is logged from our origin server.

Request Event Example:

Illustration of the logs when adding a origin request policy
Illustration of the logs when adding a origin request policy
Click to view Request Event (in JSON format)
{
  "event": {
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/test",
    "rawQueryString": "",
    "headers": {
      "x-amzn-tls-version": "TLSv1.2",
      "x-forwarded-proto": "https",
      "x-forwarded-port": "443",
      "x-forwarded-for": "2001:569:7f36:4000:59d2:4451:b85c:dd8d",
      "via": "2.0 d7782b26e589b8e1397d352f4daf0d58.cloudfront.net (CloudFront)",
      "x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
      "x-amzn-trace-id": "Root=1-6379c843-5a977794388130833ee2d137",
      "host": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl.lambda-url.us-east-1.on.aws",
      "x-very-secret-client-token": "secret-token",
      "cache-control": "no-cache",
      "accept-encoding": "br,gzip",
      "x-amz-cf-id": "e7D4qcWlIgzSWXm-EMof5oqJi0tr6Ii21E8RrmlJBiulN0GH27fslg==",
      "user-agent": "Amazon CloudFront"
    },
    "requestContext": {
      "accountId": "anonymous",
      "apiId": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl",
      "domainName": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl.lambda-url.us-east-1.on.aws",
      "domainPrefix": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl",
      "http": {
        "method": "GET",
        "path": "/test",
        "protocol": "HTTP/1.1",
        "sourceIp": "64.252.71.170",
        "userAgent": "Amazon CloudFront"
      },
      "requestId": "7f3331cd-c417-495a-ab56-72891d04c5a4",
      "routeKey": "$default",
      "stage": "$default",
      "time": "20/Nov/2022:06:25:07 +0000",
      "timeEpoch": 1668925507591
    },
    "isBase64Encoded": false
  }
}

As you can see our custom header (X-Very-Secret-Client-Token ) is there.

For reference, here is the repository of completed tutorial - Github: amazon-cloudfront-origin-request-policy.

Conclusion

That’s really it for the origin request policy, remember its a policy that allow us to customize the properties passed into the origin server.

And... that’s all for now, stay tuned for more!

If you found this helpful or learned something new, please do share this article with a friend or co-worker 🙏❤️ (Thanks!)

🔥 Challenge:

Try to add query strings and cookies to our existing setup then test it out to see if it get passed over by inspecting the logs.

See if you can get it to work!


Enjoy the content ?

Then consider signing up to get notified when new content arrives!

Jerry Chang 2023. All rights reserved.