Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CloudFront Distribution with S3 Origin Responds with XML ListBucketResult

My intention is to have my static website files (in React, if that's a factor) accessible only via my domain and not directly through S3 URLs. It seems to be working on my own computer (though that might be CloudFront cache from when the bucket was public), but other clients receive only S3 messages in XML. Requesting the domain without any path gives a response. Requesting any path (e.g. /index.html, a file in my bucket) gives a response with the code NoSuchKey.

What am I doing wrong? Here's the current configuration.

  • In Route 53, I'm pointing the appropriate subdomain at the CloudFront distribution with a CNAME record (xxxxxxxxxxx.cloudfront.net.)
  • In ACM, I have a certificate that covers the subdomain (*.mydomain.com)
  • In CloudFront, I have a distribution with those domain name (xxxxxxxxxxx.cloudfront.net) and alternate domain name (subdomain.mydomain.com). - It's enabled and has been in the deployed state for several hours now.
  • It has a single origin, with domain name subdomain.mydomain.com.s3.amazonaws.com
  • I chose to restrict bucket access and selected an existing identity for origin access. I had CloudFront update the bucket policy earlier today.
  • The distribution has a single behavior record which redirects HTTP to HTTPS and only allows GET and HEAD methods
  • My S3 bucket name matches the Route 53 record (subdomain.mydomain.com)
  • Static website hosting is enabled, with both the index and error documents set to index.html
  • The bucket policy was autogenerated. It includes a single identity and limits use to the s3:GetObject action on resource arn:aws:s3:::subdomain.mydomain.com/*
  • CORS configuration is empty
  • Inside the bucket is a React app, with index.html as its entry point.

Edit: my bucket policy (do I need to add another action?)

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EZOBXXXXXXXXX"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::subdomain.mydomain.com/*"
        }
    ]
}
like image 259
carpiediem Avatar asked Oct 25 '18 08:10

carpiediem


People also ask

What is CloudFront distribution origin?

An origin is the location where content is stored, and from which CloudFront gets content to serve to viewers. To specify an origin: Use S3OriginConfig to specify an Amazon S3 bucket that is not configured with static website hosting.

How does S3 and CloudFront work?

S3 stores all the pre-recorded videos in different formats. CloudFront is used to cache the video to edge locations. This can be used to deliver content to the end user. Location is picked up automatically based on the closest physical edge location.

Can CloudFront have multiple S3 origins?

You can configure a single CloudFront web distribution to serve different types of requests from multiple origins. For example, your website might serve static content from an Amazon Simple Storage Service (Amazon S3) bucket and dynamic content from a load balancer.

How do I connect my S3 to CloudFront?

Open the CloudFront console. Choose Create Distribution. Under Origin, for Origin domain, choose your S3 bucket's REST API endpoint from the dropdown list. Or, enter your S3 bucket's website endpoint.


1 Answers

Two changes got this working nicely. Thanks to Michael for his help.

  1. ~~I modified the permissions of individual objects in my S3 bucket. I had thought that the bucket policy (above) was sufficient but that turned out not to be the case. The bucket policy refers to an origin access identity (OAI) in CloudFront. Now I reference the canonical user ID of that same origin access identity when running the sync command in the AWS command line interface (CLI). In package.json scripts: "deploy": "aws s3 sync build/ s3://subdomain.mydomain.com --delete --grants read=id=S3CANONICALIDOFORIGINACCESSIDENTITY"~~
  2. I specified a Default Root Object as index.html in my CloudFront distribution. (Click the Edit button in the General tab.) This tells CloudFront what to serve if it can't match the request path with an object in the S3 bucket. This is critical for client-side routing, like I'm doing in React.
  3. I had been issuing invalidations to my CloudFront distribution against /index.html, then feeling confused when my app didn't seem to update. I think this was because, although index.html is always served to users, it's never explicitly requested. My routes looked lik / or /dashboard, which were not being invalidated. Now, I clear the CloudFront cache by invalidating /*.

Edit: It's been a few years, but I don't think #1 is necessary. I've set up several distributions since then with only the bucket policy defining permissions.

like image 164
carpiediem Avatar answered Sep 20 '22 03:09

carpiediem