Skip to content

Commit 8d37c8d

Browse files
authored
add section to README on migrating from OAI to OAC (#31247)
- add section to README on migrating from OAI to OAC - update sections in README for setting up OAI and OAC using imported buckets (user needs to manually update the bucket policy) - update warnings to match the correct README section title
2 parents 69f10ad + 5d6d0d4 commit 8d37c8d

File tree

4 files changed

+222
-114
lines changed

4 files changed

+222
-114
lines changed

packages/aws-cdk-lib/aws-cloudfront-origins/README.md

+210-95
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ S3 buckets, Elastic Load Balancing v2 load balancers, or any other domain name.
55

66
## S3 Bucket
77

8-
An S3 bucket can be used as an origin. An S3 bucket origin can either be configured as a standard bucket or as a website endpoint (see [Use an S3 Bucket](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html#using-s3-as-origin)).
8+
An S3 bucket can be used as an origin. An S3 bucket origin can either be configured as a standard bucket or as a website endpoint (see AWS docs for [Using an S3 Bucket](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html#using-s3-as-origin)).
99

1010
### Standard S3 Bucket
1111

@@ -37,50 +37,91 @@ new cloudfront.Distribution(this, 'myDist', {
3737

3838
> Note: `S3Origin` has been deprecated. Use `S3BucketOrigin` for standard S3 origins and `S3StaticWebsiteOrigin` for static website S3 origins.
3939
40-
## Migrating from OAI to OAC
40+
### Restricting access to S3 Origin
4141

42-
If you are currently using OAI for your S3 origin and wish to migrate to OAC,
43-
replace the `S3Origin` construct (now deprecated) with `S3BucketOrigin.withOriginAccessControl()` which automatically
44-
creates and sets up a OAC for you.
45-
The OAI will be deleted as part of the
46-
stack update. The logical IDs of the resources managed by
47-
the stack (i.e. distribution, bucket) will be unchanged. Run `cdk diff` before deploying to verify the
48-
changes to your stack.
42+
CloudFront provides two ways to send authenticated requests to an Amazon S3 origin:
43+
origin access control (OAC) and origin access identity (OAI).
44+
OAI is considered legacy due to limited functionality and regional
45+
limitations, whereas OAC is recommended because it supports all Amazon S3
46+
buckets in all AWS Regions, Amazon S3 server-side encryption with AWS KMS (SSE-KMS), and dynamic requests (PUT and DELETE) to Amazon S3. Additionally,
47+
OAC provides stronger security posture with short term credentials,
48+
and more frequent credential rotations as compared to OAI. OAI and OAC can be used in conjunction with a bucket that is not public to
49+
require that your users access your content using CloudFront URLs and not S3 URLs directly.
4950

50-
Existing setup using OAI and `S3Origin`:
51+
See AWS docs on [Restricting access to an Amazon S3 Origin](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html) for more details.
52+
53+
> Note: OAC and OAI can only be used with an regular S3 bucket origin (not a bucket configured as a website endpoint).
54+
55+
The `S3BucketOrigin` class supports creating a S3 origin with OAC, OAI, and no access control (using your bucket access settings) via
56+
the `withOriginAccessControl()`, `withOriginAccessIdentity()`, and `withBucketDefaults()` methods respectively.
57+
58+
#### Setting up a new origin access control (OAC)
59+
60+
Setup an S3 origin with origin access control as follows:
5161

5262
```ts
5363
const myBucket = new s3.Bucket(this, 'myBucket');
5464
new cloudfront.Distribution(this, 'myDist', {
55-
defaultBehavior: { origin: new origins.S3Origin(myBucket) },
65+
defaultBehavior: {
66+
origin: origins.S3BucketOrigin.withOriginAccessControl(myBucket) // Automatically creates a S3OriginAccessControl construct
67+
},
5668
});
5769
```
5870

59-
Updated setup using `S3BucketOrigin.withOriginAccessControl()`:
71+
When creating a S3 origin using `origins.S3BucketOrigin.withOriginAccessControl()`, an [Origin Access Control resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-originaccesscontrol-originaccesscontrolconfig.html) is automatically created with the origin type set to `s3` and signing behavior set to `always`.
72+
73+
You can grant read, write or delete access to the OAC using the `originAccessLevels` property:
74+
75+
```ts
76+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(myBucket, {
77+
originAccessLevels: [cloudfront.AccessLevel.READ, cloudfront.AccessLevel.WRITE, cloudfront.AccessLevel.DELETE],
78+
});
79+
```
80+
81+
You can also pass in a custom S3 origin access control:
6082

6183
```ts
6284
const myBucket = new s3.Bucket(this, 'myBucket');
85+
const oac = new cloudfront.S3OriginAccessControl(this, 'MyOAC', {
86+
signing: cloudfront.Signing.SIGV4_NO_OVERRIDE
87+
});
88+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(bucket, {
89+
originAccessControl: oac
90+
}
91+
)
6392
new cloudfront.Distribution(this, 'myDist', {
64-
defaultBehavior: { origin: origins.S3BucketOrigin.withOriginAccessControl(myBucket) },
93+
defaultBehavior: {
94+
origin: s3Origin
95+
},
6596
});
6697
```
6798

68-
For more information, see [Migrating from origin access identity (OAI) to origin access control (OAC)](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#migrate-from-oai-to-oac).
99+
An existing S3 origin access control can be imported using the `fromOriginAccessControlId` method:
69100

70-
### Using pre-existing S3 buckets
101+
```ts
102+
const importedOAC = cloudfront.S3OriginAccessControl.fromOriginAccessControlId(this, 'myImportedOAC', {
103+
originAccessControlId: 'ABC123ABC123AB',
104+
});
105+
```
106+
107+
> [Note](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html): When you use OAC with S3
108+
bucket origins, the bucket's object ownership must be set to Bucket owner enforced (default for new S3 buckets), or Bucket owner preferred (only if you require ACLs).
109+
110+
#### Setting up OAC with imported S3 buckets
71111

72112
If you are using an imported bucket for your S3 Origin and want to use OAC,
73113
you will need to update
74-
the S3 bucket policy manually. CDK apps cannot modify the configuration of imported constructs. After deploying the distribution, add the following
114+
the S3 bucket policy manually to allow the OAC to access the S3 origin. Like most imported resources, CDK apps cannot modify the configuration of imported buckets.
115+
116+
After deploying the distribution, add the following
75117
policy statement to your
76118
S3 bucket to allow CloudFront read-only access
77-
(or additional permissions as required):
119+
(or additional S3 permissions as required):
78120

79121
```json
80122
{
81123
"Version": "2012-10-17",
82124
"Statement": {
83-
"Sid": "GrantOACAccessToS3",
84125
"Effect": "Allow",
85126
"Principal": {
86127
"Service": "cloudfront.amazonaws.com"
@@ -96,60 +137,57 @@ S3 bucket to allow CloudFront read-only access
96137
}
97138
```
98139

99-
> Note: If your bucket previously used OAI, you will need to manually remove the policy statement
100-
that gives the OAI access to your bucket from your bucket policy.
101-
140+
See CloudFront docs on [Giving the origin access control permission to access the S3 bucket](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#create-oac-overview-s3) for more details.
102141

103-
CloudFront provides two ways to send authenticated requests to an Amazon S3 origin:
104-
origin access control (OAC) and origin access identity (OAI).
105-
OAI is considered legacy due to limited functionality and regional
106-
limitations, whereas OAC is recommended because it supports All Amazon S3
107-
buckets in all AWS Regions, Amazon S3 server-side encryption with AWS KMS (SSE-KMS), and dynamic requests (PUT and DELETE) to Amazon S3. Additionally,
108-
OAC provides stronger security posture with short term credentials,
109-
and more frequent credential rotations as compared to OAI.
110-
(see [Restricting access to an Amazon S3 Origin](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html)).
111-
OAI and OAC can be used in conjunction with a bucket that is not public to
112-
require that your users access your content using CloudFront URLs and not S3 URLs directly.
113-
114-
> Note: OAC and OAI can only be used with an regular S3 bucket origin (not a bucket configured as a website endpoint).
142+
> Note: If your bucket previously used OAI, you will need to manually remove the policy statement
143+
that gives the OAI access to your bucket after setting up OAC.
115144

116-
The `S3BucketOrigin` class supports creating a S3 origin with OAC, OAI, and no access control (using your bucket access settings) via
117-
the `withOriginAccessControl()`, `withOriginAccessIdentity()`, and `withBucketDefaults()` methods respectively.
145+
#### Using OAC for a SSE-KMS encrypted S3 origin
118146

119-
Setup an S3 origin with origin access control as follows:
147+
If the objects in the S3 bucket origin are encrypted using server-side encryption with
148+
AWS Key Management Service (SSE-KMS), the OAC must have permission to use the KMS key.
149+
Setting up an S3 origin using `S3BucketOrigin.withOriginAccessControl()` will automatically add the statement to the KMS key policy
150+
to give the OAC permission to use the KMS key.
151+
For imported keys, you will need to manually update the
152+
key policy yourself as CDK apps cannot modify the configuration of imported resources.
120153

121154
```ts
122-
const myBucket = new s3.Bucket(this, 'myBucket');
123-
new cloudfront.Distribution(this, 'myDist', {
124-
defaultBehavior: {
125-
origin: origins.S3BucketOrigin.withOriginAccessControl(myBucket) // Automatically creates an OAC
126-
},
155+
const myKmsKey = new kms.Key(this, 'myKMSKey');
156+
const myBucket = new s3.Bucket(this, 'mySSEKMSEncryptedBucket', {
157+
encryption: s3.BucketEncryption.KMS,
158+
encryptionKey: kmsKey,
159+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
127160
});
128-
```
129-
130-
You can also pass in a custom S3 origin access control:
131-
132-
```ts
133-
const myBucket = new s3.Bucket(this, 'myBucket');
134-
const oac = new cloudfront.S3OriginAccessControl(this, 'MyOAC', { signing: cloudfront.Signing.SIGV4_NO_OVERRIDE });
135-
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(
136-
bucket, { originAccessControl: oac }
137-
)
138161
new cloudfront.Distribution(this, 'myDist', {
139162
defaultBehavior: {
140-
origin: s3Origin
163+
origin: origins.S3BucketOrigin.withOriginAccessControl(myBucket) // Automatically grants Distribution access to `myKmsKey`
141164
},
142165
});
143166
```
144167

145-
An existing S3 origin access control can be imported using the `fromOriginAccessControlId` method:
168+
If the S3 bucket has an `encryptionKey` defined, `S3BucketOrigin.withOriginAccessControl()`
169+
will update the KMS key policy by appending the following policy statement to allow CloudFront read-only access (unless otherwise specified in the `originAccessLevels` property):
146170

147-
```ts
148-
const importedOAC = cloudfront.S3OriginAccessControl.fromOriginAccessControlId(this, 'myImportedOAC', {
149-
originAccessControlId: 'ABC123ABC123AB',
150-
});
171+
```json
172+
{
173+
"Statement": {
174+
"Effect": "Allow",
175+
"Principal": {
176+
"Service": "cloudfront.amazonaws.com"
177+
},
178+
"Action": "kms:Decrypt",
179+
"Resource": "arn:aws:kms:::key/<key ID>",
180+
"Condition": {
181+
"ArnLike": {
182+
"AWS:SourceArn": "arn:aws:cloudfront::<account ID>:distribution/*"
183+
}
184+
}
185+
}
186+
}
151187
```
152188

189+
#### Setting up an OAI (legacy)
190+
153191
Setup an S3 origin with origin access identity (legacy) as follows:
154192

155193
```ts
@@ -178,7 +216,38 @@ new cloudfront.Distribution(this, 'myDist', {
178216
});
179217
```
180218

181-
To setup an S3 origin with no access control:
219+
#### Setting up OAI with imported S3 buckets (legacy)
220+
221+
If you are using an imported bucket for your S3 Origin and want to use OAI,
222+
you will need to update
223+
the S3 bucket policy manually to allow the OAI to access the S3 origin. Like most imported resources, CDK apps cannot modify the configuration of imported buckets.
224+
225+
Add the following
226+
policy statement to your
227+
S3 bucket to allow the OAI read access:
228+
229+
```json
230+
{
231+
"Version": "2012-10-17",
232+
"Id": "PolicyForCloudFrontPrivateContent",
233+
"Statement": [
234+
{
235+
"Effect": "Allow",
236+
"Principal": {
237+
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
238+
},
239+
"Action": "s3:GetObject",
240+
"Resource": "arn:aws:s3:::<S3 bucket name>/*"
241+
}
242+
]
243+
}
244+
```
245+
246+
See AWS docs on [Giving an origin access identity permission to read files in the Amazon S3 bucket](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#private-content-restricting-access-to-s3-oai) for more details.
247+
248+
### Setting up a S3 origin with no origin access control
249+
250+
To setup an S3 origin with no access control (no OAI nor OAC), use `origins.S3BucketOrigin.withBucketDefaults()`:
182251

183252
```ts
184253
const myBucket = new s3.Bucket(this, 'myBucket');
@@ -189,54 +258,100 @@ new cloudfront.Distribution(this, 'myDist', {
189258
});
190259
```
191260

192-
> [Note](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html): When you use OAC with S3
193-
bucket origins you must set the bucket's object ownership to Bucket owner enforced, or Bucket owner preferred (only if you require ACLs).
261+
### Migrating from OAI to OAC
194262

195-
#### Using OAC for a SSE-KMS encrypted S3 origin
263+
If you are currently using OAI for your S3 origin and wish to migrate to OAC,
264+
replace the `S3Origin` construct (deprecated) with `S3BucketOrigin.withOriginAccessControl()` which automatically
265+
creates and sets up an OAC for you.
196266

197-
If the objects in the S3 bucket origin are encrypted using server-side encryption with
198-
AWS Key Management Service (SSE-KMS), the OAC must have permission to use the AWS KMS key.
199-
Setting up an S3 origin using `S3BucketOrigin.withOriginAccessControl()` will automatically add the statement to the KMS key policy
200-
to give the OAC permission to use the KMS key.
201-
For imported keys, you will need to manually update the
202-
key policy yourself as CDK apps cannot modify the configuration of imported resources.
267+
Existing setup using OAI and `S3Origin`:
203268

204269
```ts
205-
const myKmsKey = new kms.Key(this, 'myKMSKey');
206-
const myBucket = new s3.Bucket(this, 'mySSEKMSEncryptedBucket', {
207-
encryption: s3.BucketEncryption.KMS,
208-
encryptionKey: kmsKey,
209-
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
210-
});
211-
new cloudfront.Distribution(this, 'myDist', {
212-
defaultBehavior: {
213-
origin: origins.S3BucketOrigin.withOriginAccessControl(myBucket) // Automatically grants Distribution access to `myKmsKey`
214-
},
270+
const myBucket = new s3.Bucket(this, 'myBucket');
271+
const s3Origin = new origins.S3Origin(myBucket);
272+
const distribution = new cloudfront.Distribution(this, 'myDist', {
273+
defaultBehavior: { origin: s3Origin },
215274
});
216275
```
217276

218-
If the S3 bucket has an `encryptionKey` defined, `S3BucketOrigin.withOriginAccessControl()`
219-
will update the KMS key policy by appending the following policy statement to allow CloudFront read-only access (unless otherwise specified in the `originAccessLevels` property):
277+
**Step 1:**
220278

221-
```json
222-
{
223-
"Statement": {
224-
"Sid": "GrantOACAccessToKMS",
225-
"Effect": "Allow",
226-
"Principal": {
227-
"Service": "cloudfront.amazonaws.com"
228-
},
229-
"Action": "kms:Decrypt",
230-
"Resource": "arn:aws:kms:::key/<key ID>",
231-
"Condition": {
232-
"StringEquals": {
233-
"AWS:SourceArn": "arn:aws:cloudfront::<account ID>:distribution/<CloudFront distribution ID>"
234-
}
235-
}
279+
To ensure CloudFront doesn't lose access to the bucket during the transition, add a statement to bucket policy to grant OAC access to S3 origin. Deploy the stack. If you are okay with downtime during the transition, you can skip this step.
280+
281+
> Tip: Run `cdk diff` before deploying to verify the
282+
changes to your stack.
283+
284+
```ts
285+
const myBucket = new s3.Bucket(this, 'myBucket');
286+
const s3Origin = new origins.S3Origin(myBucket);
287+
const distribution = new cloudfront.Distribution(this, 'myDist', {
288+
defaultBehavior: { origin: s3Origin },
289+
});
290+
291+
// Construct the bucket policy statement
292+
const distributionArn = stack.formatArn(
293+
{
294+
service: 'cloudfront',
295+
region: '',
296+
resource: 'distribution',
297+
resourceName: distribution.distributionId,
298+
arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME
299+
}
300+
);
301+
302+
const cloudfrontSP = new iam.ServicePrincipal('cloudfront.amazonaws.com');
303+
304+
const oacBucketPolicyStatement = new iam.PolicyStatement(
305+
{
306+
effect: iam.Effect.ALLOW,
307+
principals: [cloudfrontSP],
308+
actions: ['s3:GetObject'],
309+
resources: [bucket.arnForObjects('*')],
310+
conditions: {
311+
"StringEquals": {
312+
"AWS:SourceArn": distributionArn
313+
}
236314
}
237-
}
315+
}
316+
)
317+
318+
// Add statement to bucket policy
319+
bucket.addToResourcePolicy(oacBucketPolicyStatement);
320+
```
321+
322+
The following changes will take place:
323+
324+
1. The bucket policy will be modified to grant the CloudFront distribution access. At this point the bucket policy allows both an OAI and an OAC to access the S3 origin.
325+
326+
**Step 2:**
327+
328+
Replace `S3Origin` with `S3BucketOrigin.withOriginAccessControl()`, which creates an OAC and attaches it to the distribution. You can remove the code from Step 1 which updated the bucket policy, as `S3BucketOrigin.withOriginAccessControl()` updates the bucket policy automatically with the same statement when defined in the `Distribution` (no net difference).
329+
330+
Run `cdk diff` before deploying to verify the changes to your stack.
331+
332+
```ts
333+
const bucket = new s3.Bucket(stack, 'Bucket');
334+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(bucket);
335+
const distribution = new cloudfront.Distribution(stack, 'Distribution', {
336+
defaultBehavior: { origin: s3Origin },
337+
});
238338
```
239339

340+
The following changes will take place:
341+
342+
1. A `AWS::CloudFront::OriginAccessControl` resource will be created.
343+
2. The `Origin` property of the `AWS::CloudFront::Distribution` will set [`OriginAccessControlId`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html#cfn-cloudfront-distribution-origin-originaccesscontrolid) to the OAC ID after it is created. It will also set [`S3OriginConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-s3originconfig.html#aws-properties-cloudfront-distribution-s3originconfig-properties) to `{"OriginAccessIdentity": ""}`, which deletes the origin access identity from the existing distribution.
344+
3. The `AWS::CloudFront::CloudFrontOriginAccessIdentity` resource will be deleted.
345+
346+
**Will migrating from OAI to OAC cause any resource replacement?**
347+
348+
No, following the migration steps does not cause any replacement of the existing `AWS::CloudFront::Distribution`, `AWS::S3::Bucket` nor `AWS::S3::BucketPolicy` resources. It will modify the bucket policy, create a `AWS::CloudFront::OriginAccessControl` resource, and delete the existing `AWS::CloudFront::CloudFrontOriginAccessIdentity`.
349+
350+
**Will migrating from OAI to OAC have any availability implications for my application?**
351+
While the above steps follow the order recommended by CloudFront, updates to CloudFront distributions and S3 bucket policies can take some time to propagate globally. Bucket configuration updates are eventually consistent. As such, you should be aware there is a possibility of downtime.
352+
353+
For more information, see [Migrating from origin access identity (OAI) to origin access control (OAC)](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#migrate-from-oai-to-oac).
354+
240355
### Adding Custom Headers
241356

242357
You can configure CloudFront to add custom headers to the requests that it sends to your origin. These custom headers enable you to send and gather information from your origin that you don’t get with typical viewer requests. These headers can even be customized for each origin. CloudFront supports custom headers for both for custom and Amazon S3 origins.

0 commit comments

Comments
 (0)