@aws/nx-plugin 1.0.0-rc.40 → 1.0.0-rc.41
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/package.json +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +42 -0
- package/src/open-api/ts-client/generator.js.map +1 -1
- package/src/open-api/ts-hooks/generator.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +4 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +17 -0
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +51 -0
- package/src/ts/lib/biome.js +36 -3
- package/src/ts/lib/biome.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +572 -195
- package/src/ts/sync/generator.js +0 -2
- package/src/ts/sync/generator.js.map +1 -1
- package/src/utils/ast.js.map +1 -1
- package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +61 -70
- package/src/utils/format.js +11 -2
- package/src/utils/format.js.map +1 -1
- package/src/utils/warm-ruff-cache.d.ts +12 -0
- package/src/utils/warm-ruff-cache.js +39 -0
- package/src/utils/warm-ruff-cache.js.map +1 -0
- package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +127 -36
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +77 -74
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CfnOutput,
|
|
3
|
+
CfnResource,
|
|
4
|
+
Duration,
|
|
5
|
+
Lazy,
|
|
6
|
+
Names,
|
|
7
|
+
RemovalPolicy,
|
|
8
|
+
Stack,
|
|
9
|
+
} from 'aws-cdk-lib';
|
|
2
10
|
import {
|
|
3
11
|
Distribution,
|
|
4
12
|
HeadersFrameOption,
|
|
5
13
|
HeadersReferrerPolicy,
|
|
6
14
|
ResponseHeadersPolicy,
|
|
15
|
+
SecurityPolicyProtocol,
|
|
7
16
|
ViewerProtocolPolicy,
|
|
8
17
|
} from 'aws-cdk-lib/aws-cloudfront';
|
|
18
|
+
import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
|
|
9
19
|
import { S3BucketOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
|
|
10
20
|
import {
|
|
11
21
|
BlockPublicAccess,
|
|
@@ -22,6 +32,14 @@ import {
|
|
|
22
32
|
import { Construct } from 'constructs';
|
|
23
33
|
import { RuntimeConfig } from './runtime-config<% if (esm) { %>.js<% } %>';
|
|
24
34
|
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
35
|
+
import {
|
|
36
|
+
CfnDelivery,
|
|
37
|
+
CfnDeliveryDestination,
|
|
38
|
+
CfnDeliverySource,
|
|
39
|
+
LogGroup,
|
|
40
|
+
RetentionDays,
|
|
41
|
+
} from 'aws-cdk-lib/aws-logs';
|
|
42
|
+
import { Effect, PolicyStatement, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
25
43
|
import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2';
|
|
26
44
|
import { suppressRules } from './checkov<% if (esm) { %>.js<% } %>';
|
|
27
45
|
|
|
@@ -47,6 +65,15 @@ const CONTENT_SECURITY_POLICY = [
|
|
|
47
65
|
export interface StaticWebsiteProps {
|
|
48
66
|
readonly websiteName: string;
|
|
49
67
|
readonly websiteFilePath: string;
|
|
68
|
+
/**
|
|
69
|
+
* Custom domain names for the CloudFront distribution. Requires `certificate`.
|
|
70
|
+
*/
|
|
71
|
+
readonly domainNames?: string[];
|
|
72
|
+
/**
|
|
73
|
+
* ACM certificate for the custom domain names. Must be in us-east-1.
|
|
74
|
+
* When provided, viewers are required to use TLS 1.2 or later.
|
|
75
|
+
*/
|
|
76
|
+
readonly certificate?: ICertificate;
|
|
50
77
|
}
|
|
51
78
|
|
|
52
79
|
/**
|
|
@@ -65,39 +92,43 @@ export class StaticWebsite extends Construct {
|
|
|
65
92
|
constructor(
|
|
66
93
|
scope: Construct,
|
|
67
94
|
id: string,
|
|
68
|
-
{ websiteFilePath, websiteName }: StaticWebsiteProps
|
|
95
|
+
{ websiteFilePath, websiteName, domainNames, certificate }: StaticWebsiteProps
|
|
69
96
|
) {
|
|
70
97
|
super(scope, id);
|
|
71
|
-
this.node.setContext(
|
|
72
|
-
'@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy',
|
|
73
|
-
true
|
|
74
|
-
);
|
|
75
98
|
|
|
76
99
|
const websiteKey = new Key(this, 'WebsiteKey', {
|
|
77
100
|
enableKeyRotation: true,
|
|
78
101
|
});
|
|
79
102
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
// Allow CloudWatch Logs to use the website key for server access log delivery.
|
|
104
|
+
const stack = Stack.of(this);
|
|
105
|
+
websiteKey.addToResourcePolicy(
|
|
106
|
+
new PolicyStatement({
|
|
107
|
+
effect: Effect.ALLOW,
|
|
108
|
+
principals: [
|
|
109
|
+
new ServicePrincipal(`logs.${stack.region}.amazonaws.com`),
|
|
110
|
+
],
|
|
111
|
+
actions: [
|
|
112
|
+
'kms:Encrypt',
|
|
113
|
+
'kms:Decrypt',
|
|
114
|
+
'kms:ReEncrypt*',
|
|
115
|
+
'kms:GenerateDataKey*',
|
|
116
|
+
'kms:DescribeKey',
|
|
117
|
+
],
|
|
118
|
+
resources: ['*'],
|
|
119
|
+
conditions: {
|
|
120
|
+
ArnLike: {
|
|
121
|
+
'kms:EncryptionContext:aws:logs:arn': `arn:aws:logs:${stack.region}:${stack.account}:log-group:*`,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const accessLogs = new LogGroup(this, 'AccessLogs', {
|
|
128
|
+
retention: RetentionDays.ONE_YEAR,
|
|
86
129
|
encryptionKey: websiteKey,
|
|
87
|
-
|
|
88
|
-
publicReadAccess: false,
|
|
89
|
-
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
130
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
90
131
|
});
|
|
91
|
-
suppressRules(
|
|
92
|
-
accessLogsBucket,
|
|
93
|
-
['CKV_AWS_21'],
|
|
94
|
-
'Access log bucket does not need versioning enabled',
|
|
95
|
-
);
|
|
96
|
-
suppressRules(
|
|
97
|
-
accessLogsBucket,
|
|
98
|
-
['CKV_AWS_18'],
|
|
99
|
-
'Access log bucket does not need an access log bucket',
|
|
100
|
-
);
|
|
101
132
|
|
|
102
133
|
// S3 Bucket to hold website files
|
|
103
134
|
this.websiteBucket = new Bucket(this, 'WebsiteBucket', {
|
|
@@ -110,13 +141,23 @@ export class StaticWebsite extends Construct {
|
|
|
110
141
|
objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
|
|
111
142
|
publicReadAccess: false,
|
|
112
143
|
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
113
|
-
serverAccessLogsPrefix: 'website-access-logs',
|
|
114
|
-
serverAccessLogsBucket: accessLogsBucket,
|
|
115
144
|
});
|
|
145
|
+
suppressRules(
|
|
146
|
+
this.websiteBucket,
|
|
147
|
+
['CKV_AWS_18'],
|
|
148
|
+
'Server access logs are delivered to CloudWatch Logs',
|
|
149
|
+
);
|
|
150
|
+
this.deliverAccessLogsToCloudWatch(
|
|
151
|
+
'Website',
|
|
152
|
+
this.websiteBucket,
|
|
153
|
+
accessLogs,
|
|
154
|
+
);
|
|
116
155
|
// Web ACL
|
|
117
156
|
const wafStack = new CloudfrontWebAcl(this, 'waf');
|
|
118
157
|
|
|
119
|
-
//
|
|
158
|
+
// Bucket holding CloudFront standard access logs. CloudFront delivers its
|
|
159
|
+
// own logs to S3 only, so this bucket is retained; its S3 server access
|
|
160
|
+
// logs are delivered to CloudWatch Logs.
|
|
120
161
|
const logBucket = new Bucket(this, 'DistributionLogBucket', {
|
|
121
162
|
enforceSSL: true,
|
|
122
163
|
autoDeleteObjects: true,
|
|
@@ -126,14 +167,18 @@ export class StaticWebsite extends Construct {
|
|
|
126
167
|
objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED,
|
|
127
168
|
publicReadAccess: false,
|
|
128
169
|
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
129
|
-
serverAccessLogsPrefix: 'distribution-access-logs',
|
|
130
|
-
serverAccessLogsBucket: accessLogsBucket,
|
|
131
170
|
});
|
|
132
171
|
suppressRules(
|
|
133
172
|
logBucket,
|
|
134
173
|
['CKV_AWS_21'],
|
|
135
174
|
'Distribution log bucket does not need versioning enabled',
|
|
136
175
|
);
|
|
176
|
+
suppressRules(
|
|
177
|
+
logBucket,
|
|
178
|
+
['CKV_AWS_18'],
|
|
179
|
+
'Server access logs are delivered to CloudWatch Logs',
|
|
180
|
+
);
|
|
181
|
+
this.deliverAccessLogsToCloudWatch('Distribution', logBucket, accessLogs);
|
|
137
182
|
|
|
138
183
|
// Security headers applied to all responses.
|
|
139
184
|
const responseHeadersPolicy = new ResponseHeadersPolicy(
|
|
@@ -172,6 +217,13 @@ export class StaticWebsite extends Construct {
|
|
|
172
217
|
webAclId: wafStack.wafArn,
|
|
173
218
|
enableLogging: true,
|
|
174
219
|
logBucket: logBucket,
|
|
220
|
+
...(certificate
|
|
221
|
+
? {
|
|
222
|
+
certificate,
|
|
223
|
+
domainNames,
|
|
224
|
+
minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021,
|
|
225
|
+
}
|
|
226
|
+
: {}),
|
|
175
227
|
defaultBehavior: {
|
|
176
228
|
origin: S3BucketOrigin.withOriginAccessControl(this.websiteBucket),
|
|
177
229
|
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
@@ -192,12 +244,14 @@ export class StaticWebsite extends Construct {
|
|
|
192
244
|
],
|
|
193
245
|
},
|
|
194
246
|
);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
247
|
+
if (!certificate) {
|
|
248
|
+
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
249
|
+
suppressRules(
|
|
250
|
+
this.cloudFrontDistribution,
|
|
251
|
+
['CKV_AWS_174'],
|
|
252
|
+
'Cloudfront default certificate does not use TLS 1.2',
|
|
253
|
+
);
|
|
254
|
+
}
|
|
201
255
|
|
|
202
256
|
// Deploy Website
|
|
203
257
|
this.bucketDeployment = new BucketDeployment(this, 'WebsiteDeployment', {
|
|
@@ -246,6 +300,43 @@ export class StaticWebsite extends Construct {
|
|
|
246
300
|
value: this.websiteBucket.bucketName,
|
|
247
301
|
});
|
|
248
302
|
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Delivers a bucket's S3 server access logs to a CloudWatch log group using
|
|
306
|
+
* CloudWatch Logs vended log delivery.
|
|
307
|
+
*/
|
|
308
|
+
private deliverAccessLogsToCloudWatch(
|
|
309
|
+
id: string,
|
|
310
|
+
bucket: IBucket,
|
|
311
|
+
logGroup: LogGroup,
|
|
312
|
+
) {
|
|
313
|
+
const source: CfnDeliverySource = new CfnDeliverySource(
|
|
314
|
+
this,
|
|
315
|
+
`${id}AccessLogsSource`,
|
|
316
|
+
{
|
|
317
|
+
name: Lazy.string({
|
|
318
|
+
produce: () => Names.uniqueResourceName(source, { maxLength: 60 }),
|
|
319
|
+
}),
|
|
320
|
+
logType: 'S3_SERVER_ACCESS_LOGS',
|
|
321
|
+
resourceArn: bucket.bucketArn,
|
|
322
|
+
},
|
|
323
|
+
);
|
|
324
|
+
const destination: CfnDeliveryDestination = new CfnDeliveryDestination(
|
|
325
|
+
this,
|
|
326
|
+
`${id}AccessLogsDestination`,
|
|
327
|
+
{
|
|
328
|
+
name: Lazy.string({
|
|
329
|
+
produce: () => Names.uniqueResourceName(destination, { maxLength: 60 }),
|
|
330
|
+
}),
|
|
331
|
+
destinationResourceArn: logGroup.logGroupArn,
|
|
332
|
+
},
|
|
333
|
+
);
|
|
334
|
+
const delivery = new CfnDelivery(this, `${id}AccessLogsDelivery`, {
|
|
335
|
+
deliverySourceName: source.name,
|
|
336
|
+
deliveryDestinationArn: destination.attrArn,
|
|
337
|
+
});
|
|
338
|
+
delivery.addDependency(source);
|
|
339
|
+
}
|
|
249
340
|
}
|
|
250
341
|
|
|
251
342
|
export class CloudfrontWebAcl extends Stack {
|
package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template
CHANGED
|
@@ -23,6 +23,18 @@ variable "website_file_path" {
|
|
|
23
23
|
type = string
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
variable "custom_domain_names" {
|
|
27
|
+
description = "Custom domain names (aliases) for the CloudFront distribution. Requires acm_certificate_arn."
|
|
28
|
+
type = list(string)
|
|
29
|
+
default = []
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
variable "acm_certificate_arn" {
|
|
33
|
+
description = "ARN of an ACM certificate (in us-east-1) for the custom domain names. When set, viewers are required to use TLS 1.2 or later."
|
|
34
|
+
type = string
|
|
35
|
+
default = null
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
# Content-Security-Policy enforced on all responses. Restricts scripts and
|
|
27
39
|
# framing to mitigate XSS and clickjacking, while permitting HTTPS/WSS calls
|
|
28
40
|
# (connect-src) to AWS service endpoints such as API Gateway, Cognito and
|
|
@@ -30,6 +42,10 @@ variable "website_file_path" {
|
|
|
30
42
|
# connect-src to your specific origins once they are known.
|
|
31
43
|
locals {
|
|
32
44
|
content_security_policy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https: wss:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
|
|
45
|
+
|
|
46
|
+
# Delivery source/destination names have a 60 character maximum. Truncate the
|
|
47
|
+
# website name so the longest delivery name stays within the limit.
|
|
48
|
+
access_logs_name_prefix = substr(lower(var.website_name), 0, 16)
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
|
|
@@ -68,6 +84,26 @@ resource "aws_kms_key" "website_key" {
|
|
|
68
84
|
]
|
|
69
85
|
Resource = "*"
|
|
70
86
|
},
|
|
87
|
+
{
|
|
88
|
+
Sid = "AllowCloudWatchLogs"
|
|
89
|
+
Effect = "Allow"
|
|
90
|
+
Principal = {
|
|
91
|
+
Service = "logs.${data.aws_region.current.region}.amazonaws.com"
|
|
92
|
+
}
|
|
93
|
+
Action = [
|
|
94
|
+
"kms:Encrypt",
|
|
95
|
+
"kms:Decrypt",
|
|
96
|
+
"kms:ReEncrypt*",
|
|
97
|
+
"kms:GenerateDataKey*",
|
|
98
|
+
"kms:DescribeKey"
|
|
99
|
+
]
|
|
100
|
+
Resource = "*"
|
|
101
|
+
Condition = {
|
|
102
|
+
ArnLike = {
|
|
103
|
+
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:log-group:*"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
71
107
|
{
|
|
72
108
|
Sid = "AllowCloudFrontServicePrincipalSSE-KMS"
|
|
73
109
|
Effect = "Allow"
|
|
@@ -99,18 +135,12 @@ resource "aws_kms_alias" "website_key_alias" {
|
|
|
99
135
|
target_key_id = aws_kms_key.website_key.key_id
|
|
100
136
|
}
|
|
101
137
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
bucket = "${lower(var.website_name)}-access-logs-${random_id.bucket_suffix.hex}"
|
|
109
|
-
force_destroy = true
|
|
110
|
-
|
|
111
|
-
tags = {
|
|
112
|
-
Name = "${lower(var.website_name)}-access-logs"
|
|
113
|
-
}
|
|
138
|
+
# CloudWatch log group receiving S3 server access logs for the website and
|
|
139
|
+
# distribution log buckets.
|
|
140
|
+
resource "aws_cloudwatch_log_group" "access_logs" {
|
|
141
|
+
name = "/aws/s3/${lower(var.website_name)}-access-logs-${random_id.bucket_suffix.hex}"
|
|
142
|
+
retention_in_days = 365
|
|
143
|
+
kms_key_id = aws_kms_key.website_key.arn
|
|
114
144
|
}
|
|
115
145
|
|
|
116
146
|
resource "random_id" "unique_suffix" {
|
|
@@ -121,63 +151,12 @@ resource "random_id" "bucket_suffix" {
|
|
|
121
151
|
byte_length = 8
|
|
122
152
|
}
|
|
123
153
|
|
|
124
|
-
resource "aws_s3_bucket_versioning" "access_logs_versioning" {
|
|
125
|
-
bucket = aws_s3_bucket.access_logs.id
|
|
126
|
-
versioning_configuration {
|
|
127
|
-
status = "Disabled"
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
resource "aws_s3_bucket_server_side_encryption_configuration" "access_logs_encryption" {
|
|
132
|
-
bucket = aws_s3_bucket.access_logs.id
|
|
133
|
-
|
|
134
|
-
rule {
|
|
135
|
-
apply_server_side_encryption_by_default {
|
|
136
|
-
kms_master_key_id = aws_kms_key.website_key.arn
|
|
137
|
-
sse_algorithm = "aws:kms"
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
resource "aws_s3_bucket_public_access_block" "access_logs_pab" {
|
|
143
|
-
bucket = aws_s3_bucket.access_logs.id
|
|
144
|
-
|
|
145
|
-
block_public_acls = true
|
|
146
|
-
block_public_policy = true
|
|
147
|
-
ignore_public_acls = true
|
|
148
|
-
restrict_public_buckets = true
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
resource "aws_s3_bucket_policy" "access_logs_ssl_policy" {
|
|
152
|
-
bucket = aws_s3_bucket.access_logs.id
|
|
153
|
-
|
|
154
|
-
policy = jsonencode({
|
|
155
|
-
Version = "2012-10-17"
|
|
156
|
-
Statement = [
|
|
157
|
-
{
|
|
158
|
-
Sid = "DenyInsecureConnections"
|
|
159
|
-
Effect = "Deny"
|
|
160
|
-
Principal = "*"
|
|
161
|
-
Action = "s3:*"
|
|
162
|
-
Resource = [
|
|
163
|
-
aws_s3_bucket.access_logs.arn,
|
|
164
|
-
"${aws_s3_bucket.access_logs.arn}/*"
|
|
165
|
-
]
|
|
166
|
-
Condition = {
|
|
167
|
-
Bool = {
|
|
168
|
-
"aws:SecureTransport" = "false"
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
]
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
|
|
176
154
|
# Website Bucket
|
|
177
155
|
resource "aws_s3_bucket" "website" {
|
|
178
156
|
#checkov:skip=CKV2_AWS_61:Lifecycle configuration not required for static website content
|
|
179
157
|
#checkov:skip=CKV_AWS_144:Cross-region replication not required for static website
|
|
180
158
|
#checkov:skip=CKV2_AWS_62:Event notifications not required for static website bucket
|
|
159
|
+
#checkov:skip=CKV_AWS_18:Server access logs are delivered to CloudWatch Logs (see aws_cloudwatch_log_delivery.website)
|
|
181
160
|
bucket = "${lower(var.website_name)}-website-${random_id.bucket_suffix.hex}"
|
|
182
161
|
force_destroy = true
|
|
183
162
|
|
|
@@ -221,20 +200,35 @@ resource "aws_s3_bucket_ownership_controls" "website_ownership" {
|
|
|
221
200
|
}
|
|
222
201
|
}
|
|
223
202
|
|
|
224
|
-
|
|
225
|
-
|
|
203
|
+
# Deliver the website bucket's server access logs to CloudWatch Logs.
|
|
204
|
+
resource "aws_cloudwatch_log_delivery_source" "website" {
|
|
205
|
+
name = "${local.access_logs_name_prefix}-website-logs-${random_id.bucket_suffix.hex}"
|
|
206
|
+
log_type = "S3_SERVER_ACCESS_LOGS"
|
|
207
|
+
resource_arn = aws_s3_bucket.website.arn
|
|
208
|
+
}
|
|
226
209
|
|
|
227
|
-
|
|
228
|
-
|
|
210
|
+
resource "aws_cloudwatch_log_delivery_destination" "access_logs" {
|
|
211
|
+
name = "${local.access_logs_name_prefix}-access-logs-${random_id.bucket_suffix.hex}"
|
|
212
|
+
|
|
213
|
+
delivery_destination_configuration {
|
|
214
|
+
destination_resource_arn = aws_cloudwatch_log_group.access_logs.arn
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
resource "aws_cloudwatch_log_delivery" "website" {
|
|
219
|
+
delivery_source_name = aws_cloudwatch_log_delivery_source.website.name
|
|
220
|
+
delivery_destination_arn = aws_cloudwatch_log_delivery_destination.access_logs.arn
|
|
229
221
|
}
|
|
230
222
|
|
|
231
223
|
|
|
232
|
-
# Distribution Log Bucket
|
|
224
|
+
# Distribution Log Bucket — holds CloudFront standard access logs (CloudFront
|
|
225
|
+
# delivers only to S3). Its own S3 server access logs go to CloudWatch Logs.
|
|
233
226
|
resource "aws_s3_bucket" "distribution_logs" {
|
|
234
227
|
#checkov:skip=CKV2_AWS_61:Lifecycle configuration not required for CloudFront logs
|
|
235
228
|
#checkov:skip=CKV_AWS_144:Cross-region replication not required for CloudFront logs
|
|
236
229
|
#checkov:skip=CKV2_AWS_62:Event notifications not required for CloudFront logs bucket
|
|
237
230
|
#checkov:skip=CKV_AWS_21:Versioning not required for CloudFront access logs
|
|
231
|
+
#checkov:skip=CKV_AWS_18:Server access logs are delivered to CloudWatch Logs (see aws_cloudwatch_log_delivery.distribution_logs)
|
|
238
232
|
bucket = "${lower(var.website_name)}-distribution-logs-${random_id.bucket_suffix.hex}"
|
|
239
233
|
force_destroy = true
|
|
240
234
|
|
|
@@ -280,11 +274,16 @@ resource "aws_s3_bucket_acl" "distribution_logs_acl" {
|
|
|
280
274
|
depends_on = [aws_s3_bucket_ownership_controls.distribution_logs_ownership]
|
|
281
275
|
}
|
|
282
276
|
|
|
283
|
-
|
|
284
|
-
|
|
277
|
+
# Deliver the distribution log bucket's server access logs to CloudWatch Logs.
|
|
278
|
+
resource "aws_cloudwatch_log_delivery_source" "distribution_logs" {
|
|
279
|
+
name = "${local.access_logs_name_prefix}-distribution-logs-${random_id.bucket_suffix.hex}"
|
|
280
|
+
log_type = "S3_SERVER_ACCESS_LOGS"
|
|
281
|
+
resource_arn = aws_s3_bucket.distribution_logs.arn
|
|
282
|
+
}
|
|
285
283
|
|
|
286
|
-
|
|
287
|
-
|
|
284
|
+
resource "aws_cloudwatch_log_delivery" "distribution_logs" {
|
|
285
|
+
delivery_source_name = aws_cloudwatch_log_delivery_source.distribution_logs.name
|
|
286
|
+
delivery_destination_arn = aws_cloudwatch_log_delivery_destination.access_logs.arn
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
resource "aws_s3_bucket_policy" "distribution_logs_ssl_policy" {
|
|
@@ -439,6 +438,7 @@ resource "aws_cloudfront_distribution" "website" {
|
|
|
439
438
|
is_ipv6_enabled = true
|
|
440
439
|
default_root_object = "index.html"
|
|
441
440
|
web_acl_id = aws_wafv2_web_acl.cloudfront_waf.arn
|
|
441
|
+
aliases = var.custom_domain_names
|
|
442
442
|
|
|
443
443
|
logging_config {
|
|
444
444
|
include_cookies = false
|
|
@@ -485,7 +485,10 @@ resource "aws_cloudfront_distribution" "website" {
|
|
|
485
485
|
}
|
|
486
486
|
|
|
487
487
|
viewer_certificate {
|
|
488
|
-
cloudfront_default_certificate =
|
|
488
|
+
cloudfront_default_certificate = var.acm_certificate_arn == null
|
|
489
|
+
acm_certificate_arn = var.acm_certificate_arn
|
|
490
|
+
ssl_support_method = var.acm_certificate_arn == null ? null : "sni-only"
|
|
491
|
+
minimum_protocol_version = var.acm_certificate_arn == null ? "TLSv1" : "TLSv1.2_2021"
|
|
489
492
|
}
|
|
490
493
|
|
|
491
494
|
tags = {
|