@medplum/cdk 2.1.17 → 2.1.19

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/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
- ];
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist/types",
5
- "noEmit": false,
6
- "emitDeclarationOnly": true
7
- },
8
- "exclude": ["**/*.test.ts"]
9
- }
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "lib": ["esnext"]
6
- },
7
- "include": ["src/**/*.ts"]
8
- }