@medplum/cdk 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +4 -4
- package/dist/types/backend.d.ts +28 -0
- package/dist/types/cloudtrail.d.ts +11 -0
- package/dist/types/frontend.d.ts +9 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/oai.d.ts +4 -2
- package/dist/types/stack.d.ts +24 -0
- package/dist/types/storage.d.ts +9 -0
- package/package.json +7 -7
- package/src/backend.ts +107 -85
- package/src/cloudtrail.ts +134 -0
- package/src/frontend.ts +29 -20
- package/src/index.test.ts +38 -0
- package/src/index.ts +8 -44
- package/src/oai.ts +4 -1
- package/src/stack.ts +65 -0
- package/src/storage.ts +31 -22
package/src/frontend.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
aws_certificatemanager as acm,
|
|
4
4
|
aws_cloudfront as cloudfront,
|
|
5
5
|
Duration,
|
|
6
|
+
aws_iam as iam,
|
|
6
7
|
aws_cloudfront_origins as origins,
|
|
7
8
|
RemovalPolicy,
|
|
8
9
|
aws_route53 as route53,
|
|
@@ -21,14 +22,21 @@ import { awsManagedRules } from './waf';
|
|
|
21
22
|
* Route53 alias record, and ACM certificate.
|
|
22
23
|
*/
|
|
23
24
|
export class FrontEnd extends Construct {
|
|
25
|
+
appBucket: s3.IBucket;
|
|
26
|
+
responseHeadersPolicy?: cloudfront.IResponseHeadersPolicy;
|
|
27
|
+
waf?: wafv2.CfnWebACL;
|
|
28
|
+
apiOriginCachePolicy?: cloudfront.ICachePolicy;
|
|
29
|
+
originAccessIdentity?: cloudfront.OriginAccessIdentity;
|
|
30
|
+
originAccessPolicyStatement?: iam.PolicyStatement;
|
|
31
|
+
distribution?: cloudfront.IDistribution;
|
|
32
|
+
dnsRecord?: route53.IRecordSet;
|
|
33
|
+
|
|
24
34
|
constructor(parent: Construct, config: MedplumInfraConfig, region: string) {
|
|
25
35
|
super(parent, 'FrontEnd');
|
|
26
36
|
|
|
27
|
-
let appBucket: s3.IBucket;
|
|
28
|
-
|
|
29
37
|
if (region === config.region) {
|
|
30
38
|
// S3 bucket
|
|
31
|
-
appBucket = new s3.Bucket(this, 'AppBucket', {
|
|
39
|
+
this.appBucket = new s3.Bucket(this, 'AppBucket', {
|
|
32
40
|
bucketName: config.appDomainName,
|
|
33
41
|
publicReadAccess: false,
|
|
34
42
|
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
|
|
@@ -39,7 +47,7 @@ export class FrontEnd extends Construct {
|
|
|
39
47
|
});
|
|
40
48
|
} else {
|
|
41
49
|
// Otherwise, reference the bucket by name and region
|
|
42
|
-
appBucket = s3.Bucket.fromBucketAttributes(this, 'AppBucket', {
|
|
50
|
+
this.appBucket = s3.Bucket.fromBucketAttributes(this, 'AppBucket', {
|
|
43
51
|
bucketName: config.appDomainName,
|
|
44
52
|
region: config.region,
|
|
45
53
|
});
|
|
@@ -47,7 +55,7 @@ export class FrontEnd extends Construct {
|
|
|
47
55
|
|
|
48
56
|
if (region === 'us-east-1') {
|
|
49
57
|
// HTTP response headers policy
|
|
50
|
-
|
|
58
|
+
this.responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {
|
|
51
59
|
securityHeadersBehavior: {
|
|
52
60
|
contentSecurityPolicy: {
|
|
53
61
|
contentSecurityPolicy: [
|
|
@@ -85,7 +93,7 @@ export class FrontEnd extends Construct {
|
|
|
85
93
|
});
|
|
86
94
|
|
|
87
95
|
// WAF
|
|
88
|
-
|
|
96
|
+
this.waf = new wafv2.CfnWebACL(this, 'FrontEndWAF', {
|
|
89
97
|
defaultAction: { allow: {} },
|
|
90
98
|
scope: 'CLOUDFRONT',
|
|
91
99
|
name: `${config.stackName}-FrontEndWAF`,
|
|
@@ -98,7 +106,7 @@ export class FrontEnd extends Construct {
|
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
// API Origin Cache Policy
|
|
101
|
-
|
|
109
|
+
this.apiOriginCachePolicy = new cloudfront.CachePolicy(this, 'ApiOriginCachePolicy', {
|
|
102
110
|
cachePolicyName: `${config.stackName}-ApiOriginCachePolicy`,
|
|
103
111
|
cookieBehavior: cloudfront.CacheCookieBehavior.all(),
|
|
104
112
|
headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
|
|
@@ -115,15 +123,20 @@ export class FrontEnd extends Construct {
|
|
|
115
123
|
});
|
|
116
124
|
|
|
117
125
|
// Origin access identity
|
|
118
|
-
|
|
119
|
-
grantBucketAccessToOriginAccessIdentity(
|
|
126
|
+
this.originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
|
|
127
|
+
this.originAccessPolicyStatement = grantBucketAccessToOriginAccessIdentity(
|
|
128
|
+
this.appBucket,
|
|
129
|
+
this.originAccessIdentity
|
|
130
|
+
);
|
|
120
131
|
|
|
121
132
|
// CloudFront distribution
|
|
122
|
-
|
|
133
|
+
this.distribution = new cloudfront.Distribution(this, 'AppDistribution', {
|
|
123
134
|
defaultRootObject: 'index.html',
|
|
124
135
|
defaultBehavior: {
|
|
125
|
-
origin: new origins.S3Origin(appBucket, {
|
|
126
|
-
|
|
136
|
+
origin: new origins.S3Origin(this.appBucket, {
|
|
137
|
+
originAccessIdentity: this.originAccessIdentity,
|
|
138
|
+
}),
|
|
139
|
+
responseHeadersPolicy: this.responseHeadersPolicy,
|
|
127
140
|
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
128
141
|
},
|
|
129
142
|
additionalBehaviors: config.appApiProxy
|
|
@@ -131,7 +144,7 @@ export class FrontEnd extends Construct {
|
|
|
131
144
|
'/api/*': {
|
|
132
145
|
origin: new origins.HttpOrigin(config.apiDomainName),
|
|
133
146
|
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
|
|
134
|
-
cachePolicy: apiOriginCachePolicy,
|
|
147
|
+
cachePolicy: this.apiOriginCachePolicy,
|
|
135
148
|
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
136
149
|
},
|
|
137
150
|
}
|
|
@@ -150,7 +163,7 @@ export class FrontEnd extends Construct {
|
|
|
150
163
|
responsePagePath: '/index.html',
|
|
151
164
|
},
|
|
152
165
|
],
|
|
153
|
-
webAclId: waf.attrArn,
|
|
166
|
+
webAclId: this.waf.attrArn,
|
|
154
167
|
logBucket: config.appLoggingBucket
|
|
155
168
|
? s3.Bucket.fromBucketName(this, 'LoggingBucket', config.appLoggingBucket)
|
|
156
169
|
: undefined,
|
|
@@ -158,22 +171,18 @@ export class FrontEnd extends Construct {
|
|
|
158
171
|
});
|
|
159
172
|
|
|
160
173
|
// DNS
|
|
161
|
-
let record = undefined;
|
|
162
174
|
if (!config.skipDns) {
|
|
163
175
|
const zone = route53.HostedZone.fromLookup(this, 'Zone', {
|
|
164
176
|
domainName: config.domainName.split('.').slice(-2).join('.'),
|
|
165
177
|
});
|
|
166
178
|
|
|
167
179
|
// Route53 alias record for the CloudFront distribution
|
|
168
|
-
|
|
180
|
+
this.dnsRecord = new route53.ARecord(this, 'AppAliasRecord', {
|
|
169
181
|
recordName: config.appDomainName,
|
|
170
|
-
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
|
|
182
|
+
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(this.distribution)),
|
|
171
183
|
zone,
|
|
172
184
|
});
|
|
173
185
|
}
|
|
174
|
-
|
|
175
|
-
// Debug
|
|
176
|
-
console.log('ARecord', record?.domainName);
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
188
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -498,4 +498,42 @@ describe('Infra', () => {
|
|
|
498
498
|
expect(() => main({ config: filename })).not.toThrow();
|
|
499
499
|
unlinkSync(filename);
|
|
500
500
|
});
|
|
501
|
+
|
|
502
|
+
test('CloudTrail alarms', () => {
|
|
503
|
+
const filename = resolve('./medplum.cloudtrail.config.json');
|
|
504
|
+
writeFileSync(
|
|
505
|
+
filename,
|
|
506
|
+
JSON.stringify({
|
|
507
|
+
name: 'cloudtrail',
|
|
508
|
+
stackName: 'MedplumCloudTrailStack',
|
|
509
|
+
accountNumber: '647991932601',
|
|
510
|
+
region: 'us-east-1',
|
|
511
|
+
domainName: 'medplum.com',
|
|
512
|
+
apiPort: 8103,
|
|
513
|
+
apiDomainName: 'api.medplum.com',
|
|
514
|
+
apiSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/08bf1daf-3a2b-4cbe-91a0-739b4364a1ec',
|
|
515
|
+
appDomainName: 'app.medplum.com',
|
|
516
|
+
appSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/fd21b628-b2c0-4a5d-b4f5-b5c9a6d63b1a',
|
|
517
|
+
storageBucketName: 'medplum-storage',
|
|
518
|
+
storageDomainName: 'storage.medplum.com',
|
|
519
|
+
storageSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/19d85245-0a1d-4bf5-9789-23082b1a15fc',
|
|
520
|
+
storagePublicKey: '-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----',
|
|
521
|
+
maxAzs: 2,
|
|
522
|
+
rdsInstances: 1,
|
|
523
|
+
desiredServerCount: 1,
|
|
524
|
+
serverImage: 'medplum/medplum-server:staging',
|
|
525
|
+
serverMemory: 512,
|
|
526
|
+
serverCpu: 256,
|
|
527
|
+
cloudTrailAlarms: {
|
|
528
|
+
logGroupName: 'cloudtrail-logs',
|
|
529
|
+
logGroupCreate: true,
|
|
530
|
+
snsTopicName: 'cloudtrail-alarms',
|
|
531
|
+
},
|
|
532
|
+
}),
|
|
533
|
+
{ encoding: 'utf-8' }
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
expect(() => main({ config: filename })).not.toThrow();
|
|
537
|
+
unlinkSync(filename);
|
|
538
|
+
});
|
|
501
539
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,47 +1,15 @@
|
|
|
1
1
|
import { MedplumInfraConfig } from '@medplum/core';
|
|
2
|
-
import { App
|
|
2
|
+
import { App } from 'aws-cdk-lib';
|
|
3
3
|
import { readFileSync } from 'fs';
|
|
4
4
|
import { resolve } from 'path';
|
|
5
|
-
import {
|
|
6
|
-
import { FrontEnd } from './frontend';
|
|
7
|
-
import { Storage } from './storage';
|
|
5
|
+
import { MedplumStack } from './stack';
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
constructor(scope: App, config: MedplumInfraConfig) {
|
|
16
|
-
this.primaryStack = new Stack(scope, config.stackName, {
|
|
17
|
-
env: {
|
|
18
|
-
region: config.region,
|
|
19
|
-
account: config.accountNumber,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
Tags.of(this.primaryStack).add('medplum:environment', config.name);
|
|
23
|
-
|
|
24
|
-
this.backEnd = new BackEnd(this.primaryStack, config);
|
|
25
|
-
this.frontEnd = new FrontEnd(this.primaryStack, config, config.region);
|
|
26
|
-
this.storage = new Storage(this.primaryStack, config, config.region);
|
|
27
|
-
|
|
28
|
-
if (config.region !== 'us-east-1') {
|
|
29
|
-
// Some resources must be created in us-east-1
|
|
30
|
-
// For example, CloudFront distributions and ACM certificates
|
|
31
|
-
// If the primary region is not us-east-1, create these resources in us-east-1
|
|
32
|
-
const usEast1Stack = new Stack(scope, config.stackName + '-us-east-1', {
|
|
33
|
-
env: {
|
|
34
|
-
region: 'us-east-1',
|
|
35
|
-
account: config.accountNumber,
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
Tags.of(usEast1Stack).add('medplum:environment', config.name);
|
|
39
|
-
|
|
40
|
-
this.frontEnd = new FrontEnd(usEast1Stack, config, 'us-east-1');
|
|
41
|
-
this.storage = new Storage(usEast1Stack, config, 'us-east-1');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
7
|
+
export * from './backend';
|
|
8
|
+
export * from './cloudtrail';
|
|
9
|
+
export * from './frontend';
|
|
10
|
+
export * from './stack';
|
|
11
|
+
export * from './storage';
|
|
12
|
+
export * from './waf';
|
|
45
13
|
|
|
46
14
|
export function main(context?: Record<string, string>): void {
|
|
47
15
|
const app = new App({ context });
|
|
@@ -56,11 +24,7 @@ export function main(context?: Record<string, string>): void {
|
|
|
56
24
|
const config = JSON.parse(readFileSync(resolve(configFileName), 'utf-8')) as MedplumInfraConfig;
|
|
57
25
|
|
|
58
26
|
const stack = new MedplumStack(app, config);
|
|
59
|
-
|
|
60
27
|
console.log('Stack', stack.primaryStack.stackId);
|
|
61
|
-
console.log('BackEnd', stack.backEnd.node.id);
|
|
62
|
-
console.log('FrontEnd', stack.frontEnd.node.id);
|
|
63
|
-
console.log('Storage', stack.storage.node.id);
|
|
64
28
|
|
|
65
29
|
app.synth();
|
|
66
30
|
}
|
package/src/oai.ts
CHANGED
|
@@ -11,13 +11,15 @@ import { aws_cloudfront as cloudfront, aws_iam as iam, aws_s3 as s3 } from 'aws-
|
|
|
11
11
|
* However, if importing an S3 bucket via `s3.Bucket.fromBucketAttributes()`, that does not work.
|
|
12
12
|
*
|
|
13
13
|
* See: https://stackoverflow.com/a/60917015
|
|
14
|
+
*
|
|
14
15
|
* @param bucket The S3 bucket.
|
|
15
16
|
* @param identity The CloudFront Origin Access Identity.
|
|
17
|
+
* @returns The policy statement.
|
|
16
18
|
*/
|
|
17
19
|
export function grantBucketAccessToOriginAccessIdentity(
|
|
18
20
|
bucket: s3.IBucket,
|
|
19
21
|
identity: cloudfront.OriginAccessIdentity
|
|
20
|
-
):
|
|
22
|
+
): iam.PolicyStatement {
|
|
21
23
|
const policyStatement = new iam.PolicyStatement();
|
|
22
24
|
policyStatement.addActions('s3:GetObject*');
|
|
23
25
|
policyStatement.addActions('s3:GetBucket*');
|
|
@@ -26,4 +28,5 @@ export function grantBucketAccessToOriginAccessIdentity(
|
|
|
26
28
|
policyStatement.addResources(`${bucket.bucketArn}/*`);
|
|
27
29
|
policyStatement.addCanonicalUserPrincipal(identity.cloudFrontOriginAccessIdentityS3CanonicalUserId);
|
|
28
30
|
bucket.addToResourcePolicy(policyStatement);
|
|
31
|
+
return policyStatement;
|
|
29
32
|
}
|
package/src/stack.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { MedplumInfraConfig } from '@medplum/core';
|
|
2
|
+
import { App, Stack, Tags } from 'aws-cdk-lib';
|
|
3
|
+
import { BackEnd } from './backend';
|
|
4
|
+
import { CloudTrailAlarms } from './cloudtrail';
|
|
5
|
+
import { FrontEnd } from './frontend';
|
|
6
|
+
import { Storage } from './storage';
|
|
7
|
+
|
|
8
|
+
export class MedplumStack {
|
|
9
|
+
primaryStack: MedplumPrimaryStack;
|
|
10
|
+
globalStack?: MedplumGlobalStack;
|
|
11
|
+
|
|
12
|
+
constructor(scope: App, config: MedplumInfraConfig) {
|
|
13
|
+
this.primaryStack = new MedplumPrimaryStack(scope, config);
|
|
14
|
+
|
|
15
|
+
if (config.region !== 'us-east-1') {
|
|
16
|
+
// Some resources must be created in us-east-1
|
|
17
|
+
// For example, CloudFront distributions and ACM certificates
|
|
18
|
+
// If the primary region is not us-east-1, create these resources in us-east-1
|
|
19
|
+
this.globalStack = new MedplumGlobalStack(scope, config);
|
|
20
|
+
this.globalStack.addDependency(this.primaryStack);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class MedplumPrimaryStack extends Stack {
|
|
26
|
+
backEnd: BackEnd;
|
|
27
|
+
frontEnd: FrontEnd;
|
|
28
|
+
storage: Storage;
|
|
29
|
+
cloudTrail: CloudTrailAlarms;
|
|
30
|
+
|
|
31
|
+
constructor(scope: App, config: MedplumInfraConfig) {
|
|
32
|
+
super(scope, config.stackName, {
|
|
33
|
+
env: {
|
|
34
|
+
region: config.region,
|
|
35
|
+
account: config.accountNumber,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
Tags.of(this).add('medplum:environment', config.name);
|
|
39
|
+
|
|
40
|
+
this.backEnd = new BackEnd(this, config);
|
|
41
|
+
this.frontEnd = new FrontEnd(this, config, config.region);
|
|
42
|
+
this.storage = new Storage(this, config, config.region);
|
|
43
|
+
this.cloudTrail = new CloudTrailAlarms(this, config);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class MedplumGlobalStack extends Stack {
|
|
48
|
+
frontEnd: FrontEnd;
|
|
49
|
+
storage: Storage;
|
|
50
|
+
cloudTrail: CloudTrailAlarms;
|
|
51
|
+
|
|
52
|
+
constructor(scope: App, config: MedplumInfraConfig) {
|
|
53
|
+
super(scope, config.stackName + '-us-east-1', {
|
|
54
|
+
env: {
|
|
55
|
+
region: 'us-east-1',
|
|
56
|
+
account: config.accountNumber,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
Tags.of(this).add('medplum:environment', config.name);
|
|
60
|
+
|
|
61
|
+
this.frontEnd = new FrontEnd(this, config, 'us-east-1');
|
|
62
|
+
this.storage = new Storage(this, config, 'us-east-1');
|
|
63
|
+
this.cloudTrail = new CloudTrailAlarms(this, config);
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/storage.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
aws_certificatemanager as acm,
|
|
4
4
|
aws_cloudfront as cloudfront,
|
|
5
5
|
Duration,
|
|
6
|
+
aws_iam as iam,
|
|
6
7
|
aws_cloudfront_origins as origins,
|
|
7
8
|
aws_route53 as route53,
|
|
8
9
|
aws_s3 as s3,
|
|
@@ -18,14 +19,21 @@ import { awsManagedRules } from './waf';
|
|
|
18
19
|
* Binary storage bucket and CloudFront distribution.
|
|
19
20
|
*/
|
|
20
21
|
export class Storage extends Construct {
|
|
22
|
+
storageBucket: s3.IBucket;
|
|
23
|
+
keyGroup?: cloudfront.IKeyGroup;
|
|
24
|
+
responseHeadersPolicy?: cloudfront.IResponseHeadersPolicy;
|
|
25
|
+
waf?: wafv2.CfnWebACL;
|
|
26
|
+
originAccessIdentity?: cloudfront.OriginAccessIdentity;
|
|
27
|
+
originAccessPolicyStatement?: iam.PolicyStatement;
|
|
28
|
+
distribution?: cloudfront.IDistribution;
|
|
29
|
+
dnsRecord?: route53.IRecordSet;
|
|
30
|
+
|
|
21
31
|
constructor(parent: Construct, config: MedplumInfraConfig, region: string) {
|
|
22
32
|
super(parent, 'Storage');
|
|
23
33
|
|
|
24
|
-
let storageBucket: s3.IBucket;
|
|
25
|
-
|
|
26
34
|
if (region === config.region) {
|
|
27
35
|
// S3 bucket
|
|
28
|
-
storageBucket = new s3.Bucket(this, 'StorageBucket', {
|
|
36
|
+
this.storageBucket = new s3.Bucket(this, 'StorageBucket', {
|
|
29
37
|
bucketName: config.storageBucketName,
|
|
30
38
|
publicReadAccess: false,
|
|
31
39
|
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
|
|
@@ -42,11 +50,11 @@ export class Storage extends Construct {
|
|
|
42
50
|
logsPrefix: config.clamscanLoggingPrefix,
|
|
43
51
|
},
|
|
44
52
|
});
|
|
45
|
-
sc.addSourceBucket(storageBucket);
|
|
53
|
+
sc.addSourceBucket(this.storageBucket);
|
|
46
54
|
}
|
|
47
55
|
} else {
|
|
48
|
-
// Otherwise, reference the bucket by name
|
|
49
|
-
storageBucket = s3.Bucket.fromBucketAttributes(this, 'StorageBucket', {
|
|
56
|
+
// Otherwise, reference the bucket by name and region
|
|
57
|
+
this.storageBucket = s3.Bucket.fromBucketAttributes(this, 'StorageBucket', {
|
|
50
58
|
bucketName: config.storageBucketName,
|
|
51
59
|
region: config.region,
|
|
52
60
|
});
|
|
@@ -64,12 +72,12 @@ export class Storage extends Construct {
|
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
// Authorized key group for presigned URLs
|
|
67
|
-
|
|
75
|
+
this.keyGroup = new cloudfront.KeyGroup(this, 'StorageKeyGroup', {
|
|
68
76
|
items: [publicKey],
|
|
69
77
|
});
|
|
70
78
|
|
|
71
79
|
// HTTP response headers policy
|
|
72
|
-
|
|
80
|
+
this.responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {
|
|
73
81
|
securityHeadersBehavior: {
|
|
74
82
|
contentSecurityPolicy: {
|
|
75
83
|
contentSecurityPolicy:
|
|
@@ -93,7 +101,7 @@ export class Storage extends Construct {
|
|
|
93
101
|
});
|
|
94
102
|
|
|
95
103
|
// WAF
|
|
96
|
-
|
|
104
|
+
this.waf = new wafv2.CfnWebACL(this, 'StorageWAF', {
|
|
97
105
|
defaultAction: { allow: {} },
|
|
98
106
|
scope: 'CLOUDFRONT',
|
|
99
107
|
name: `${config.stackName}-StorageWAF`,
|
|
@@ -106,20 +114,25 @@ export class Storage extends Construct {
|
|
|
106
114
|
});
|
|
107
115
|
|
|
108
116
|
// Origin access identity
|
|
109
|
-
|
|
110
|
-
grantBucketAccessToOriginAccessIdentity(
|
|
117
|
+
this.originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
|
|
118
|
+
this.originAccessPolicyStatement = grantBucketAccessToOriginAccessIdentity(
|
|
119
|
+
this.storageBucket,
|
|
120
|
+
this.originAccessIdentity
|
|
121
|
+
);
|
|
111
122
|
|
|
112
123
|
// CloudFront distribution
|
|
113
|
-
|
|
124
|
+
this.distribution = new cloudfront.Distribution(this, 'StorageDistribution', {
|
|
114
125
|
defaultBehavior: {
|
|
115
|
-
origin: new origins.S3Origin(storageBucket, {
|
|
116
|
-
|
|
126
|
+
origin: new origins.S3Origin(this.storageBucket, {
|
|
127
|
+
originAccessIdentity: this.originAccessIdentity,
|
|
128
|
+
}),
|
|
129
|
+
responseHeadersPolicy: this.responseHeadersPolicy,
|
|
117
130
|
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
118
|
-
trustedKeyGroups: [keyGroup],
|
|
131
|
+
trustedKeyGroups: [this.keyGroup],
|
|
119
132
|
},
|
|
120
133
|
certificate: acm.Certificate.fromCertificateArn(this, 'StorageCertificate', config.storageSslCertArn),
|
|
121
134
|
domainNames: [config.storageDomainName],
|
|
122
|
-
webAclId: waf.attrArn,
|
|
135
|
+
webAclId: this.waf.attrArn,
|
|
123
136
|
logBucket: config.storageLoggingBucket
|
|
124
137
|
? s3.Bucket.fromBucketName(this, 'LoggingBucket', config.storageLoggingBucket)
|
|
125
138
|
: undefined,
|
|
@@ -127,22 +140,18 @@ export class Storage extends Construct {
|
|
|
127
140
|
});
|
|
128
141
|
|
|
129
142
|
// DNS
|
|
130
|
-
let record = undefined;
|
|
131
143
|
if (!config.skipDns) {
|
|
132
144
|
const zone = route53.HostedZone.fromLookup(this, 'Zone', {
|
|
133
145
|
domainName: config.domainName.split('.').slice(-2).join('.'),
|
|
134
146
|
});
|
|
135
147
|
|
|
136
148
|
// Route53 alias record for the CloudFront distribution
|
|
137
|
-
|
|
149
|
+
this.dnsRecord = new route53.ARecord(this, 'StorageAliasRecord', {
|
|
138
150
|
recordName: config.storageDomainName,
|
|
139
|
-
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
|
|
151
|
+
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(this.distribution)),
|
|
140
152
|
zone,
|
|
141
153
|
});
|
|
142
154
|
}
|
|
143
|
-
|
|
144
|
-
// Debug
|
|
145
|
-
console.log('ARecord', record?.domainName);
|
|
146
155
|
}
|
|
147
156
|
}
|
|
148
157
|
}
|