Skip to content

Instantly share code, notes, and snippets.

@antonbabenko
Created November 19, 2024 16:11
Show Gist options
  • Save antonbabenko/f9eee9603a525d55c3ae1abba1a561f5 to your computer and use it in GitHub Desktop.
Save antonbabenko/f9eee9603a525d55c3ae1abba1a561f5 to your computer and use it in GitHub Desktop.
Lambda@Edge (origin-request) for CloudFront OAC with Lambda Function URL integration for POST/PUT
'use strict';
const crypto = require('crypto');
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const method = request.method;
const body = Buffer.from(request.body.data, 'base64').toString();
if (method != 'POST' && method != 'PUT') {
return callback(null, request);
}
const hash = crypto.createHash("sha256").update(body).digest("hex");
console.log("hash", hash);
if (!headers["x-amz-content-sha256"]) {
headers["x-amz-content-sha256"] = [{ "key": "x-amz-content-sha256", "value": hash }];
}
return callback(null, request);
};
module "cloudfront" {
source = "terraform-aws-modules/cloudfront/aws"
version = "~> 3.0"
# OAC is not supported for anything except GET requests out of the box:
# https://dev.to/vng_bach/resolve-lambda-url-error-signature-not-match-when-using-postput-44jm
# https://repost.aws/questions/QU7QethaVnTWKOF1JHGQggWg/lambda-url-with-x-amz-content-sha256-header
create_origin_access_control = true
origin_access_control = {
lambda_webhook_receiver = {
description = "CloudFront access to Lambda"
origin_type = "lambda"
signing_behavior = "always"
signing_protocol = "sigv4"
}
}
origin = {
lambda_webhook_receiver = {
domain_name = trimsuffix(trimprefix(module.webhook_receiver.lambda_function_url, "https://"), "/")
origin_access_control = "lambda_webhook_receiver"
custom_origin_config = {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default = { # to disallow requests except to real webhook receiver
domain_name = "somethingnonexistent-just-to-disallow-requests.com"
custom_origin_config = {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
}
default_cache_behavior = {
target_origin_id = "default"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
use_forwarded_values = false
compress = true
cache_policy_name = "Managed-CachingDisabled"
}
ordered_cache_behavior = [
{
path_pattern = local.webhook_path_pattern
target_origin_id = "lambda_webhook_receiver"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
use_forwarded_values = false
compress = true
cache_policy_name = "Managed-CachingDisabled"
origin_request_policy_name = "Managed-AllViewerExceptHostHeader"
lambda_function_association = {
origin-request = {
lambda_arn = module.amz_content_sha256.lambda_function_qualified_arn
include_body = true
}
}
}
]
viewer_certificate = {
acm_certificate_arn = module.acm_us_east_1.acm_certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
geo_restriction = {
restriction_type = "blacklist"
locations = ["RU", "BY"]
}
}
# Lambda@Edge function which calculates the SHA256 hash of the request body
# and adds it as a custom header for CloudFront to be able to call Lambda function with OAC
module "amz_content_sha256" {
source = "terraform-aws-modules/lambda/aws"
version = "~> 7.0"
function_name = "amz-content-sha256"
description = "Lambda@Edge function which calculates the SHA256 hash of the request body and adds it as a custom header for CloudFront to be able to call Lambda function with OAC"
handler = "amz_content_sha256.handler"
runtime = "nodejs20.x"
architectures = ["x86_64"]
publish = true
lambda_at_edge = true
source_path = "${path.module}/amz_content_sha256.js"
create_current_version_allowed_triggers = true
create_unqualified_alias_allowed_triggers = true
allowed_triggers = {
cloudfront = {
service = "cloudfront"
source_arn = module.cloudfront.cloudfront_distribution_arn
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment