7

I have setup a CloudFront distribution and a Lambda@Edge function as described in this article:

https://aws.amazon.com/blogs/networking-and-content-delivery/resizing-images-with-amazon-cloudfront-lambdaedge-aws-cdn-blog/

I now want to limit the S3 bucket to be private and only allow the CloudFront distribution access to the bucket. I've added an Origin Access Identity to the S3 Origin configuration, and updated the bucket policy to allow this OAI GetObject access to the bucket. I have removed the statement from the bucket policy that made it publicly readable, and changed the ACL to be private. If I do not include any query string parameters on the request to CloudFront, the image is returned as expected and the S3 link is not directly accessible.

However, the solution relies on passing in query parameters to resize the images on the fly (via 2 Lambda@Edge functions) and after making the bucket private I get an Access Denied 403 response from CloudFront.

I tried disabling forwarding of query strings which had no effect. I also tried only updating the bucket policy to remove public read access, and leave the ACL as PublicRead and it worked. So it makes me think something is up with the ACL being set to private

I was wondering if perhaps S3 is returning a 403 instead of a 404 when the bucket is made private? But then I don’t understand why adding the whitelisted query string param yields a different result to the request for the same image with no query string

Update

Example of working URL: http://<my_distro>.cloudfront.net/images/house.jpeg

Example of broken URL: http://<my_distro>.cloudfront.net/images/house.jpeg?size=1200

ExoticChimp
  • 1,884
  • 1
  • 19
  • 37
  • I think there is something wrong with your query string to assume that URL is a wrong path. Do you want to share your Query String Params ? – Kannaiyan Apr 24 '18 at 18:43
  • @Kannaiyan updated to show the urls. note that both of the urls work absolutely fine and images resizing works if i make the bucket Public readable – ExoticChimp Apr 24 '18 at 19:02
  • If you check with postman, it will tell you whether you are looking at cached content of 403. Accessing a different object everytime to test will also remove this confusion. – Kannaiyan Apr 24 '18 at 19:41
  • @Kannaiyan Yeah I checked with a different image each time. The issue still seems to be something to do with the ACL on the bucket being private instead of public read as making the bucket publicread removes the error – ExoticChimp Apr 24 '18 at 20:02
  • 1
    This is a bit of a head-scratcher, but you haven't shown us how *your* Lambda function works. I would suggest that you grant the `s3:ListBucket` permission to the OAI **temporarily** and see if that resolves your issue. If it does, then I think we'll have an idea of what you need to do next. If not, review the bucket logs. Importantly, be sure your [Error Caching Minimum TTL is set to 0](https://stackoverflow.com/a/35541525/1695906) for error 403 before testing further, because CloudFront caches errors for 5 minutes by default, independently of the other caching settings. – Michael - sqlbot Apr 24 '18 at 22:54
  • @Michael-sqlbot I’ll give those a try. The code for the lambda is on the blog post link I mentioned. I just changed the name of the query string Params and made the public private – ExoticChimp Apr 25 '18 at 06:48

2 Answers2

12

After investigating it turns out that if a bucket is private, then S3 will return a 403 instead of a 404 if an object does not exist in the bucket which makes sense from a security point of view (prevents object enumeration etc.).

The viewer request code in the Lambda from the blog post transformed the image url when the query string was present. If this image doesn't exist, it generates it on the fly. The origin response functino from the blog post is checking for a status of 404 in order to trigger the image resizing. However when the bucket is made private, the response is 403 so instead the Lambda@Edge just forwards the response. The fix is to either handle the 403 or make the bucket public. I've gone with the former

ExoticChimp
  • 1,884
  • 1
  • 19
  • 37
  • I also face with that problem. I need to change code to compare with 403 status code. – Son Lam Oct 15 '18 at 09:40
  • Thank you for this answer! I'm in exactly the same situation, and have been scratching my head for a little while trying to figure out why I'm getting a 403 response, regardless of the Lamda function having full access to S3. – mustdobetter Jul 12 '19 at 13:52
4

Lambda@Edge works with s3 via special user. So, you have to add rules in your s3 bucket policy, like this:

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXX"
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::xxx/*"
        },
        {
            "Sid": "2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXX"
            },
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::xxxx"
        }
    ]
}
Aleksandr L
  • 400
  • 2
  • 5
  • This is the exact solution I was looking for. Now the bucket works with OAI and it properly returns 404. Thanks a lot :) – Serban Cezar Feb 04 '20 at 11:01