CloudFront to S3 OAC not working in CloudFormation

0

I am trying to write the most basic of static website servers possible with AWS in CloudFormation. But for some reason my OAC is not working and I am getting AccessDenied errors when I access CloudFront. Even more frustratingly after clicking around the aws console ui (changing s3 bucket to not block all public access) in the aws console, the site magically started serving the assets and the error was gone - this I couldn't repeat. Then after another deploy, the error was back again.

UPDATE: 2 hours after deployment CloudFront gained access to s3 (I have caching disabled so it is exact). So the question now is, why is that time so large?

template.yaml:

Resources:
  FrontendBucket:
    Type: AWS::S3::Bucket
  FrontendOAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: FrontendOAC
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
  FrontendBucketOACPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref FrontendBucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub ${FrontendBucket.Arn}/*
            Principal:
              Service: cloudfront.amazonaws.com
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${FrontendDistribution}
  FrontendDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: True
        DefaultCacheBehavior:
          # CachingDisabled policy
          #CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
          TargetOriginId: BucketOrigin
          #TODO: change this to redirect-to-https
          ViewerProtocolPolicy: allow-all
        DefaultRootObject: index.html
        Origins:
          - Id: BucketOrigin
            DomainName: !GetAtt FrontendBucket.DomainName
            S3OriginConfig:
              # AWS docs instruct to leave this field empty if using OAC instead of OAI
              OriginAccessIdentity: ''
            OriginAccessControlId: !GetAtt FrontendOAC.Id
Outputs:
  FrontendBucketURI:
    Value: !Sub s3://${FrontendBucket}
  FrontendDomain:
    Value: !GetAtt FrontendDistribution.DomainName
  FrontendURL:
    Value: !Sub http://${FrontendDistribution.DomainName}
2 Answers
3
Accepted Answer

Your setup is nearly the same that I've used for a long time. One difference I noticed is that you've specified !GetAtt FrontendBucket.DomainName for the origin's DomainName. I use !GetAtt FrontendBucket.RegionalDomainName instead, to match the regional hostname syntax in documentation: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesDomainName. I'm not sure if that's the problem, but it does differ from documentation and my working setups.

A very tentative guess about what might be happening is that the global name syntax might otherwise work, but it would require the s3:GetBucketLocation permission for CloudFront to determine the bucket's region, and that permission definitely isn't granted. It might not be granted on the CloudFront service account's side, either, though, so adding the permission in the bucket policy wouldn't necessarily fix it.

EXPERT
Leo K
answered a month ago
profile picture
EXPERT
reviewed a month ago
profile pictureAWS
EXPERT
reviewed a month ago
  • Using RegionalDomainName makes the site accessible almost immediately. Makes sense that when pasting an s3 domain name (not regional) into the s3 aws console it's automatically converted into the regional form. Evidently I am accessing an edge location which hasn't got the s3 permissions setup correctly, but why does using RegionalDomainName fix that?

  • An S3 bucket is a regional resource. CloudFront and all other clients have to access its contents via an S3 endpoint in that region. The RegionalDomainName attribute returns the DNS name of the S3 endpoint where that bucket can be accessed directly, while DomainName points to a global S3 service endpoint where clients can go and ask which region a bucket is located in, only knowing its name, if they have the s3:GetBucketLocation permission to the bucket. Even when permissions suffice, that incurs an extra round-trip to the global endpoint, so it's best to use the regional one directly.

-1

If your origin is an Amazon S3 bucket configured as a website endpoint, you must set it up with CloudFront as a custom origin. That means you can't use OAC (or OAI). OAC doesn't support origin redirect by using Lambda@Edge.

I think you have configured cloudfront distribution as S3 origin while you should configure it as custom origin for website

Please see this https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html

answered a month ago
profile pictureAWS
EXPERT
reviewed a month ago
  • Right at the very top of the included template.yaml you can clearly see that the bucket is not setup as a website endpoint. Furthermore this doesn't address the problem of why it suddenly started working. I have no idea why one would bother with copy pasting such an answer as this.

  • Deepak Gupta thanks for taking the time to try to help.

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions