@medplum/cdk 2.1.17 → 2.1.18
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.map +2 -2
- package/dist/esm/index.mjs.map +2 -2
- package/package.json +7 -1
- package/babel.config.json +0 -3
- package/cdk.json +0 -3
- package/esbuild.mjs +0 -47
- package/jest.config.json +0 -11
- package/src/backend.ts +0 -496
- package/src/cloudtrail.ts +0 -134
- package/src/config.test.ts +0 -487
- package/src/config.ts +0 -202
- package/src/frontend.ts +0 -188
- package/src/index.test.ts +0 -539
- package/src/index.ts +0 -42
- package/src/oai.ts +0 -32
- package/src/stack.ts +0 -65
- package/src/storage.ts +0 -157
- package/src/waf.ts +0 -122
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -8
package/src/storage.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { MedplumInfraConfig } from '@medplum/core';
|
|
2
|
-
import {
|
|
3
|
-
aws_certificatemanager as acm,
|
|
4
|
-
aws_cloudfront as cloudfront,
|
|
5
|
-
Duration,
|
|
6
|
-
aws_iam as iam,
|
|
7
|
-
aws_cloudfront_origins as origins,
|
|
8
|
-
aws_route53 as route53,
|
|
9
|
-
aws_s3 as s3,
|
|
10
|
-
aws_route53_targets as targets,
|
|
11
|
-
aws_wafv2 as wafv2,
|
|
12
|
-
} from 'aws-cdk-lib';
|
|
13
|
-
import { ServerlessClamscan } from 'cdk-serverless-clamscan';
|
|
14
|
-
import { Construct } from 'constructs';
|
|
15
|
-
import { grantBucketAccessToOriginAccessIdentity } from './oai';
|
|
16
|
-
import { awsManagedRules } from './waf';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Binary storage bucket and CloudFront distribution.
|
|
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
|
-
|
|
31
|
-
constructor(parent: Construct, config: MedplumInfraConfig, region: string) {
|
|
32
|
-
super(parent, 'Storage');
|
|
33
|
-
|
|
34
|
-
if (region === config.region) {
|
|
35
|
-
// S3 bucket
|
|
36
|
-
this.storageBucket = new s3.Bucket(this, 'StorageBucket', {
|
|
37
|
-
bucketName: config.storageBucketName,
|
|
38
|
-
publicReadAccess: false,
|
|
39
|
-
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
|
|
40
|
-
encryption: s3.BucketEncryption.S3_MANAGED,
|
|
41
|
-
enforceSSL: true,
|
|
42
|
-
versioned: true,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (config.clamscanEnabled) {
|
|
46
|
-
// ClamAV serverless scan
|
|
47
|
-
const sc = new ServerlessClamscan(this, 'ServerlessClamscan', {
|
|
48
|
-
defsBucketAccessLogsConfig: {
|
|
49
|
-
logsBucket: s3.Bucket.fromBucketName(this, 'LoggingBucket', config.clamscanLoggingBucket),
|
|
50
|
-
logsPrefix: config.clamscanLoggingPrefix,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
sc.addSourceBucket(this.storageBucket);
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
// Otherwise, reference the bucket by name and region
|
|
57
|
-
this.storageBucket = s3.Bucket.fromBucketAttributes(this, 'StorageBucket', {
|
|
58
|
-
bucketName: config.storageBucketName,
|
|
59
|
-
region: config.region,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (region === 'us-east-1') {
|
|
64
|
-
// Public key in PEM format
|
|
65
|
-
let publicKey: cloudfront.IPublicKey;
|
|
66
|
-
if (config.signingKeyId) {
|
|
67
|
-
publicKey = cloudfront.PublicKey.fromPublicKeyId(this, 'StoragePublicKey', config.signingKeyId);
|
|
68
|
-
} else {
|
|
69
|
-
publicKey = new cloudfront.PublicKey(this, 'StoragePublicKey', {
|
|
70
|
-
encodedKey: config.storagePublicKey,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Authorized key group for presigned URLs
|
|
75
|
-
this.keyGroup = new cloudfront.KeyGroup(this, 'StorageKeyGroup', {
|
|
76
|
-
items: [publicKey],
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// HTTP response headers policy
|
|
80
|
-
this.responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {
|
|
81
|
-
securityHeadersBehavior: {
|
|
82
|
-
contentSecurityPolicy: {
|
|
83
|
-
contentSecurityPolicy:
|
|
84
|
-
"default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors *.medplum.com;",
|
|
85
|
-
override: true,
|
|
86
|
-
},
|
|
87
|
-
contentTypeOptions: { override: true },
|
|
88
|
-
frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },
|
|
89
|
-
referrerPolicy: { referrerPolicy: cloudfront.HeadersReferrerPolicy.NO_REFERRER, override: true },
|
|
90
|
-
strictTransportSecurity: {
|
|
91
|
-
accessControlMaxAge: Duration.seconds(63072000),
|
|
92
|
-
includeSubdomains: true,
|
|
93
|
-
override: true,
|
|
94
|
-
},
|
|
95
|
-
xssProtection: {
|
|
96
|
-
protection: true,
|
|
97
|
-
modeBlock: true,
|
|
98
|
-
override: true,
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// WAF
|
|
104
|
-
this.waf = new wafv2.CfnWebACL(this, 'StorageWAF', {
|
|
105
|
-
defaultAction: { allow: {} },
|
|
106
|
-
scope: 'CLOUDFRONT',
|
|
107
|
-
name: `${config.stackName}-StorageWAF`,
|
|
108
|
-
rules: awsManagedRules,
|
|
109
|
-
visibilityConfig: {
|
|
110
|
-
cloudWatchMetricsEnabled: true,
|
|
111
|
-
metricName: `${config.stackName}-StorageWAF-Metric`,
|
|
112
|
-
sampledRequestsEnabled: false,
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Origin access identity
|
|
117
|
-
this.originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
|
|
118
|
-
this.originAccessPolicyStatement = grantBucketAccessToOriginAccessIdentity(
|
|
119
|
-
this.storageBucket,
|
|
120
|
-
this.originAccessIdentity
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
// CloudFront distribution
|
|
124
|
-
this.distribution = new cloudfront.Distribution(this, 'StorageDistribution', {
|
|
125
|
-
defaultBehavior: {
|
|
126
|
-
origin: new origins.S3Origin(this.storageBucket, {
|
|
127
|
-
originAccessIdentity: this.originAccessIdentity,
|
|
128
|
-
}),
|
|
129
|
-
responseHeadersPolicy: this.responseHeadersPolicy,
|
|
130
|
-
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
131
|
-
trustedKeyGroups: [this.keyGroup],
|
|
132
|
-
},
|
|
133
|
-
certificate: acm.Certificate.fromCertificateArn(this, 'StorageCertificate', config.storageSslCertArn),
|
|
134
|
-
domainNames: [config.storageDomainName],
|
|
135
|
-
webAclId: this.waf.attrArn,
|
|
136
|
-
logBucket: config.storageLoggingBucket
|
|
137
|
-
? s3.Bucket.fromBucketName(this, 'LoggingBucket', config.storageLoggingBucket)
|
|
138
|
-
: undefined,
|
|
139
|
-
logFilePrefix: config.storageLoggingPrefix,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// DNS
|
|
143
|
-
if (!config.skipDns) {
|
|
144
|
-
const zone = route53.HostedZone.fromLookup(this, 'Zone', {
|
|
145
|
-
domainName: config.domainName.split('.').slice(-2).join('.'),
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Route53 alias record for the CloudFront distribution
|
|
149
|
-
this.dnsRecord = new route53.ARecord(this, 'StorageAliasRecord', {
|
|
150
|
-
recordName: config.storageDomainName,
|
|
151
|
-
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(this.distribution)),
|
|
152
|
-
zone,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
package/src/waf.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// Based on https://gist.github.com/statik/f1ac9d6227d98d30c7a7cec0c83f4e64
|
|
2
|
-
|
|
3
|
-
import { aws_wafv2 as wafv2 } from 'aws-cdk-lib';
|
|
4
|
-
|
|
5
|
-
export const awsManagedRules: wafv2.CfnWebACL.RuleProperty[] = [
|
|
6
|
-
// Common Rule Set aligns with major portions of OWASP Core Rule Set
|
|
7
|
-
// https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html
|
|
8
|
-
{
|
|
9
|
-
name: 'AWS-AWSManagedRulesCommonRuleSet',
|
|
10
|
-
priority: 10,
|
|
11
|
-
statement: {
|
|
12
|
-
managedRuleGroupStatement: {
|
|
13
|
-
vendorName: 'AWS',
|
|
14
|
-
name: 'AWSManagedRulesCommonRuleSet',
|
|
15
|
-
// Excluding generic RFI body rule for sns notifications
|
|
16
|
-
// https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
|
|
17
|
-
excludedRules: [
|
|
18
|
-
{ name: 'NoUserAgent_HEADER' },
|
|
19
|
-
{ name: 'UserAgent_BadBots_HEADER' },
|
|
20
|
-
{ name: 'SizeRestrictions_QUERYSTRING' },
|
|
21
|
-
{ name: 'SizeRestrictions_Cookie_HEADER' },
|
|
22
|
-
{ name: 'SizeRestrictions_BODY' },
|
|
23
|
-
{ name: 'SizeRestrictions_URIPATH' },
|
|
24
|
-
{ name: 'EC2MetaDataSSRF_BODY' },
|
|
25
|
-
{ name: 'EC2MetaDataSSRF_COOKIE' },
|
|
26
|
-
{ name: 'EC2MetaDataSSRF_URIPATH' },
|
|
27
|
-
{ name: 'EC2MetaDataSSRF_QUERYARGUMENTS' },
|
|
28
|
-
{ name: 'GenericLFI_QUERYARGUMENTS' },
|
|
29
|
-
{ name: 'GenericLFI_URIPATH' },
|
|
30
|
-
{ name: 'GenericLFI_BODY' },
|
|
31
|
-
{ name: 'RestrictedExtensions_URIPATH' },
|
|
32
|
-
{ name: 'RestrictedExtensions_QUERYARGUMENTS' },
|
|
33
|
-
{ name: 'GenericRFI_QUERYARGUMENTS' },
|
|
34
|
-
{ name: 'GenericRFI_BODY' },
|
|
35
|
-
{ name: 'GenericRFI_URIPATH' },
|
|
36
|
-
{ name: 'CrossSiteScripting_COOKIE' },
|
|
37
|
-
{ name: 'CrossSiteScripting_QUERYARGUMENTS' },
|
|
38
|
-
{ name: 'CrossSiteScripting_BODY' },
|
|
39
|
-
{ name: 'CrossSiteScripting_URIPATH' },
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
overrideAction: {
|
|
44
|
-
count: {},
|
|
45
|
-
},
|
|
46
|
-
visibilityConfig: {
|
|
47
|
-
sampledRequestsEnabled: true,
|
|
48
|
-
cloudWatchMetricsEnabled: true,
|
|
49
|
-
metricName: 'AWS-AWSManagedRulesCommonRuleSet',
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
// AWS IP Reputation list includes known malicious actors/bots and is regularly updated
|
|
53
|
-
// https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-ip-rep.html
|
|
54
|
-
{
|
|
55
|
-
name: 'AWS-AWSManagedRulesAmazonIpReputationList',
|
|
56
|
-
priority: 20,
|
|
57
|
-
statement: {
|
|
58
|
-
managedRuleGroupStatement: {
|
|
59
|
-
vendorName: 'AWS',
|
|
60
|
-
name: 'AWSManagedRulesAmazonIpReputationList',
|
|
61
|
-
excludedRules: [{ name: 'AWSManagedIPReputationList' }, { name: 'AWSManagedReconnaissanceList' }],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
overrideAction: {
|
|
65
|
-
count: {},
|
|
66
|
-
},
|
|
67
|
-
visibilityConfig: {
|
|
68
|
-
sampledRequestsEnabled: true,
|
|
69
|
-
cloudWatchMetricsEnabled: true,
|
|
70
|
-
metricName: 'AWSManagedRulesAmazonIpReputationList',
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
// Blocks common SQL Injection
|
|
74
|
-
// https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-sql-db
|
|
75
|
-
{
|
|
76
|
-
name: 'AWSManagedRulesSQLiRuleSet',
|
|
77
|
-
priority: 30,
|
|
78
|
-
visibilityConfig: {
|
|
79
|
-
sampledRequestsEnabled: true,
|
|
80
|
-
cloudWatchMetricsEnabled: true,
|
|
81
|
-
metricName: 'AWSManagedRulesSQLiRuleSet',
|
|
82
|
-
},
|
|
83
|
-
overrideAction: {
|
|
84
|
-
count: {},
|
|
85
|
-
},
|
|
86
|
-
statement: {
|
|
87
|
-
managedRuleGroupStatement: {
|
|
88
|
-
vendorName: 'AWS',
|
|
89
|
-
name: 'AWSManagedRulesSQLiRuleSet',
|
|
90
|
-
excludedRules: [
|
|
91
|
-
{ name: 'SQLi_QUERYARGUMENTS' },
|
|
92
|
-
{ name: 'SQLiExtendedPatterns_QUERYARGUMENTS' },
|
|
93
|
-
{ name: 'SQLi_BODY' },
|
|
94
|
-
{ name: 'SQLiExtendedPatterns_BODY' },
|
|
95
|
-
{ name: 'SQLi_COOKIE' },
|
|
96
|
-
{ name: 'SQLi_URIPATH' },
|
|
97
|
-
],
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
// Blocks attacks targeting LFI(Local File Injection) for linux systems
|
|
102
|
-
// https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-linux-os
|
|
103
|
-
{
|
|
104
|
-
name: 'AWSManagedRuleLinux',
|
|
105
|
-
priority: 40,
|
|
106
|
-
visibilityConfig: {
|
|
107
|
-
sampledRequestsEnabled: true,
|
|
108
|
-
cloudWatchMetricsEnabled: true,
|
|
109
|
-
metricName: 'AWSManagedRuleLinux',
|
|
110
|
-
},
|
|
111
|
-
overrideAction: {
|
|
112
|
-
count: {},
|
|
113
|
-
},
|
|
114
|
-
statement: {
|
|
115
|
-
managedRuleGroupStatement: {
|
|
116
|
-
vendorName: 'AWS',
|
|
117
|
-
name: 'AWSManagedRulesLinuxRuleSet',
|
|
118
|
-
excludedRules: [{ name: 'LFI_URIPATH' }, { name: 'LFI_QUERYSTRING' }, { name: 'LFI_COOKIE' }],
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
];
|
package/tsconfig.build.json
DELETED