diff --git a/s3-cloudfront-oac-cdk-python/README.md b/s3-cloudfront-oac-cdk-python/README.md new file mode 100644 index 0000000000..c3badc3c1b --- /dev/null +++ b/s3-cloudfront-oac-cdk-python/README.md @@ -0,0 +1,77 @@ +# S3 Hosted Website Served by a CloudFront Distribution restricted by CloudFront Origin Access Control (OAC) + +This repo contains serverless patterns showing how to setup a S3 website hosting bucket that is served by a CloudFront distribution that also obfuscates the CloudFront Distribution domain via CloudFront Origin Access Control (OAC). + +![Demo Project Solution Architecture Diagram](diagram.PNG) + +- Learn more about these patterns at https://serverlessland.com/patterns. +- To learn more about submitting a pattern, read the [publishing guidelines page](https://github.com/aws-samples/serverless-patterns/blob/main/PUBLISHING.md). + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) (AWS CDK) Installed and account bootstrapped + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ```bash + cd s3-cloudfront-oac-cdk-python + ``` +3. Create a virtual environment for python: + ```bash + python3 -m venv .venv + ``` +4. Activate the virtual environment: + ```bash + source .venv/bin/activate + ``` +5. Install python modules: + ```bash + python3 -m pip install -r requirements.txt + ``` +6. From the command line, use CDK to synthesize the CloudFormation template and check for errors: + ```bash + cdk synth + ``` +7. From the command line, use CDK to deploy the stack: + ```bash + cdk deploy + ``` + +## How it works + +This CDK app creates a private S3 bucket, uploads a sample `index.html` to it, and creates a CloudFront distribution in front of the bucket. CloudFront reads the bucket content through Origin Access Control (OAC), so the bucket stays private and is only accessible via CloudFront. + +## Testing + +1. Note the `DistributionId` and `DistributionDomainName` values from the outputs of the CDK deployment process. +2. Open `https://` in your browser. You should see the sample `index.html` page saying `Hello from S3 + CloudFront!`. +3. Confirm that the distribution accesses the S3 origin via Origin Access Control (OAC). + ```bash + aws cloudfront get-distribution-config --id --query 'DistributionConfig.Origins.Items[0].OriginAccessControlId' + ``` + +## Cleanup + +1. Delete the stack + ```bash + cdk destroy + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'S3CloudFrontOACStack')].StackStatus" + ``` + +---- +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/s3-cloudfront-oai-cdk-python/s3_cloudfront_oai_cdk.py b/s3-cloudfront-oac-cdk-python/app.py similarity index 54% rename from s3-cloudfront-oai-cdk-python/s3_cloudfront_oai_cdk.py rename to s3-cloudfront-oac-cdk-python/app.py index e551c433e9..692eed15db 100644 --- a/s3-cloudfront-oai-cdk-python/s3_cloudfront_oai_cdk.py +++ b/s3-cloudfront-oac-cdk-python/app.py @@ -1,5 +1,7 @@ +import aws_cdk as cdk from constructs import Construct from aws_cdk import ( + CfnOutput, RemovalPolicy, aws_s3 as s3, aws_s3_deployment as s3deploy, @@ -7,17 +9,16 @@ aws_cloudfront_origins as origins ) -class S3CloudFrontOAI(Construct): - - def __init__(self, scope: Construct, id: str, **kwargs): - super().__init__(scope, id, **kwargs) +class S3CloudFrontOAC(Construct): + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) website_bucket = s3.Bucket( self, "My-Website-Bucket", removal_policy=RemovalPolicy.DESTROY, auto_delete_objects=True, - encryption=s3.BucketEncryption.KMS, + encryption=s3.BucketEncryption.S3_MANAGED, enforce_ssl=True, versioned=True ) @@ -29,25 +30,31 @@ def __init__(self, scope: Construct, id: str, **kwargs): exposed_headers=["Access-Control-Allow-Origin"] ) - oai = cloudfront.OriginAccessIdentity( - self, - "My-OAI", - comment="My OAI for the S3 Website" - ) - - website_bucket.grant_read(oai) - - - cd = cloudfront.Distribution(self, "myCloudFrontDistribution", + distribution = cloudfront.Distribution(self, "myCloudFrontDistribution", default_root_object='index.html', default_behavior=cloudfront.BehaviorOptions( - origin=origins.S3Origin(website_bucket, origin_access_identity=oai), - origin_request_policy=cloudfront.OriginRequestPolicy.CORS_S3_ORIGIN, + origin=origins.S3BucketOrigin.with_origin_access_control(website_bucket), + origin_request_policy=cloudfront.OriginRequestPolicy.CORS_S3_ORIGIN, viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, response_headers_policy=cloudfront.ResponseHeadersPolicy.CORS_ALLOW_ALL_ORIGINS, cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED, allowed_methods=cloudfront.AllowedMethods.ALLOW_ALL - ) + ) ) - \ No newline at end of file + s3deploy.BucketDeployment( + self, + "DeployWebsite", + sources=[s3deploy.Source.asset("./website")], + destination_bucket=website_bucket, + distribution=distribution, + distribution_paths=["/*"] + ) + + CfnOutput(self, "DistributionId", value=distribution.distribution_id) + CfnOutput(self, "DistributionDomainName", value=distribution.distribution_domain_name) + +app = cdk.App() +stack = cdk.Stack(app, "S3CloudFrontOACStack") +S3CloudFrontOAC(stack, "s3-hosted-website") +app.synth() diff --git a/s3-cloudfront-oac-cdk-python/cdk.json b/s3-cloudfront-oac-cdk-python/cdk.json new file mode 100644 index 0000000000..b4baa10225 --- /dev/null +++ b/s3-cloudfront-oac-cdk-python/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "python3 app.py" +} diff --git a/s3-cloudfront-oac-cdk-python/diagram.PNG b/s3-cloudfront-oac-cdk-python/diagram.PNG new file mode 100644 index 0000000000..51d0900d01 Binary files /dev/null and b/s3-cloudfront-oac-cdk-python/diagram.PNG differ diff --git a/s3-cloudfront-oac-cdk-python/diagram.drawio.svg b/s3-cloudfront-oac-cdk-python/diagram.drawio.svg new file mode 100644 index 0000000000..32ada50175 --- /dev/null +++ b/s3-cloudfront-oac-cdk-python/diagram.drawio.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + +
+
+
+ AWS Cloud +
+
+
+
+ + AWS Cloud + +
+
+
+ + + + + + + +
+
+
+ Region +
+
+
+
+ + Region + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ Amazon S3 +
+ + Bucket + +
+
+
+
+
+ + Amazon S3... + +
+
+
+ + + + + + + + + + + + + +
+
+
+ Amazon CloudFront +
+ + Distribution + +
+
+
+
+
+ + Amazon CloudFront... + +
+
+
+ + + + + + + + + + +
+
+
+ Users +
+
+
+
+
+
+
+ + Users + +
+
+
+ + + + + + + + + +
+
+
+ CloudFront +
+ + URL + +
+
+
+
+
+ + CloudFront... + +
+
+
+ + + + + + + + + +
+
+
+ S3 URL Restricted by OAC +
+
+
+
+ + S3 URL Restricted by OAC + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/s3-cloudfront-oac-cdk-python/requirements.txt b/s3-cloudfront-oac-cdk-python/requirements.txt new file mode 100644 index 0000000000..81bf648456 --- /dev/null +++ b/s3-cloudfront-oac-cdk-python/requirements.txt @@ -0,0 +1,2 @@ +aws-cdk-lib==2.260.0 +constructs>=10.0.0,<11.0.0 diff --git a/s3-cloudfront-oac-cdk-python/website/index.html b/s3-cloudfront-oac-cdk-python/website/index.html new file mode 100644 index 0000000000..7027a81a49 --- /dev/null +++ b/s3-cloudfront-oac-cdk-python/website/index.html @@ -0,0 +1,12 @@ + + + + + + S3 + CloudFront OAC Sample + + +

Hello from S3 + CloudFront!

+

This page is served from a private S3 bucket through CloudFront using Origin Access Control (OAC).

+ + diff --git a/s3-cloudfront-oai-cdk-python/README.md b/s3-cloudfront-oai-cdk-python/README.md deleted file mode 100644 index 1b1db73630..0000000000 --- a/s3-cloudfront-oai-cdk-python/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# S3 Hosted Website Served by a CloudFront Distribution restricted by Cloudfront Origin Access Identity (OAI) - -This repo contains serverless patterns showing how to setup a S3 website hosting bucket that is served by a CloudFront distribution that also obfuscates the CloudFront Distribution domain via Cloudfront Origin Access Identity (OAI). - -![Demo Project Solution Architecture Diagram](diagram.PNG) - -- Learn more about these patterns at https://serverlessland.com/patterns. -- To learn more about submitting a pattern, read the [publishing guidelines page](https://github.com/aws-samples/serverless-patterns/blob/main/PUBLISHING.md). - -Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. - -## Requirements - -* AWS Account -* AWS CLI already configured with Administrator permission -* [NodeJS 14.x installed](https://nodejs.org/en/download/) -* CDK v2 installed: See Getting Started With the AWS CDK -* Python CDK required libraries: (install with pip install -r requirements.txt) -* Clone this repo! - -## Deployment Instructions - -1. Within your CDK Python module directory(where all your cdk stacks are located) create a constructs folder -2. Place the `s3_cloudfront_oai_cdk.py` file in the constructs folder you created -3. Import the construct into the desired stack you would like to use this construct (ex.`from .constructs.s3_website import S3CloudFrontOAI`) -4. Use this construct in your stack by defining it in your stack (ex. `myS3HostedWebsite = S3CloudFrontOAI(self, 's3-hosted-website')`) -5. In your terminal run `CDK Deploy` for the specified stack that uses this construct - -### Removing the resources - -1. run `CDK Destroy ` for the specified stack that used this construct - -``` -git clone https://github.com/aws-samples/serverless-patterns/s3-cloudfront-oai-cdk-python -``` - -Each subdirectory contains additional installation and usage instructions. - ----- -Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. ----- - diff --git a/s3-cloudfront-oai-cdk-python/diagram.PNG b/s3-cloudfront-oai-cdk-python/diagram.PNG deleted file mode 100644 index d9ff8ca3a9..0000000000 Binary files a/s3-cloudfront-oai-cdk-python/diagram.PNG and /dev/null differ