Skip to content

Commit 1d621df

Browse files
authored
Merge pull request #3 from danielcondemarin/serve-private-S3-content-through-cloudfront
Serving private S3 content through CloudFront
2 parents 4c7b4c1 + ecb49fb commit 1d621df

File tree

11 files changed

+473
-62
lines changed

11 files changed

+473
-62
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,25 @@ distribution:
8686
viewer-request: arn:aws:lambda:us-east-1:123:function:myFunc:version # lambda ARN including version
8787
```
8888
89+
#### Private S3 Content
90+
91+
To restrict access to content that you serve from S3 you can mark as `private` your S3 origins:
92+
93+
```yml
94+
# serverless.yml
95+
96+
distribution:
97+
component: '@serverless/aws-cloudfront'
98+
inputs:
99+
origins:
100+
- url: https://my-private-bucket.s3.amazonaws.com
101+
private: true
102+
```
103+
104+
A bucket policy will be added that grants CloudFront with access to the bucket objects. Note that it doesn't remove any existing permissions on the bucket. If users currently have permission to access the files in your bucket using Amazon S3 URLs you will need to manually remove those.
105+
106+
This is documented in more detail here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
107+
89108
### 4. Deploy
90109

91110
```console

__mocks__/aws-sdk.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,38 @@ const mockGetDistributionConfigPromise = promisifyMock(mockGetDistributionConfig
1919
const mockDeleteDistribution = jest.fn()
2020
const mockDeleteDistributionPromise = promisifyMock(mockDeleteDistribution)
2121

22+
const mockCreateCloudFrontOriginAccessIdentity = jest.fn()
23+
const mockCreateCloudFrontOriginAccessIdentityPromise = promisifyMock(
24+
mockCreateCloudFrontOriginAccessIdentity
25+
)
26+
27+
const mockPutBucketPolicy = jest.fn()
28+
const mockPutBucketPolicyPromise = promisifyMock(mockPutBucketPolicy)
29+
2230
module.exports = {
2331
mockCreateDistribution,
2432
mockUpdateDistribution,
2533
mockGetDistributionConfig,
2634
mockDeleteDistribution,
35+
mockCreateCloudFrontOriginAccessIdentity,
36+
mockPutBucketPolicy,
37+
38+
mockPutBucketPolicyPromise,
2739
mockCreateDistributionPromise,
2840
mockUpdateDistributionPromise,
2941
mockGetDistributionConfigPromise,
3042
mockDeleteDistributionPromise,
43+
mockCreateCloudFrontOriginAccessIdentityPromise,
3144

3245
CloudFront: jest.fn(() => ({
3346
createDistribution: mockCreateDistribution,
3447
updateDistribution: mockUpdateDistribution,
3548
getDistributionConfig: mockGetDistributionConfig,
36-
deleteDistribution: mockDeleteDistribution
49+
deleteDistribution: mockDeleteDistribution,
50+
createCloudFrontOriginAccessIdentity: mockCreateCloudFrontOriginAccessIdentity
51+
})),
52+
53+
S3: jest.fn(() => ({
54+
putBucketPolicy: mockPutBucketPolicy
3755
}))
3856
}

__tests__/__snapshots__/custom-url-origin.test.js.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Object {
77
"Items": Array [],
88
"Quantity": 0,
99
},
10+
"CacheBehaviors": Object {
11+
"Items": Array [],
12+
"Quantity": 0,
13+
},
1014
"CallerReference": "1566599541192",
1115
"Comment": "",
1216
"DefaultCacheBehavior": Object {
@@ -93,6 +97,10 @@ Object {
9397
exports[`Input origin as a custom url updates distribution 1`] = `
9498
Object {
9599
"DistributionConfig": Object {
100+
"CacheBehaviors": Object {
101+
"Items": Array [],
102+
"Quantity": 0,
103+
},
96104
"DefaultCacheBehavior": Object {
97105
"AllowedMethods": Object {
98106
"CachedMethods": Object {

__tests__/__snapshots__/s3-origin.test.js.snap

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,184 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Input origin as an S3 bucket url creates distribution with S3 origin 1`] = `
3+
exports[`S3 origins When origin is an S3 URL only accessible via CloudFront creates distribution 1`] = `
44
Object {
55
"DistributionConfig": Object {
66
"Aliases": Object {
77
"Items": Array [],
88
"Quantity": 0,
99
},
10+
"CacheBehaviors": Object {
11+
"Items": Array [],
12+
"Quantity": 0,
13+
},
14+
"CallerReference": "1566599541192",
15+
"Comment": "",
16+
"DefaultCacheBehavior": Object {
17+
"AllowedMethods": Object {
18+
"CachedMethods": Object {
19+
"Items": Array [
20+
"HEAD",
21+
"GET",
22+
],
23+
"Quantity": 2,
24+
},
25+
"Items": Array [
26+
"HEAD",
27+
"GET",
28+
],
29+
"Quantity": 2,
30+
},
31+
"Compress": false,
32+
"DefaultTTL": 86400,
33+
"FieldLevelEncryptionId": "",
34+
"ForwardedValues": Object {
35+
"Cookies": Object {
36+
"Forward": "none",
37+
},
38+
"Headers": Object {
39+
"Items": Array [],
40+
"Quantity": 0,
41+
},
42+
"QueryString": false,
43+
"QueryStringCacheKeys": Object {
44+
"Items": Array [],
45+
"Quantity": 0,
46+
},
47+
},
48+
"LambdaFunctionAssociations": Object {
49+
"Items": Array [],
50+
"Quantity": 0,
51+
},
52+
"MaxTTL": 31536000,
53+
"MinTTL": 0,
54+
"SmoothStreaming": false,
55+
"TargetOriginId": "mybucket",
56+
"TrustedSigners": Object {
57+
"Enabled": false,
58+
"Items": Array [],
59+
"Quantity": 0,
60+
},
61+
"ViewerProtocolPolicy": "redirect-to-https",
62+
},
63+
"Enabled": true,
64+
"HttpVersion": "http2",
65+
"Origins": Object {
66+
"Items": Array [
67+
Object {
68+
"CustomHeaders": Object {
69+
"Items": Array [],
70+
"Quantity": 0,
71+
},
72+
"DomainName": "mybucket.s3.amazonaws.com",
73+
"Id": "mybucket",
74+
"OriginPath": "",
75+
"S3OriginConfig": Object {
76+
"OriginAccessIdentity": "origin-access-identity/cloudfront/access-identity-xyz",
77+
},
78+
},
79+
],
80+
"Quantity": 1,
81+
},
82+
"PriceClass": "PriceClass_All",
83+
},
84+
}
85+
`;
86+
87+
exports[`S3 origins When origin is an S3 URL only accessible via CloudFront updates distribution 1`] = `
88+
Object {
89+
"DistributionConfig": Object {
90+
"Aliases": Object {
91+
"Items": Array [],
92+
"Quantity": 0,
93+
},
94+
"CacheBehaviors": Object {
95+
"Items": Array [],
96+
"Quantity": 0,
97+
},
98+
"CallerReference": "1566599541192",
99+
"Comment": "",
100+
"DefaultCacheBehavior": Object {
101+
"AllowedMethods": Object {
102+
"CachedMethods": Object {
103+
"Items": Array [
104+
"HEAD",
105+
"GET",
106+
],
107+
"Quantity": 2,
108+
},
109+
"Items": Array [
110+
"HEAD",
111+
"GET",
112+
],
113+
"Quantity": 2,
114+
},
115+
"Compress": false,
116+
"DefaultTTL": 86400,
117+
"FieldLevelEncryptionId": "",
118+
"ForwardedValues": Object {
119+
"Cookies": Object {
120+
"Forward": "none",
121+
},
122+
"Headers": Object {
123+
"Items": Array [],
124+
"Quantity": 0,
125+
},
126+
"QueryString": false,
127+
"QueryStringCacheKeys": Object {
128+
"Items": Array [],
129+
"Quantity": 0,
130+
},
131+
},
132+
"LambdaFunctionAssociations": Object {
133+
"Items": Array [],
134+
"Quantity": 0,
135+
},
136+
"MaxTTL": 31536000,
137+
"MinTTL": 0,
138+
"SmoothStreaming": false,
139+
"TargetOriginId": "mybucket",
140+
"TrustedSigners": Object {
141+
"Enabled": false,
142+
"Items": Array [],
143+
"Quantity": 0,
144+
},
145+
"ViewerProtocolPolicy": "redirect-to-https",
146+
},
147+
"Enabled": true,
148+
"HttpVersion": "http2",
149+
"Origins": Object {
150+
"Items": Array [
151+
Object {
152+
"CustomHeaders": Object {
153+
"Items": Array [],
154+
"Quantity": 0,
155+
},
156+
"DomainName": "mybucket.s3.amazonaws.com",
157+
"Id": "mybucket",
158+
"OriginPath": "",
159+
"S3OriginConfig": Object {
160+
"OriginAccessIdentity": "origin-access-identity/cloudfront/access-identity-xyz",
161+
},
162+
},
163+
],
164+
"Quantity": 1,
165+
},
166+
"PriceClass": "PriceClass_All",
167+
},
168+
}
169+
`;
170+
171+
exports[`S3 origins When origin is an S3 bucket URL creates distribution 1`] = `
172+
Object {
173+
"DistributionConfig": Object {
174+
"Aliases": Object {
175+
"Items": Array [],
176+
"Quantity": 0,
177+
},
178+
"CacheBehaviors": Object {
179+
"Items": Array [],
180+
"Quantity": 0,
181+
},
10182
"CallerReference": "1566599541192",
11183
"Comment": "",
12184
"DefaultCacheBehavior": Object {
@@ -80,9 +252,13 @@ Object {
80252
}
81253
`;
82254

83-
exports[`Input origin as an S3 bucket url updates distribution 1`] = `
255+
exports[`S3 origins When origin is an S3 bucket URL updates distribution 1`] = `
84256
Object {
85257
"DistributionConfig": Object {
258+
"CacheBehaviors": Object {
259+
"Items": Array [],
260+
"Quantity": 0,
261+
},
86262
"DefaultCacheBehavior": Object {
87263
"AllowedMethods": Object {
88264
"CachedMethods": Object {

0 commit comments

Comments
 (0)