Skip to content

Instantly share code, notes, and snippets.

@jrochkind
Last active June 18, 2024 21:00
Show Gist options
  • Save jrochkind/4edcc8a4a1abf090a771a3e0324f6187 to your computer and use it in GitHub Desktop.
Save jrochkind/4edcc8a4a1abf090a771a3e0324f6187 to your computer and use it in GitHub Desktop.
Example CloudFront distribution which can pass response-content-distribution to S3 origin

Example terraform files showing creation of a Cloudfront distribution on top of an S3 origin, that can pass along response-content-disposition and response-content-type headers to the S3 origin, and properly return specified HTTP headers.

As discussed in blog post at https://bibwild.wordpress.com/2024/06/18/cloudfront-in-front-of-s3-using-response-content-disposition/

Some configuration in this example may go beyond what's discussed in blog post for our own choices.

To run this example yourself, you'd want to replace the string demo-example-app with a unique token of your choice. (At least for name of S3 bucket, which needs to be globally unique). you'd probably also want to use your own public key, if you want to have the coresponding private key so you can sign cloudfront urls!

resource "aws_s3_bucket" "demo-example-app" {
bucket = "demo-example-app"
}
# Have bucket allow access to our cloudfront distro
resource "aws_s3_bucket_policy" "demo-example-app" {
bucket = aws_s3_bucket.demo-example-app.id
policy = templatefile("s3_cloudfront_access_policy.tftpl",
{
bucket_name : aws_s3_bucket.demo-example-app.bucket,
cloudfront_arn : aws_cloudfront_distribution.demo-example-app.arn
}
)
}
resource "aws_s3_bucket_public_access_block" "derivatives" {
bucket = aws_s3_bucket.demo-example-app.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_cloudfront_distribution" "demo-example-app" {
comment = "demo-example-app"
enabled = true
is_ipv6_enabled = true
http_version = "http2and3"
# Only North America/Europe to save money
price_class = "PriceClass_100"
default_cache_behavior {
allowed_methods = [
"GET",
"HEAD",
"OPTIONS",
]
cached_methods = [
"GET",
"HEAD",
"OPTIONS",
]
compress = true
target_origin_id = aws_s3_bucket.demo-example-app.bucket_regional_domain_name
viewer_protocol_policy = "https-only"
# restricted cloudfront distribution, urls must be signed with this key group
trusted_key_groups = [aws_cloudfront_key_group.demo-example-app.id]
# for an unrestricted cloudfront distribution, instead:
# trusted_key_groups = []
cache_policy_id = aws_cloudfront_cache_policy.jrochkind-test-caching-optimized-plus-s3-params.id
response_headers_policy_id = data.aws_cloudfront_response_headers_policy.Managed-CORS-with-preflight-and-SecurityHeadersPolicy.id
}
origin {
connection_attempts = 3
connection_timeout = 1
domain_name = aws_s3_bucket.demo-example-app.bucket_regional_domain_name
origin_id = aws_s3_bucket.demo-example-app.bucket_regional_domain_name
origin_access_control_id = aws_cloudfront_origin_access_control.demo-example-app.id
}
restrictions {
geo_restriction {
locations = []
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
minimum_protocol_version = "TLSv1.2_2021"
}
}
# Query params included in cache will also be forwarded to origin
resource "aws_cloudfront_cache_policy" "jrochkind-test-caching-optimized-plus-s3-params" {
name = "jrochkind-test-caching-optimized-plus-s3-params"
comment = "Based on Managed-CachingOptimized, but also including select S3 query params"
default_ttl = 86400
max_ttl = 31536000
min_ttl = 1
parameters_in_cache_key_and_forwarded_to_origin {
enable_accept_encoding_brotli = true
enable_accept_encoding_gzip = true
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "none"
}
query_strings_config {
query_string_behavior = "whitelist"
query_strings {
items = [
"response-content-disposition",
"response-content-type"
]
}
}
}
}
# Data import of id for AWS managed Cloudfront policy
data "aws_cloudfront_response_headers_policy" "Managed-CORS-with-preflight-and-SecurityHeadersPolicy" {
name = "Managed-CORS-with-preflight-and-SecurityHeadersPolicy"
}
# AWS OAC object used to have Cloudfront sign URLs to S3 origin
resource "aws_cloudfront_origin_access_control" "demo-example-app" {
description = "Cloudfront signed s3"
name = "demo-example-app"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
# Cloudfront key group granted access to restricted cloudfront distribution
resource "aws_cloudfront_key_group" "demo-example-app" {
comment = "key group used by our app for signing urls"
items = [aws_cloudfront_public_key.demo-example-app.id]
name = "demo-example-app"
}
# Public key used by key group for restricted cloudfront distribution
resource "aws_cloudfront_public_key" "demo-example-app" {
comment = "public key used by our app for signing urls"
encoded_key = file("public_key-demo-example-app.pem")
name = "demo-example-app"
}
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TwqCJvveZgspf6oZu6r
rgNQ6sWaYe3HPuhicsjeX2xwCOmp8hNnz/tpR87gwiDTbxU8njW5tjhwozXaumxi
FJsUAkzbMRi+xNbAaUkE9u9OJynDTpJxm3RmE3tLeZVqUM2R9A2vFI8WGPAvyoZl
dqlpOQNYsNwfouz4VtxqA3ujL456Q+PywVZVT2WN7lJXskp7uwT/1f41DhT5PEEx
b67jdh4xBigTn/aq/Q5MzFJOY7A7SIypiOJUQe0CCp1j7rc5gsuVRJ1iB0A5shEG
pgaHZkhPVsDOTjiIBPJLVQ/bnCjj+cMsb2u2o/TUT9wyZA8CwVxlnNb4XCnFB5Sc
pQIDAQAB
-----END PUBLIC KEY-----
# S3 policy which grants access from a specified Cloudfront distribution
${jsonencode(
{
Id = "PolicyForCloudFrontPrivateContent"
Statement = [
{
Action = "s3:GetObject"
Condition = {
StringEquals = {
"AWS:SourceArn" = "${cloudfront_arn}"
}
}
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Resource = "arn:aws:s3:::${bucket_name}/*"
Sid = "AllowCloudFrontServicePrincipal"
},
]
Version = "2008-10-17"
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment