Using Cloudfront with an Amazon S3 bucket keeps allows us to prevent direct access to the S3 bucket while allowing viewers (users) to access the content in the bucket only through the specified CloudFront distribution.
CloudFront provides two ways to achieve this.
In this article we will utilize terraform automate provisioning the AWS resources.
1resource "aws_s3_bucket" "main" { 2 bucket = "s3-cloudfront-oac.raugustine.xyz" 3 4 tags = { 5 Environment = "dev" 6 Terraform = true 7 } 8} 9 10resource "aws_s3_bucket_acl" "main" { 11 bucket = aws_s3_bucket.main.id 12 acl = "private" 13}
1resource "aws_cloudfront_origin_access_control" "main" { 2 name = "s3-cloudfront-oac" 3 description = "Grant cloudfront access to s3 bucket ${aws_s3_bucket.main.id}" 4 origin_access_control_origin_type = "s3" 5 signing_behavior = "always" 6 signing_protocol = "sigv4" 7} 8 9locals { 10 s3_origin_id = "myS3Origin" 11} 12 13resource "aws_cloudfront_distribution" "s3_distribution" { 14 origin { 15 domain_name = aws_s3_bucket.main.bucket_regional_domain_name 16 origin_access_control_id = aws_cloudfront_origin_access_control.main.id 17 origin_id = local.s3_origin_id 18 } 19 20 enabled = true 21 default_root_object = "index.html" 22 23 # Optional - Extra CNAMEs (alternate domain names), if any, for this distribution 24 # aliases = ["mysite.example.com", "yoursite.example.com"] 25 26 default_cache_behavior { 27 allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] 28 cached_methods = ["GET", "HEAD"] 29 target_origin_id = local.s3_origin_id 30 31 forwarded_values { 32 query_string = true 33 34 cookies { 35 forward = "none" 36 } 37 } 38 39 viewer_protocol_policy = "allow-all" 40 min_ttl = 0 41 default_ttl = 3600 42 max_ttl = 86400 43 } 44 45 price_class = "PriceClass_100" 46 47 restrictions { 48 geo_restriction { 49 restriction_type = "none" 50 } 51 } 52 53 viewer_certificate { 54 cloudfront_default_certificate = true 55 } 56}
1resource "aws_s3_bucket_policy" "default" { 2 bucket = aws_s3_bucket.main.id 3 policy = data.aws_iam_policy_document.cloudfront_oac_access.json 4} 5 6data "aws_iam_policy_document" "cloudfront_oac_access" { 7 statement { 8 principals { 9 type = "Service" 10 identifiers = ["cloudfront.amazonaws.com"] 11 } 12 13 actions = [ 14 "s3:GetObject" 15 ] 16 17 resources = [ 18 aws_s3_bucket.main.arn, 19 "${aws_s3_bucket.main.arn}/*" 20 ] 21 22 condition { 23 test = "StringEquals" 24 variable = "AWS:SourceArn" 25 values = [aws_cloudfront_distribution.s3_distribution.arn] 26 } 27 } 28}
1resource "aws_s3_bucket" "main" { 2 bucket = "s3-cloudfront-oac.raugustine.xyz" 3 4 tags = { 5 Environment = "dev" 6 Terraform = true 7 } 8} 9 10resource "aws_s3_bucket_acl" "main" { 11 bucket = aws_s3_bucket.main.id 12 acl = "private" 13} 14 15resource "aws_s3_bucket_policy" "default" { 16 bucket = aws_s3_bucket.main.id 17 policy = data.aws_iam_policy_document.cloudfront_oac_access.json 18} 19 20data "aws_iam_policy_document" "cloudfront_oac_access" { 21 statement { 22 principals { 23 type = "Service" 24 identifiers = ["cloudfront.amazonaws.com"] 25 } 26 27 actions = [ 28 "s3:GetObject" 29 ] 30 31 resources = [ 32 aws_s3_bucket.main.arn, 33 "${aws_s3_bucket.main.arn}/*" 34 ] 35 36 condition { 37 test = "StringEquals" 38 variable = "AWS:SourceArn" 39 values = [aws_cloudfront_distribution.s3_distribution.arn] 40 } 41 } 42} 43 44resource "aws_cloudfront_origin_access_control" "main" { 45 name = "s3-cloudfront-oac" 46 description = "Grant cloudfront access to s3 bucket ${aws_s3_bucket.main.id}" 47 origin_access_control_origin_type = "s3" 48 signing_behavior = "always" 49 signing_protocol = "sigv4" 50} 51 52locals { 53 s3_origin_id = "myS3Origin" 54} 55 56resource "aws_cloudfront_distribution" "s3_distribution" { 57 origin { 58 domain_name = aws_s3_bucket.main.bucket_regional_domain_name 59 origin_access_control_id = aws_cloudfront_origin_access_control.main.id 60 origin_id = local.s3_origin_id 61 } 62 63 enabled = true 64 default_root_object = "index.html" 65 66 # Optional - Extra CNAMEs (alternate domain names), if any, for this distribution 67 # aliases = ["mysite.example.com", "yoursite.example.com"] 68 69 default_cache_behavior { 70 allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] 71 cached_methods = ["GET", "HEAD"] 72 target_origin_id = local.s3_origin_id 73 74 forwarded_values { 75 query_string = true 76 77 cookies { 78 forward = "none" 79 } 80 } 81 82 viewer_protocol_policy = "allow-all" 83 min_ttl = 0 84 default_ttl = 3600 85 max_ttl = 86400 86 } 87 88 price_class = "PriceClass_100" 89 90 restrictions { 91 geo_restriction { 92 restriction_type = "none" 93 } 94 } 95 96 viewer_certificate { 97 cloudfront_default_certificate = true 98 } 99}
You can find a copy of the terraform configuration on my https://github.com/r-augustine/private-s3-cloudfront-oac