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);
By default, your Amazon S3 bucket and all the files in it are private—only the Amazon account that created the bucket has permission to read or write the files. If you want to allow anyone to access the files in your Amazon S3 bucket using CloudFront URLs, you must grant public read permissions to the objects.
Open the CloudFront console at https://console.aws.amazon.com/cloudfront/v3/home . Choose a distribution with an S3 origin that you want to add the OAC to, then choose the Origins tab. Select the S3 origin that you want to add the OAC to, then choose Edit.
For enabling CORS we need to configure Cloudfront to allow forwarding of required headers. We can configure the behavior of Cloudfront by clicking on Cloudfront Distribution's "Distribution Settings". Then from the "Behaviour" tab click on "Edit". Here we need to whitelist the headers that need to be forwarded.
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 }
]
}
]
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With