问题
Created Cloud Front web distribution with AWS CDK for S3 bucket without public access. Able to create Origin access identity, and deploy but on successful deploy i get access denied response on browser.
Grant Read Permissions on Bucket from Origin settings will be set to No, setting this to Yes manually everything will work fine, but this setting needs to be achieved through AWS CDK and python. Below is my code.
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')
I also tried adding the permissions to the as below but still no luck.
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);
回答1:
I tried to mimic this and was able to integrate Cloudfront distribution to a private S3 bucket successfully. However, I used TS for my stack. I am sure it will be easy to correlate below code to Python version. Assume there is an index.html
file in dist
aws-cdk v1.31.0 (latest as of March 29th, 2020)
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 }
]
}
]
});
}
}
== Update == [S3 bucket without Web Hosting]
Here is an example where S3 is used as an Origin without Web hosting. It works as expected.
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']
});
}
}
== UPDATE == [S3 Bucket imported instead of creating in the same stack]
When we refer to an existing S3 bucket the issue can be recreated.
Reason:
The root cause of the issue lies here in this line of code. autoCreatePolicy
will always be false for an imported S3 bucket.
To make addResourcePolicy
work either the imported bucket has to already have an existing Bucket policy so that the new policy statements can be appended or manually create new BucketPolicy and add the policy statements. In the below code I have manually created the bucket policy and add the required policy statements. This is very close to the github issue #941 but the subtle difference is between creating a bucket in the stack vs importing an already created bucket.
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 }
]
}
]
});
}
}
来源:https://stackoverflow.com/questions/60905976/cloudfront-give-access-denied-response-created-through-aws-cdk-python-for-s3-buc