@aws/nx-plugin 0.101.0 → 0.103.0
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/LICENSE-THIRD-PARTY +207 -0
- package/package.json +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +510 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +12 -1
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +12 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +437 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +522 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +12 -1
- package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +12 -1
- package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +9 -1
- package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +6 -0
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +99 -1
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +20 -0
- package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +115 -0
|
@@ -1026,7 +1026,13 @@ import {
|
|
|
1026
1026
|
IResource,
|
|
1027
1027
|
Stage,
|
|
1028
1028
|
} from 'aws-cdk-lib/aws-apigateway';
|
|
1029
|
-
import { IAspect } from 'aws-cdk-lib';
|
|
1029
|
+
import { IAspect, RemovalPolicy } from 'aws-cdk-lib';
|
|
1030
|
+
import {
|
|
1031
|
+
CfnLoggingConfiguration,
|
|
1032
|
+
CfnWebACL,
|
|
1033
|
+
CfnWebACLAssociation,
|
|
1034
|
+
} from 'aws-cdk-lib/aws-wafv2';
|
|
1035
|
+
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
1030
1036
|
import { RuntimeConfig } from '../runtime-config.js';
|
|
1031
1037
|
import {
|
|
1032
1038
|
ApiIntegrations,
|
|
@@ -1057,6 +1063,14 @@ export interface RestApiProps<
|
|
|
1057
1063
|
* Map of integration keys to their API Gateway integrations
|
|
1058
1064
|
*/
|
|
1059
1065
|
readonly integrations: TIntegrations;
|
|
1066
|
+
/**
|
|
1067
|
+
* Whether to enable AWS WAFv2 with the default managed ruleset
|
|
1068
|
+
* (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet)
|
|
1069
|
+
* and associate it with the API's default stage.
|
|
1070
|
+
*
|
|
1071
|
+
* @default true
|
|
1072
|
+
*/
|
|
1073
|
+
readonly enableWaf?: boolean;
|
|
1060
1074
|
}
|
|
1061
1075
|
|
|
1062
1076
|
/**
|
|
@@ -1080,6 +1094,9 @@ export class RestApi<
|
|
|
1080
1094
|
/** Map of integration keys to their API Gateway integrations */
|
|
1081
1095
|
public readonly integrations: TIntegrations;
|
|
1082
1096
|
|
|
1097
|
+
/** The WAFv2 Web ACL associated with the API's default stage, if WAF is enabled */
|
|
1098
|
+
public readonly webAcl?: CfnWebACL;
|
|
1099
|
+
|
|
1083
1100
|
constructor(
|
|
1084
1101
|
scope: Construct,
|
|
1085
1102
|
id: string,
|
|
@@ -1087,6 +1104,7 @@ export class RestApi<
|
|
|
1087
1104
|
apiName,
|
|
1088
1105
|
operations,
|
|
1089
1106
|
integrations,
|
|
1107
|
+
enableWaf = true,
|
|
1090
1108
|
...props
|
|
1091
1109
|
}: RestApiProps<TIntegrations, TOperation>,
|
|
1092
1110
|
) {
|
|
@@ -1096,6 +1114,86 @@ export class RestApi<
|
|
|
1096
1114
|
// Create the API Gateway REST API
|
|
1097
1115
|
this.api = new _RestApi(this, 'Api', props);
|
|
1098
1116
|
|
|
1117
|
+
if (enableWaf) {
|
|
1118
|
+
this.webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
1119
|
+
defaultAction: { allow: {} },
|
|
1120
|
+
scope: 'REGIONAL',
|
|
1121
|
+
visibilityConfig: {
|
|
1122
|
+
cloudWatchMetricsEnabled: true,
|
|
1123
|
+
metricName: \`\${apiName}WebAcl\`,
|
|
1124
|
+
sampledRequestsEnabled: true,
|
|
1125
|
+
},
|
|
1126
|
+
rules: [
|
|
1127
|
+
{
|
|
1128
|
+
name: 'CRSRule',
|
|
1129
|
+
priority: 0,
|
|
1130
|
+
statement: {
|
|
1131
|
+
managedRuleGroupStatement: {
|
|
1132
|
+
name: 'AWSManagedRulesCommonRuleSet',
|
|
1133
|
+
vendorName: 'AWS',
|
|
1134
|
+
// Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
1135
|
+
ruleActionOverrides: [
|
|
1136
|
+
{
|
|
1137
|
+
name: 'SizeRestrictions_BODY',
|
|
1138
|
+
actionToUse: { count: {} },
|
|
1139
|
+
},
|
|
1140
|
+
],
|
|
1141
|
+
},
|
|
1142
|
+
},
|
|
1143
|
+
visibilityConfig: {
|
|
1144
|
+
cloudWatchMetricsEnabled: true,
|
|
1145
|
+
metricName: \`\${apiName}WebAcl-CRS\`,
|
|
1146
|
+
sampledRequestsEnabled: true,
|
|
1147
|
+
},
|
|
1148
|
+
overrideAction: {
|
|
1149
|
+
none: {},
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
name: 'KnownBadInputsRule',
|
|
1154
|
+
priority: 1,
|
|
1155
|
+
statement: {
|
|
1156
|
+
managedRuleGroupStatement: {
|
|
1157
|
+
name: 'AWSManagedRulesKnownBadInputsRuleSet',
|
|
1158
|
+
vendorName: 'AWS',
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
visibilityConfig: {
|
|
1162
|
+
cloudWatchMetricsEnabled: true,
|
|
1163
|
+
metricName: \`\${apiName}WebAcl-KnownBadInputs\`,
|
|
1164
|
+
sampledRequestsEnabled: true,
|
|
1165
|
+
},
|
|
1166
|
+
overrideAction: {
|
|
1167
|
+
none: {},
|
|
1168
|
+
},
|
|
1169
|
+
},
|
|
1170
|
+
],
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
new CfnWebACLAssociation(this, 'WebAclAssociation', {
|
|
1174
|
+
resourceArn: this.api.deploymentStage.stageArn,
|
|
1175
|
+
webAclArn: this.webAcl.attrArn,
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
// Send WAF request logs to CloudWatch. The log group name must start with
|
|
1179
|
+
// \`aws-waf-logs-\` to satisfy the WAFv2 logging destination requirement.
|
|
1180
|
+
const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
|
|
1181
|
+
logGroupName: \`aws-waf-logs-\${apiName}-\${this.node.addr.slice(-8)}\`,
|
|
1182
|
+
retention: RetentionDays.ONE_MONTH,
|
|
1183
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
1184
|
+
});
|
|
1185
|
+
suppressRules(
|
|
1186
|
+
wafLogGroup,
|
|
1187
|
+
['CKV_AWS_158'],
|
|
1188
|
+
'Using default CloudWatch log encryption for WAF logs',
|
|
1189
|
+
);
|
|
1190
|
+
|
|
1191
|
+
new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
|
|
1192
|
+
resourceArn: this.webAcl.attrArn,
|
|
1193
|
+
logDestinationConfigs: [wafLogGroup.logGroupArn],
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1099
1197
|
suppressRules(
|
|
1100
1198
|
this.api,
|
|
1101
1199
|
['CKV_AWS_120'],
|
|
@@ -1239,6 +1337,12 @@ export interface TestApiProps<
|
|
|
1239
1337
|
* Map of operation names to their API Gateway integrations
|
|
1240
1338
|
*/
|
|
1241
1339
|
integrations: TIntegrations;
|
|
1340
|
+
/**
|
|
1341
|
+
* Whether to enable AWS WAFv2 with the default managed ruleset on the API's default stage.
|
|
1342
|
+
*
|
|
1343
|
+
* @default true
|
|
1344
|
+
*/
|
|
1345
|
+
enableWaf?: boolean;
|
|
1242
1346
|
}
|
|
1243
1347
|
|
|
1244
1348
|
/**
|
|
@@ -3754,6 +3858,10 @@ terraform {
|
|
|
3754
3858
|
source = "hashicorp/aws"
|
|
3755
3859
|
version = "~> 6.33"
|
|
3756
3860
|
}
|
|
3861
|
+
random = {
|
|
3862
|
+
source = "hashicorp/random"
|
|
3863
|
+
version = "~> 3.1"
|
|
3864
|
+
}
|
|
3757
3865
|
}
|
|
3758
3866
|
}
|
|
3759
3867
|
|
|
@@ -3782,6 +3890,12 @@ variable "stage_auto_deploy" {
|
|
|
3782
3890
|
default = true
|
|
3783
3891
|
}
|
|
3784
3892
|
|
|
3893
|
+
variable "enable_waf" {
|
|
3894
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
3895
|
+
type = bool
|
|
3896
|
+
default = true
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3785
3899
|
# CORS Configuration
|
|
3786
3900
|
|
|
3787
3901
|
variable "cors_allow_headers" {
|
|
@@ -3863,6 +3977,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
|
3863
3977
|
}
|
|
3864
3978
|
}
|
|
3865
3979
|
|
|
3980
|
+
resource "aws_wafv2_web_acl" "api_waf" {
|
|
3981
|
+
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
3982
|
+
count = var.enable_waf ? 1 : 0
|
|
3983
|
+
|
|
3984
|
+
name = "\${var.api_name}-waf"
|
|
3985
|
+
scope = "REGIONAL"
|
|
3986
|
+
|
|
3987
|
+
default_action {
|
|
3988
|
+
allow {}
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
rule {
|
|
3992
|
+
name = "CRSRule"
|
|
3993
|
+
priority = 0
|
|
3994
|
+
|
|
3995
|
+
override_action {
|
|
3996
|
+
none {}
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
statement {
|
|
4000
|
+
managed_rule_group_statement {
|
|
4001
|
+
name = "AWSManagedRulesCommonRuleSet"
|
|
4002
|
+
vendor_name = "AWS"
|
|
4003
|
+
|
|
4004
|
+
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
4005
|
+
rule_action_override {
|
|
4006
|
+
name = "SizeRestrictions_BODY"
|
|
4007
|
+
action_to_use {
|
|
4008
|
+
count {}
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
visibility_config {
|
|
4015
|
+
cloudwatch_metrics_enabled = true
|
|
4016
|
+
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
4017
|
+
sampled_requests_enabled = true
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
rule {
|
|
4022
|
+
name = "KnownBadInputsRule"
|
|
4023
|
+
priority = 1
|
|
4024
|
+
|
|
4025
|
+
override_action {
|
|
4026
|
+
none {}
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
statement {
|
|
4030
|
+
managed_rule_group_statement {
|
|
4031
|
+
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
4032
|
+
vendor_name = "AWS"
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
visibility_config {
|
|
4037
|
+
cloudwatch_metrics_enabled = true
|
|
4038
|
+
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
4039
|
+
sampled_requests_enabled = true
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
visibility_config {
|
|
4044
|
+
cloudwatch_metrics_enabled = true
|
|
4045
|
+
metric_name = "\${var.api_name}WebAcl"
|
|
4046
|
+
sampled_requests_enabled = true
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
tags = var.tags
|
|
4050
|
+
|
|
4051
|
+
lifecycle {
|
|
4052
|
+
create_before_destroy = true
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
4057
|
+
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
4058
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
4059
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
4060
|
+
count = var.enable_waf ? 1 : 0
|
|
4061
|
+
|
|
4062
|
+
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
4063
|
+
retention_in_days = 30
|
|
4064
|
+
tags = var.tags
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4067
|
+
resource "random_id" "waf_log_suffix" {
|
|
4068
|
+
count = var.enable_waf ? 1 : 0
|
|
4069
|
+
byte_length = 4
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
4073
|
+
count = var.enable_waf ? 1 : 0
|
|
4074
|
+
|
|
4075
|
+
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
4076
|
+
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
|
|
3866
4080
|
# Outputs
|
|
3867
4081
|
|
|
3868
4082
|
output "api_id" {
|
|
@@ -3890,6 +4104,11 @@ output "api_root_resource_id" {
|
|
|
3890
4104
|
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
3891
4105
|
}
|
|
3892
4106
|
|
|
4107
|
+
output "waf_web_acl_arn" {
|
|
4108
|
+
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
4109
|
+
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
4110
|
+
}
|
|
4111
|
+
|
|
3893
4112
|
# Note: Stage and deployment outputs are provided by the consuming module
|
|
3894
4113
|
|
|
3895
4114
|
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
@@ -3951,6 +4170,13 @@ variable "cors_allow_origins" {
|
|
|
3951
4170
|
default = ["*"]
|
|
3952
4171
|
}
|
|
3953
4172
|
|
|
4173
|
+
# WAF Configuration
|
|
4174
|
+
variable "enable_waf" {
|
|
4175
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
4176
|
+
type = bool
|
|
4177
|
+
default = true
|
|
4178
|
+
}
|
|
4179
|
+
|
|
3954
4180
|
# Tags
|
|
3955
4181
|
variable "tags" {
|
|
3956
4182
|
description = "Tags to apply to all resources"
|
|
@@ -3980,6 +4206,9 @@ module "rest_api" {
|
|
|
3980
4206
|
stage_name = "prod"
|
|
3981
4207
|
stage_auto_deploy = true
|
|
3982
4208
|
|
|
4209
|
+
# WAF Configuration
|
|
4210
|
+
enable_waf = var.enable_waf
|
|
4211
|
+
|
|
3983
4212
|
# CORS Configuration
|
|
3984
4213
|
cors_allow_headers = var.cors_allow_headers
|
|
3985
4214
|
cors_allow_methods = var.cors_allow_methods
|
|
@@ -3989,6 +4218,15 @@ module "rest_api" {
|
|
|
3989
4218
|
tags = var.tags
|
|
3990
4219
|
}
|
|
3991
4220
|
|
|
4221
|
+
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
4222
|
+
count = var.enable_waf ? 1 : 0
|
|
4223
|
+
|
|
4224
|
+
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
4225
|
+
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
4226
|
+
|
|
4227
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
4228
|
+
}
|
|
4229
|
+
|
|
3992
4230
|
# Lambda function
|
|
3993
4231
|
resource "aws_lambda_function" "api_lambda" {
|
|
3994
4232
|
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
@@ -4223,6 +4461,7 @@ resource "aws_api_gateway_stage" "api_stage" {
|
|
|
4223
4461
|
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
4224
4462
|
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
4225
4463
|
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
4464
|
+
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
4226
4465
|
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
4227
4466
|
rest_api_id = module.rest_api.api_id
|
|
4228
4467
|
stage_name = "prod"
|
|
@@ -4432,6 +4671,10 @@ terraform {
|
|
|
4432
4671
|
source = "hashicorp/aws"
|
|
4433
4672
|
version = "~> 6.33"
|
|
4434
4673
|
}
|
|
4674
|
+
random = {
|
|
4675
|
+
source = "hashicorp/random"
|
|
4676
|
+
version = "~> 3.1"
|
|
4677
|
+
}
|
|
4435
4678
|
}
|
|
4436
4679
|
}
|
|
4437
4680
|
|
|
@@ -4460,6 +4703,12 @@ variable "stage_auto_deploy" {
|
|
|
4460
4703
|
default = true
|
|
4461
4704
|
}
|
|
4462
4705
|
|
|
4706
|
+
variable "enable_waf" {
|
|
4707
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
4708
|
+
type = bool
|
|
4709
|
+
default = true
|
|
4710
|
+
}
|
|
4711
|
+
|
|
4463
4712
|
# CORS Configuration
|
|
4464
4713
|
|
|
4465
4714
|
variable "cors_allow_headers" {
|
|
@@ -4541,6 +4790,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
|
4541
4790
|
}
|
|
4542
4791
|
}
|
|
4543
4792
|
|
|
4793
|
+
resource "aws_wafv2_web_acl" "api_waf" {
|
|
4794
|
+
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
4795
|
+
count = var.enable_waf ? 1 : 0
|
|
4796
|
+
|
|
4797
|
+
name = "\${var.api_name}-waf"
|
|
4798
|
+
scope = "REGIONAL"
|
|
4799
|
+
|
|
4800
|
+
default_action {
|
|
4801
|
+
allow {}
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
rule {
|
|
4805
|
+
name = "CRSRule"
|
|
4806
|
+
priority = 0
|
|
4807
|
+
|
|
4808
|
+
override_action {
|
|
4809
|
+
none {}
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
statement {
|
|
4813
|
+
managed_rule_group_statement {
|
|
4814
|
+
name = "AWSManagedRulesCommonRuleSet"
|
|
4815
|
+
vendor_name = "AWS"
|
|
4816
|
+
|
|
4817
|
+
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
4818
|
+
rule_action_override {
|
|
4819
|
+
name = "SizeRestrictions_BODY"
|
|
4820
|
+
action_to_use {
|
|
4821
|
+
count {}
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
|
|
4827
|
+
visibility_config {
|
|
4828
|
+
cloudwatch_metrics_enabled = true
|
|
4829
|
+
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
4830
|
+
sampled_requests_enabled = true
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4834
|
+
rule {
|
|
4835
|
+
name = "KnownBadInputsRule"
|
|
4836
|
+
priority = 1
|
|
4837
|
+
|
|
4838
|
+
override_action {
|
|
4839
|
+
none {}
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
statement {
|
|
4843
|
+
managed_rule_group_statement {
|
|
4844
|
+
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
4845
|
+
vendor_name = "AWS"
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
visibility_config {
|
|
4850
|
+
cloudwatch_metrics_enabled = true
|
|
4851
|
+
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
4852
|
+
sampled_requests_enabled = true
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4856
|
+
visibility_config {
|
|
4857
|
+
cloudwatch_metrics_enabled = true
|
|
4858
|
+
metric_name = "\${var.api_name}WebAcl"
|
|
4859
|
+
sampled_requests_enabled = true
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
tags = var.tags
|
|
4863
|
+
|
|
4864
|
+
lifecycle {
|
|
4865
|
+
create_before_destroy = true
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
|
|
4869
|
+
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
4870
|
+
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
4871
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
4872
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
4873
|
+
count = var.enable_waf ? 1 : 0
|
|
4874
|
+
|
|
4875
|
+
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
4876
|
+
retention_in_days = 30
|
|
4877
|
+
tags = var.tags
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4880
|
+
resource "random_id" "waf_log_suffix" {
|
|
4881
|
+
count = var.enable_waf ? 1 : 0
|
|
4882
|
+
byte_length = 4
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
4886
|
+
count = var.enable_waf ? 1 : 0
|
|
4887
|
+
|
|
4888
|
+
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
4889
|
+
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4892
|
+
|
|
4544
4893
|
# Outputs
|
|
4545
4894
|
|
|
4546
4895
|
output "api_id" {
|
|
@@ -4568,6 +4917,11 @@ output "api_root_resource_id" {
|
|
|
4568
4917
|
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
4569
4918
|
}
|
|
4570
4919
|
|
|
4920
|
+
output "waf_web_acl_arn" {
|
|
4921
|
+
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
4922
|
+
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4571
4925
|
# Note: Stage and deployment outputs are provided by the consuming module
|
|
4572
4926
|
|
|
4573
4927
|
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
@@ -4619,6 +4973,13 @@ variable "cors_allow_origins" {
|
|
|
4619
4973
|
default = ["*"]
|
|
4620
4974
|
}
|
|
4621
4975
|
|
|
4976
|
+
# WAF Configuration
|
|
4977
|
+
variable "enable_waf" {
|
|
4978
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
4979
|
+
type = bool
|
|
4980
|
+
default = true
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4622
4983
|
# Tags
|
|
4623
4984
|
variable "tags" {
|
|
4624
4985
|
description = "Tags to apply to all resources"
|
|
@@ -4648,6 +5009,9 @@ module "rest_api" {
|
|
|
4648
5009
|
stage_name = "prod"
|
|
4649
5010
|
stage_auto_deploy = true
|
|
4650
5011
|
|
|
5012
|
+
# WAF Configuration
|
|
5013
|
+
enable_waf = var.enable_waf
|
|
5014
|
+
|
|
4651
5015
|
# CORS Configuration
|
|
4652
5016
|
cors_allow_headers = var.cors_allow_headers
|
|
4653
5017
|
cors_allow_methods = var.cors_allow_methods
|
|
@@ -4657,6 +5021,15 @@ module "rest_api" {
|
|
|
4657
5021
|
tags = var.tags
|
|
4658
5022
|
}
|
|
4659
5023
|
|
|
5024
|
+
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
5025
|
+
count = var.enable_waf ? 1 : 0
|
|
5026
|
+
|
|
5027
|
+
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
5028
|
+
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
5029
|
+
|
|
5030
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
5031
|
+
}
|
|
5032
|
+
|
|
4660
5033
|
# Lambda function
|
|
4661
5034
|
resource "aws_lambda_function" "api_lambda" {
|
|
4662
5035
|
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
@@ -4882,6 +5255,7 @@ resource "aws_api_gateway_stage" "api_stage" {
|
|
|
4882
5255
|
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
4883
5256
|
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
4884
5257
|
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
5258
|
+
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
4885
5259
|
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
4886
5260
|
rest_api_id = module.rest_api.api_id
|
|
4887
5261
|
stage_name = "prod"
|
|
@@ -5102,6 +5476,10 @@ terraform {
|
|
|
5102
5476
|
source = "hashicorp/aws"
|
|
5103
5477
|
version = "~> 6.33"
|
|
5104
5478
|
}
|
|
5479
|
+
random = {
|
|
5480
|
+
source = "hashicorp/random"
|
|
5481
|
+
version = "~> 3.1"
|
|
5482
|
+
}
|
|
5105
5483
|
}
|
|
5106
5484
|
}
|
|
5107
5485
|
|
|
@@ -5130,6 +5508,12 @@ variable "stage_auto_deploy" {
|
|
|
5130
5508
|
default = true
|
|
5131
5509
|
}
|
|
5132
5510
|
|
|
5511
|
+
variable "enable_waf" {
|
|
5512
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
5513
|
+
type = bool
|
|
5514
|
+
default = true
|
|
5515
|
+
}
|
|
5516
|
+
|
|
5133
5517
|
# CORS Configuration
|
|
5134
5518
|
|
|
5135
5519
|
variable "cors_allow_headers" {
|
|
@@ -5211,6 +5595,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
|
5211
5595
|
}
|
|
5212
5596
|
}
|
|
5213
5597
|
|
|
5598
|
+
resource "aws_wafv2_web_acl" "api_waf" {
|
|
5599
|
+
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
5600
|
+
count = var.enable_waf ? 1 : 0
|
|
5601
|
+
|
|
5602
|
+
name = "\${var.api_name}-waf"
|
|
5603
|
+
scope = "REGIONAL"
|
|
5604
|
+
|
|
5605
|
+
default_action {
|
|
5606
|
+
allow {}
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
rule {
|
|
5610
|
+
name = "CRSRule"
|
|
5611
|
+
priority = 0
|
|
5612
|
+
|
|
5613
|
+
override_action {
|
|
5614
|
+
none {}
|
|
5615
|
+
}
|
|
5616
|
+
|
|
5617
|
+
statement {
|
|
5618
|
+
managed_rule_group_statement {
|
|
5619
|
+
name = "AWSManagedRulesCommonRuleSet"
|
|
5620
|
+
vendor_name = "AWS"
|
|
5621
|
+
|
|
5622
|
+
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
5623
|
+
rule_action_override {
|
|
5624
|
+
name = "SizeRestrictions_BODY"
|
|
5625
|
+
action_to_use {
|
|
5626
|
+
count {}
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
|
|
5632
|
+
visibility_config {
|
|
5633
|
+
cloudwatch_metrics_enabled = true
|
|
5634
|
+
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
5635
|
+
sampled_requests_enabled = true
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5639
|
+
rule {
|
|
5640
|
+
name = "KnownBadInputsRule"
|
|
5641
|
+
priority = 1
|
|
5642
|
+
|
|
5643
|
+
override_action {
|
|
5644
|
+
none {}
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5647
|
+
statement {
|
|
5648
|
+
managed_rule_group_statement {
|
|
5649
|
+
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
5650
|
+
vendor_name = "AWS"
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
|
|
5654
|
+
visibility_config {
|
|
5655
|
+
cloudwatch_metrics_enabled = true
|
|
5656
|
+
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
5657
|
+
sampled_requests_enabled = true
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
|
|
5661
|
+
visibility_config {
|
|
5662
|
+
cloudwatch_metrics_enabled = true
|
|
5663
|
+
metric_name = "\${var.api_name}WebAcl"
|
|
5664
|
+
sampled_requests_enabled = true
|
|
5665
|
+
}
|
|
5666
|
+
|
|
5667
|
+
tags = var.tags
|
|
5668
|
+
|
|
5669
|
+
lifecycle {
|
|
5670
|
+
create_before_destroy = true
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
|
|
5674
|
+
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
5675
|
+
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
5676
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
5677
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
5678
|
+
count = var.enable_waf ? 1 : 0
|
|
5679
|
+
|
|
5680
|
+
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
5681
|
+
retention_in_days = 30
|
|
5682
|
+
tags = var.tags
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
resource "random_id" "waf_log_suffix" {
|
|
5686
|
+
count = var.enable_waf ? 1 : 0
|
|
5687
|
+
byte_length = 4
|
|
5688
|
+
}
|
|
5689
|
+
|
|
5690
|
+
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
5691
|
+
count = var.enable_waf ? 1 : 0
|
|
5692
|
+
|
|
5693
|
+
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
5694
|
+
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
5695
|
+
}
|
|
5696
|
+
|
|
5697
|
+
|
|
5214
5698
|
# Outputs
|
|
5215
5699
|
|
|
5216
5700
|
output "api_id" {
|
|
@@ -5238,6 +5722,11 @@ output "api_root_resource_id" {
|
|
|
5238
5722
|
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
5239
5723
|
}
|
|
5240
5724
|
|
|
5725
|
+
output "waf_web_acl_arn" {
|
|
5726
|
+
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
5727
|
+
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
5728
|
+
}
|
|
5729
|
+
|
|
5241
5730
|
# Note: Stage and deployment outputs are provided by the consuming module
|
|
5242
5731
|
|
|
5243
5732
|
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
@@ -5289,6 +5778,13 @@ variable "cors_allow_origins" {
|
|
|
5289
5778
|
default = ["*"]
|
|
5290
5779
|
}
|
|
5291
5780
|
|
|
5781
|
+
# WAF Configuration
|
|
5782
|
+
variable "enable_waf" {
|
|
5783
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
5784
|
+
type = bool
|
|
5785
|
+
default = true
|
|
5786
|
+
}
|
|
5787
|
+
|
|
5292
5788
|
# Tags
|
|
5293
5789
|
variable "tags" {
|
|
5294
5790
|
description = "Tags to apply to all resources"
|
|
@@ -5318,6 +5814,9 @@ module "rest_api" {
|
|
|
5318
5814
|
stage_name = "prod"
|
|
5319
5815
|
stage_auto_deploy = true
|
|
5320
5816
|
|
|
5817
|
+
# WAF Configuration
|
|
5818
|
+
enable_waf = var.enable_waf
|
|
5819
|
+
|
|
5321
5820
|
# CORS Configuration
|
|
5322
5821
|
cors_allow_headers = var.cors_allow_headers
|
|
5323
5822
|
cors_allow_methods = var.cors_allow_methods
|
|
@@ -5327,6 +5826,15 @@ module "rest_api" {
|
|
|
5327
5826
|
tags = var.tags
|
|
5328
5827
|
}
|
|
5329
5828
|
|
|
5829
|
+
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
5830
|
+
count = var.enable_waf ? 1 : 0
|
|
5831
|
+
|
|
5832
|
+
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
5833
|
+
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
5834
|
+
|
|
5835
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
5836
|
+
}
|
|
5837
|
+
|
|
5330
5838
|
# Lambda function
|
|
5331
5839
|
resource "aws_lambda_function" "api_lambda" {
|
|
5332
5840
|
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
@@ -5554,6 +6062,7 @@ resource "aws_api_gateway_stage" "api_stage" {
|
|
|
5554
6062
|
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
5555
6063
|
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
5556
6064
|
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
6065
|
+
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
5557
6066
|
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
5558
6067
|
rest_api_id = module.rest_api.api_id
|
|
5559
6068
|
stage_name = "prod"
|