skip to Main Content

This question follow a previous one, i decided to close that since the code changed a bit and the question did not reflect my actual situation any more.

The problem

I’m trying to deploy an S3 bucket hosting my static website asset and a cloudfront distribution to access it, but the distribution still return a bare csv file for an ‘access denied’ error:

enter image description here

As this docs page states in the blue Notice alert, I’ve not made the bucket a website endpoint, this way I can use an OAC to restrict access to its content.

A strange thing is that checking the distribution origin from the web console I see this blue alert, but the copyable policy is the same I found in the bucket permission at the given link.

enter image description here

I have no error during deploy, so it must be a silly configuration error, but it keeps giving me headaches since a week now and I can’t figure out what is wrong.

Bucket and object owners corresponds

Since mi website assets are uploaded to the bucket from a different project/pipeline i followed this guide to check if the bucket and the object owners were different but actually corresponds:

> aws s3api list-buckets --query Owner.ID
"3fdbd1e5cad4dd2bbf4c66a3dbaded6b888fdb67ff6aa6e66203a4107fe17b72"
> aws s3api list-objects --bucket my-test-bucket --prefix index.html
{
    "Contents": [
        {
            "Key": "index.html",
            "LastModified": "2023-01-20T11:05:38+00:00",
            "ETag": ""52f2df5ddf8c35391f3f15a7614def58"",
            "Size": 325,
            "StorageClass": "STANDARD",
            "Owner": {
                "ID": "3fdbd1e5cad4dd2bbf4c66a3dbaded6b888fdb67ff6aa6e66203a4107fe17b72"
            }
        }
    ]
}

CloudFormation template


Resources:

  BucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    DependsOn:
      - AppBucket
      - CloudFrontDistribution
    Properties:
      Bucket: !Ref AppBucket
      PolicyDocument:
        Id: MyPolicy
        Version: '2012-10-17'
        Statement:
          Sid: PolicyForCloudFrontPrivateContent
          Action: s3:GetObject
          Effect: Allow
          Principal:
            Service: cloudfront.amazonaws.com
          Condition:
            StringEquals:
              AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
          Resource: !Sub arn:aws:s3:::${AppBucket}/*

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn:
      - AppBucket
      - DefaultCachePolicy
      - DistributionOAC
    Properties:
      DistributionConfig:
        Enabled: true
        Origins:
          - Id: AppBucket
            DomainName: !GetAtt AppBucket.DomainName
            OriginPath: /*
            S3OriginConfig: {}
            OriginAccessControlId: !Ref DistributionOAC
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          ViewerProtocolPolicy: redirect-to-https
          TargetOriginId: AppBucket
          CachePolicyId: !Ref DefaultCachePolicy

  DistributionOAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties: 
      OriginAccessControlConfig: 
          Name: ExampleOAC
          OriginAccessControlOriginType: s3
          SigningBehavior: always
          SigningProtocol: sigv4

  AppBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: 'test-spa-stack-bucket-app'
      PublicAccessBlockConfiguration:
        BlockPublicAcls : false
        BlockPublicPolicy : false
        IgnorePublicAcls : false
        RestrictPublicBuckets : false
              
  DefaultCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties: 
      CachePolicyConfig: 
        Name: test-cache-policy
        DefaultTTL: 10
        MaxTTL: 10
        MinTTL: 1
        ParametersInCacheKeyAndForwardedToOrigin: 
            CookiesConfig: 
              CookieBehavior: none
            EnableAcceptEncodingBrotli: true
            EnableAcceptEncodingGzip: true
            HeadersConfig: 
              HeaderBehavior: none
            QueryStringsConfig: 
              QueryStringBehavior: none

2

Answers


  1. Chosen as BEST ANSWER

    Ok, I found a YT video that worked from cloud console, then I replicated it via CloudFormation stack template.

    I think that my problem could be caused from either of these configurations:

    • bucket encription: I read somewhere (sorry I can't find the exact page) that CloudFront distribution cannot (directly) read bucket objects if encripted with anything different from SSE-S3, and since I omitted that configuration it probably did fallback on a non-compatible configuration
    • objects ownership: here too I forgot to add the cuket owner enforced policy
    • distribution accepted http version: here I accepted v.2

    working template

    Resources:
      BucketPolicy:
        Type: 'AWS::S3::BucketPolicy'
        DependsOn:
          - AppBucket
          - CloudFrontDistribution
        Properties:
          Bucket: !Ref AppBucket
          PolicyDocument:
            Id: PolicyForCloudFrontPrivateContent
            Version: '2008-10-17'
            Statement:
              Sid: AllowCloudFrontServicePrincipal
              Action: s3:GetObject
              Effect: Allow
              Principal:
                Service: cloudfront.amazonaws.com
              Condition:
                StringEquals:
                  AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
              Resource: !Sub arn:aws:s3:::${AppBucket}/*
    
      CloudFrontDistribution:
        Type: AWS::CloudFront::Distribution
        DependsOn:
          - AppBucket
          - DistributionOAC
          - LogsBucket
        Properties:
          DistributionConfig:
            Enabled: true
            HttpVersion: http2
            Origins:
              - Id: AppBucket
                DomainName: !GetAtt AppBucket.DomainName
                S3OriginConfig: {}
                OriginAccessControlId: !Ref DistributionOAC
            DefaultRootObject: index.html
            DefaultCacheBehavior:
              Compress: true
              ViewerProtocolPolicy: allow-all
              TargetOriginId: AppBucket
              CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6   # default CachingOptimized
            Logging:
              Bucket: !GetAtt LogsBucket.DomainName
              IncludeCookies: true
              Prefix: distribution/
    
      AppBucket:
        Type: 'AWS::S3::Bucket'
        DependsOn:
          - LogsBucket
        Properties:
          BucketName: 'test-spa-stack-bucket-app'
          OwnershipControls:
            Rules:
              - ObjectOwnership: BucketOwnerEnforced
          PublicAccessBlockConfiguration:
            BlockPublicAcls : true
            BlockPublicPolicy : true
            IgnorePublicAcls : true
            RestrictPublicBuckets : true
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: AES256
          LoggingConfiguration:
            DestinationBucketName: !Ref LogsBucket
            LogFilePrefix: app/
    
      DistributionOAC:
        Type: AWS::CloudFront::OriginAccessControl
        Properties: 
          OriginAccessControlConfig: 
              Name: test-spa-distribution-oac
              OriginAccessControlOriginType: s3
              SigningBehavior: always
              SigningProtocol: sigv4
    
    
      LogsBucket:
        Type: 'AWS::S3::Bucket'
        Properties:
          BucketName: 'test-spa-stack-bucket-logs'
          PublicAccessBlockConfiguration:
            BlockPublicAcls : true
            BlockPublicPolicy : true
            IgnorePublicAcls : true
            RestrictPublicBuckets : true
          AccessControl: LogDeliveryWrite
          VersioningConfiguration:
            Status: Enabled
          BucketEncryption:
            ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: 'AES256'
    

  2. You aren’t to have much luck without using legacy OAI (for no reason).
    So, here’s a policy you can use for target S3 bucket after you create the OAI right there in Cloudfront:

        {
        "Version": "2008-10-17",
        "Id": "PolicyForCloudFrontPrivateContent",
        "Statement": [
            {
                "Sid": "1",
                "Effect": "Allow",
                "Principal": {
                    "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity INSERT_OAI_NUMBER_HERE"
                },
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::BUCKET_NAME_HERE/*"
            }
        ]
    }
    

    After you do that, it should return expected content.
    If it does work for you and you start asking why, you’d better find something better to do.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search