Cloudfront 提供通过 AWS CDK Python 为 S3 存储桶来源创建的访问被拒绝响应,无需公共访问

Posted

技术标签:

【中文标题】Cloudfront 提供通过 AWS CDK Python 为 S3 存储桶来源创建的访问被拒绝响应,无需公共访问【英文标题】:Cloudfront give Access denied response created through AWS CDK Python for S3 bucket origin without public Access 【发布时间】:2020-07-09 08:54:45 【问题描述】:

使用 AWS CDK 为 S3 存储桶创建 Cloud Front Web 分发,无需公共访问。 能够创建 Origin 访问身份并进行部署,但成功部署后,我在浏览器上收到拒绝访问响应。

Grant Read Permissions on Bucket from Origin 设置将设置为 No,手动设置为 Yes 一切正常,但此设置需要通过 AWS CDK 和 python 实现。 下面是我的代码。

from aws_cdk import aws_cloudfront as front, aws_s3 as s3

class CloudFrontStack(core.Stack):        
    def __init__(self, scope: core.Construct, idx: str, **kwargs) -> None:
        super().__init__(scope, idx, **kwargs)

        bucket = s3.Bucket.from_bucket_name(self, 'CloudFront',bucket_name="bucket_name")

        oia = aws_cloudfront.OriginAccessIdentity(self, 'OIA', comment="Created By CDK")
        bucket.grant_read(oia)

        s3_origin_source = aws_cloudfront.S3OriginConfig(s3_bucket_source=bucket, origin_access_identity=oia)

        source_config = aws_cloudfront.SourceConfiguration(s3_origin_source=s3_origin_source,
                                                           origin_path="bucket_path",
                                                           behaviors=[aws_cloudfront.Behavior(is_default_behavior=True)])

        aws_cloudfront.CloudFrontWebDistribution(self, "cloud_front_name",
                                                 origin_configs=[source_config],
                                                 comment='Cloud Formation created',
                                                 default_root_object='index.html')

我也尝试将权限添加到如下,但仍然没有成功。

policyStatement = aws_iam.PolicyStatement()
policyStatement.add_resources()

policyStatement.add_actions('s3:GetBucket*');
policyStatement.add_actions('s3:GetObject*');
policyStatement.add_actions('s3:List*');
policyStatement.add_resources(bucket.bucket_arn);
policyStatement.add_canonical_user_principal(oia.cloud_front_origin_access_identity_s3_canonical_user_id);
code_bucket.add_to_resource_policy(policyStatement);

【问题讨论】:

“我也尝试将权限添加到如下,但仍然没有运气。” 什么运气不好?从控制台观察到,这实际上是否按照您的预期设置了存储桶策略? 不,这没有设置存储桶策略,我想知道代码有什么问题,为什么它没有设置存储桶策略,其他的,为什么云前端没有设置存储桶策略,我们可以从原始设置中的云前端控制台进行设置,CDK 中没有同样的设置 我不相信 Cloudfront 控制台中显示的选项在 Cloudformation 中也可用。它不会出现在 CDK 中 .. AFAIK 【参考方案1】:

我试图模仿这一点,并能够成功地将 Cloudfront 分发集成到私有 S3 存储桶中。但是,我使用 TS 作为我的堆栈。我相信将下面的代码与 Python 版本相关联会很容易。假设dist中有一个index.html文件

aws-cdk v1.31.0(2020 年 3 月 29 日最新)

import  App, Stack, StackProps  from '@aws-cdk/core';
import  BucketDeployment, Source  from '@aws-cdk/aws-s3-deployment';
import  CloudFrontWebDistribution, OriginAccessIdentity  from '@aws-cdk/aws-cloudfront';
import  BlockPublicAccess, Bucket, BucketEncryption  from '@aws-cdk/aws-s3';

export class HelloCdkStack extends Stack 
  constructor(scope: App, id: string, props?: StackProps) 
    super(scope, id, props);

    const myFirstBucket = new Bucket(this, 'MyFirstBucket', 
      versioned: true,
      encryption: BucketEncryption.S3_MANAGED,
      bucketName: 'cdk-example-bucket-for-test',
      websiteIndexDocument: 'index.html',
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL
    );

    new BucketDeployment(this, 'DeployWebsite', 
      sources: [Source.asset('dist')],
      destinationBucket: myFirstBucket
    );

    const oia = new OriginAccessIdentity(this, 'OIA', 
      comment: "Created by CDK"
    );
    myFirstBucket.grantRead(oia);

    new CloudFrontWebDistribution(this, 'cdk-example-distribution', 
      originConfigs: [
        
          s3OriginSource: 
            s3BucketSource: myFirstBucket,
            originAccessIdentity: oia
          ,
          behaviors: [
             isDefaultBehavior: true 
          ]
        
      ]
    );
  

== 更新 == [没有虚拟主机的 S3 存储桶]

这是一个示例,其中 S3 用作没有 Web 托管的 Origin。它按预期工作。

import  App, Stack, StackProps  from '@aws-cdk/core';
import  BucketDeployment, Source  from '@aws-cdk/aws-s3-deployment';
import  CloudFrontWebDistribution, OriginAccessIdentity  from '@aws-cdk/aws-cloudfront';
import  BlockPublicAccess, Bucket, BucketEncryption  from '@aws-cdk/aws-s3';

export class CloudfrontS3Stack extends Stack 
  constructor(scope: App, id: string, props?: StackProps) 
    super(scope, id, props);

    // Create bucket (which is not a static website host), encrypted AES-256 and block all public access
    // Only Cloudfront access to S3 bucket
    const testBucket = new Bucket(this, 'TestS3Bucket', 
      encryption: BucketEncryption.S3_MANAGED,
      bucketName: 'cdk-static-asset-dmahapatro',
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL
    );

    // Create Origin Access Identity to be use Canonical User Id in S3 bucket policy
    const originAccessIdentity = new OriginAccessIdentity(this, 'OAI', 
      comment: "Created_by_dmahapatro"
    );
    testBucket.grantRead(originAccessIdentity);

    // Create Cloudfront distribution with S3 as Origin
    const distribution = new CloudFrontWebDistribution(this, 'cdk-example-distribution', 
      originConfigs: [
        
          s3OriginSource: 
            s3BucketSource: testBucket,
            originAccessIdentity: originAccessIdentity
          ,
          behaviors: [
             isDefaultBehavior: true 
          ]
        
      ]
    );

    // Upload items in bucket and provide distribution to create invalidations
    new BucketDeployment(this, 'DeployWebsite', 
      sources: [Source.asset('dist')],
      destinationBucket: testBucket,
      distribution,
      distributionPaths: ['/images/*.png']
    );
  

== 更新 == [S3 存储桶导入而不是在同一个堆栈中创建]

当我们引用现有的 S3 存储桶时,可以重新创建问题。

原因: 问题的根本原因在于here in this line of code。 autoCreatePolicywill always be false for an imported S3 bucket。 要使 addResourcePolicy 工作,导入的存储桶必须已经有一个现有的存储桶策略,以便可以附加新的策略语句,或者手动创建新的 BucketPolicy 并添加策略语句。在下面的代码中,我手动创建了存储桶策略并添加了所需的策略语句。这与github issue #941 非常接近,但细微差别在于在堆栈中创建存储桶与导入已创建存储桶之间的区别。

import  App, Stack, StackProps  from '@aws-cdk/core';
import  CloudFrontWebDistribution, OriginAccessIdentity  from '@aws-cdk/aws-cloudfront';
import  Bucket, BucketPolicy  from '@aws-cdk/aws-s3';
import  PolicyStatement  from '@aws-cdk/aws-iam';

export class CloudfrontS3Stack extends Stack 
  constructor(scope: App, id: string, props?: StackProps) 
    super(scope, id, props);

    const testBucket = Bucket.fromBucketName(this, 'TestBucket', 'dmahapatro-personal-bucket');

    // Create Origin Access Identity to be use Canonical User Id in S3 bucket policy
    const originAccessIdentity = new OriginAccessIdentity(this, 'OAI', 
      comment: "Created_by_dmahapatro"
    );

    // This does not seem to work if Bucket.fromBucketName is used
    // It works for S3 buckets which are created as part of this stack
    // testBucket.grantRead(originAccessIdentity);

    // Explicitly add Bucket Policy 
    const policyStatement = new PolicyStatement();
    policyStatement.addActions('s3:GetBucket*');
    policyStatement.addActions('s3:GetObject*');
    policyStatement.addActions('s3:List*');
    policyStatement.addResources(testBucket.bucketArn);
    policyStatement.addResources(`$testBucket.bucketArn/*`);
    policyStatement.addCanonicalUserPrincipal(originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId);

    // testBucket.addToResourcePolicy(policyStatement);

    // Manually create or update bucket policy
    if( !testBucket.policy ) 
      new BucketPolicy(this, 'Policy',  bucket: testBucket ).document.addStatements(policyStatement);
     else 
      testBucket.policy.document.addStatements(policyStatement);
    

    // Create Cloudfront distribution with S3 as Origin
    const distribution = new CloudFrontWebDistribution(this, 'cdk-example-distribution', 
      originConfigs: [
        
          s3OriginSource: 
            s3BucketSource: testBucket,
            originAccessIdentity: originAccessIdentity
          ,
          behaviors: [
             isDefaultBehavior: true 
          ]
        
      ]
    );
  

【讨论】:

上面的代码示例使用静态网站托管创建 S3 存储桶,但这对我来说不是必需的,我只是在 python 中实现了相同的工作,当我们通过 CDK 创建一个新存储桶并通过 CDK 上传.在我的情况下,我已经创建了存储桶并上传了内容,在这种情况下它仍然失败,你能帮我解决现有存储桶和存储桶中现有的代码路径吗? 是的,我可以帮忙。我今天会为你模仿一些东西。 @santosh 我已经更新了我的答案。我不想单独或通过控制台创建 S3 存储桶。你想让我在我的方法中专门使用s3.Bucket.from_bucket_name吗?如果仍然有问题,那么尝试像上面那样创建一个堆栈,看看它是否有所作为。 哇!!!,非常感谢它的工作,但这不是 AWS CDK 的问题 不幸的是,您的最后一个 sn-p 对我不起作用,即使导入的存储桶上存在现有策略,testBucket.policy 也是未定义的。所以策略总是被覆盖。我仍在寻找解决方法

以上是关于Cloudfront 提供通过 AWS CDK Python 为 S3 存储桶来源创建的访问被拒绝响应,无需公共访问的主要内容,如果未能解决你的问题,请参考以下文章

aws cdk 可以提供远程状态吗?

不可预测的 Cloudfront 502 错误

AWS Cloudfront 将 JavaScript 模块作为错误的 MIME 类型(“Text/Plain”)提供服务

AWS Cloudfront 将 JavaScript 模块作为错误的 MIME 类型(“Text/Plain”)提供服务

使用 AWS CDK 在 AWS Codepipeline 中部署 Python Lambda 函数

AWS Cloudfront 备用域名 403 被禁止