From 6c54178585c7807360ddfba59648f747a555a6ca Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Fri, 26 Apr 2024 16:04:27 +0300 Subject: [PATCH 1/5] AV-2221: Implement new config options for waf rules --- cdk/bin/opendata.ts | 37 +++++ cdk/lib/cloudfront-parameter-stack.ts | 77 +++++++++++ cdk/lib/shield-stack-props.ts | 12 +- cdk/lib/shield-stack.ts | 124 ++++++++++++----- cdk/package-lock.json | 192 +++++++++++++++++--------- cdk/package.json | 3 +- 6 files changed, 347 insertions(+), 98 deletions(-) create mode 100644 cdk/lib/cloudfront-parameter-stack.ts diff --git a/cdk/bin/opendata.ts b/cdk/bin/opendata.ts index 93c0dbd807..e7bc48ac72 100644 --- a/cdk/bin/opendata.ts +++ b/cdk/bin/opendata.ts @@ -20,6 +20,8 @@ import {DomainStack} from "../lib/domain-stack"; import {CiTestStack} from "../lib/ci-test-stack"; import {SubDomainStack} from "../lib/sub-domain-stack"; import {ShieldStack} from "../lib/shield-stack"; +import {CloudfrontParameterStack} from "../lib/cloudfront-parameter-stack"; +import {undefined} from "zod"; // load .env file, shared with docker setup // mainly for ECR repo and image tag information @@ -164,6 +166,15 @@ const bypassCdnStackBeta = new BypassCdnStack(app, 'BypassCdnStack-beta', { loadbalancer: loadBalancerStackBeta.loadBalancer }) +const cloudfrontParameterStackBeta = new CloudfrontParameterStack(app, 'CloudfrontParameterStack-beta', { + env: { + account: betaProps.account, + region: 'us-east-1', + }, + environment: betaProps.environment, +}) + + const shieldStackBeta = new ShieldStack(app, 'ShieldStack-beta', { env: { account: betaProps.account, @@ -174,6 +185,15 @@ const shieldStackBeta = new ShieldStack(app, 'ShieldStack-beta', { highPriorityRequestSamplingEnabled: false, rateLimitRequestSamplingEnabled: false, requestSampleAllTrafficEnabled: false, + cloudfrontDistributionArn: cloudfrontParameterStackBeta.cloudFrontDistributionArn, + bannedIpListParameterName: cloudfrontParameterStackBeta.bannedIpListParameterName, + whitelistedIpListParameterName: cloudfrontParameterStackBeta.whitelistedIpListParameterName, + highPriorityCountryCodeListParameterName: cloudfrontParameterStackBeta.highPriorityCountryCodeListParameterName, + highPriorityRateLimit: cloudfrontParameterStackBeta.highPriorityRateLimit, + rateLimit: cloudfrontParameterStackBeta.rateLimit, + managedRulesParameterName: cloudfrontParameterStackBeta.managedRulesParameterName, + snsTopicArn: cloudfrontParameterStackBeta.snsTopicArn, + wafAutomationArn: cloudfrontParameterStackBeta.wafAutomationArn }) const cacheStackBeta = new CacheStack(app, 'CacheStack-beta', { @@ -465,6 +485,14 @@ const bypassCdnStackProd = new BypassCdnStack(app, 'BypassCdnStack-prod', { loadbalancer: loadBalancerStackProd.loadBalancer }) +const cloudfrontParameterStackProd = new CloudfrontParameterStack(app, 'CloudfrontParameterStack-prod', { + env: { + account: prodProps.account, + region: 'us-east-1' + }, + environment: prodProps.environment, +}) + const shieldStackProd = new ShieldStack(app, 'ShieldStack-prod', { env: { account: prodProps.account, @@ -475,6 +503,15 @@ const shieldStackProd = new ShieldStack(app, 'ShieldStack-prod', { highPriorityRequestSamplingEnabled: false, rateLimitRequestSamplingEnabled: false, requestSampleAllTrafficEnabled: false, + cloudfrontDistributionArn: cloudfrontParameterStackProd.cloudFrontDistributionArn, + bannedIpListParameterName: cloudfrontParameterStackProd.bannedIpListParameterName, + whitelistedIpListParameterName: cloudfrontParameterStackProd.whitelistedIpListParameterName, + highPriorityCountryCodeListParameterName: cloudfrontParameterStackProd.highPriorityCountryCodeListParameterName, + highPriorityRateLimit: cloudfrontParameterStackProd.highPriorityRateLimit, + rateLimit: cloudfrontParameterStackProd.rateLimit, + managedRulesParameterName: cloudfrontParameterStackProd.managedRulesParameterName, + snsTopicArn: cloudfrontParameterStackProd.snsTopicArn, + wafAutomationArn: cloudfrontParameterStackProd.wafAutomationArn }) const cacheStackProd = new CacheStack(app, 'CacheStack-prod', { diff --git a/cdk/lib/cloudfront-parameter-stack.ts b/cdk/lib/cloudfront-parameter-stack.ts new file mode 100644 index 0000000000..a6f676cb46 --- /dev/null +++ b/cdk/lib/cloudfront-parameter-stack.ts @@ -0,0 +1,77 @@ +import {aws_ssm, CfnParameter, Stack} from "aws-cdk-lib"; +import {Construct} from "constructs"; + +import {EnvStackProps} from "./env-stack-props"; + +export class CloudfrontParameterStack extends Stack { + readonly cloudFrontDistributionArn: aws_ssm.IStringParameter; + readonly bannedIpListParameterName: string; + readonly whitelistedIpListParameterName: string; + readonly highPriorityCountryCodeListParameterName: string; + readonly highPriorityRateLimit: aws_ssm.IStringParameter; + readonly rateLimit: aws_ssm.IStringParameter; + readonly managedRulesParameterName: string; + readonly wafAutomationArn: aws_ssm.IStringParameter; + readonly snsTopicArn: aws_ssm.IStringParameter; + constructor(scope: Construct, id: string, props: EnvStackProps ) { + super(scope, id, props); + + this.cloudFrontDistributionArn = new aws_ssm.StringParameter(this, 'cloudfrontDistributionArn', { + stringValue: '', + description: 'Arn of cloudfront distribution', + parameterName: `/${props.environment}/waf/cloudfrontDistributionArn`, + }) + + this.bannedIpListParameterName = `/${props.environment}/waf/banned_ips` + new aws_ssm.StringListParameter(this, 'bannedIplist', { + stringListValue: ["127.0.0.1"], + description: 'List of banned IP addresses', + parameterName: this.bannedIpListParameterName + }) + + this.whitelistedIpListParameterName = `/${props.environment}/waf/whitelisted_ips` + new aws_ssm.StringListParameter(this, 'whitelistedIplist', { + stringListValue: ["127.0.0.1"], + description: 'List of whitelisted IP addresses', + parameterName: this.whitelistedIpListParameterName + }) + + this.highPriorityCountryCodeListParameterName = `/${props.environment}/waf/high_priority_country_codes` + new aws_ssm.StringListParameter(this, 'highPriorityCountryCodeList', { + stringListValue: ["Some bogus country code"], + description: 'Country codes deemed high priority', + parameterName: this.highPriorityCountryCodeListParameterName + }) + + this.highPriorityRateLimit = new aws_ssm.StringParameter(this, 'highPriorityRateLimit', { + stringValue: '0', + description: 'Rate limit for high priority country codes', + parameterName: `/${props.environment}/waf/high_priority_rate_limit` + }) + + this.rateLimit = new aws_ssm.StringParameter(this, 'rateLimit', { + stringValue: '0', + description: 'Rate limit for others', + parameterName: `/${props.environment}/waf/rate_limit` + }) + + this.managedRulesParameterName = `/${props.environment}/waf/managed_rules` + new aws_ssm.StringParameter(this, 'managedRules', { + stringValue: '', + description: 'JSON value for managed rules', + parameterName: this.managedRulesParameterName + }) + + this.wafAutomationArn = new aws_ssm.StringParameter(this, 'wafAutomationArn', { + stringValue: '', + description: 'Arn of waf automation lambda', + parameterName: `/${props.environment}/waf/waf_automation_arn`, + }) + + this.snsTopicArn = new aws_ssm.StringParameter(this, 'snsTopicArn', { + stringValue: '', + description: 'Arn of sns topic', + parameterName: `/${props.environment}/waf/sns_topic_arn`, + }) + } +} diff --git a/cdk/lib/shield-stack-props.ts b/cdk/lib/shield-stack-props.ts index 1b1541917b..64ae6c1d33 100644 --- a/cdk/lib/shield-stack-props.ts +++ b/cdk/lib/shield-stack-props.ts @@ -1,8 +1,18 @@ import {EnvStackProps} from "./env-stack-props"; +import {aws_ssm} from "aws-cdk-lib"; export interface ShieldStackProps extends EnvStackProps{ bannedIpsRequestSamplingEnabled: boolean, requestSampleAllTrafficEnabled: boolean, highPriorityRequestSamplingEnabled: boolean, - rateLimitRequestSamplingEnabled: boolean + rateLimitRequestSamplingEnabled: boolean, + cloudfrontDistributionArn: aws_ssm.IStringParameter, + bannedIpListParameterName: string, + whitelistedIpListParameterName: string, + highPriorityCountryCodeListParameterName: string, + highPriorityRateLimit: aws_ssm.IStringParameter, + rateLimit: aws_ssm.IStringParameter, + managedRulesParameterName: string, + wafAutomationArn: aws_ssm.IStringParameter, + snsTopicArn: aws_ssm.IStringParameter } diff --git a/cdk/lib/shield-stack.ts b/cdk/lib/shield-stack.ts index 6a01a28384..c3a5a89efa 100644 --- a/cdk/lib/shield-stack.ts +++ b/cdk/lib/shield-stack.ts @@ -10,23 +10,24 @@ import {Construct} from "constructs"; import {ShieldStackProps} from "./shield-stack-props"; +import { z } from "zod"; export class ShieldStack extends Stack { constructor(scope: Construct, id: string, props: ShieldStackProps) { super(scope, id, props); - const cloudfrontDistributionArn = aws_ssm.StringParameter.fromStringParameterName(this,'cloudfrontDistributionArn', - `/${props.environment}/waf/cloudfrontDistributionArn`) + //const cloudfrontDistributionArn = aws_ssm.StringParameter.fromStringParameterName(this,'cloudfrontDistributionArn', + // `/${props.environment}/waf/cloudfrontDistributionArn`) const cfnProtection = new aws_shield.CfnProtection(this, 'ShieldProtection', { name: 'Cloudfront distribution', - resourceArn: cloudfrontDistributionArn.stringValue + resourceArn: props.cloudfrontDistributionArn.stringValue }) const banned_ips = new CfnParameter(this, 'bannedIpsList', { type: 'AWS::SSM::Parameter::Value>', - default: `/${props.environment}/waf/banned_ips` + default: props.bannedIpListParameterName }) const cfnBannedIPSet = new aws_wafv2.CfnIPSet(this, 'BannedIPSet', { @@ -38,7 +39,7 @@ export class ShieldStack extends Stack { const whitelisted_ips = new CfnParameter(this, 'whitelistedIpsList', { type: 'AWS::SSM::Parameter::Value>', - default: `/${props.environment}/waf/whitelisted_ips` + default: props.whitelistedIpListParameterName }) const cfnWhiteListedIpSet = new aws_wafv2.CfnIPSet(this, 'WhitelistedIPSet', { @@ -51,21 +52,17 @@ export class ShieldStack extends Stack { const highPriorityCountryCodesParameter = new CfnParameter(this, 'highPriorityCountryCodesParameter', { type: 'AWS::SSM::Parameter::Value>', - default: `/${props.environment}/waf/high_priority_country_codes` + default: props.highPriorityCountryCodeListParameterName }); - const highPriorityRateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'highPriorityRateLimit', - `/${props.environment}/waf/high_priority_rate_limit`); + //const highPriorityRateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'highPriorityRateLimit', + // `/${props.environment}/waf/high_priority_rate_limit`); - const rateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'rateLimit', - `/${props.environment}/waf/rate_limit`); + //const rateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'rateLimit', + // `/${props.environment}/waf/rate_limit`); - const managedRulesParameter = aws_ssm.StringParameter.valueFromLookup(this, `/${props.environment}/waf/managed_rules`) - - const managedRules = managedRulesParameter.startsWith("dummy-value") ? "dummy" : JSON.parse(managedRulesParameter) - let rules = [ { name: "block-banned_ips", @@ -134,7 +131,7 @@ export class ShieldStack extends Stack { }, statement: { rateBasedStatement: { - limit: Token.asNumber(highPriorityRateLimit.stringValue), + limit: Token.asNumber(props.highPriorityRateLimit.stringValue), aggregateKeyType: "IP", scopeDownStatement: { geoMatchStatement: { @@ -158,7 +155,7 @@ export class ShieldStack extends Stack { }, statement: { rateBasedStatement: { - limit: Token.asNumber(rateLimit.stringValue), + limit: Token.asNumber(props.rateLimit.stringValue), aggregateKeyType: "IP", scopeDownStatement: { notStatement: { @@ -179,31 +176,90 @@ export class ShieldStack extends Stack { }, ] - type RuleGroup = { - groupName: string, - vendorName: string, - excludedRules: string[], - enableRequestSampling: boolean - } + const RuleGroupSchema = z.array( + z.object( + { + groupName: z.string(), + vendorName: z.string(), + ruleActionOverrideCounts: z.array(z.string()).default([]), + ruleActionOverrideAllows: z.array(z.string()).default([]), + ruleActionOverrideBlocks: z.array(z.string()).default([]), + ruleActionOverrideCaptchas: z.array(z.string()).default([]), + ruleActionOverrideChallenges: z.array(z.string()).default([]), + enableRequestSampling: z.boolean() + } + ).strict() + ) + + + const managedRulesParameter = aws_ssm.StringParameter.valueFromLookup(this, props.managedRulesParameterName) + + const managedRules = managedRulesParameter.startsWith("dummy-value") ? "dummy" : JSON.parse(managedRulesParameter) if ( managedRules !== "dummy"){ let ruleList: any[] = [] - - managedRules.forEach((rule: RuleGroup, index: number) => { + console.log(managedRules) + const validatedRules = RuleGroupSchema.parse(managedRules) + console.log(validatedRules) + validatedRules.forEach((rule, index: number) => { let ruleActionOverrides = [] - for (let excludedRule of rule.excludedRules) { - let excludedRuleObj = { + for (let overrideCountRule of rule.ruleActionOverrideCounts) { + let overrideCountRuleObj = { actionToUse: { count: {} }, - name: excludedRule + name: overrideCountRule + } + + ruleActionOverrides.push(overrideCountRuleObj) + } + + for ( let overrideAllowRule of rule.ruleActionOverrideAllows) { + let overrideAllowRuleObj = { + actionToUse: { + allow: {} + }, + name: overrideAllowRule + } + + ruleActionOverrides.push(overrideAllowRuleObj) + } + + for ( let overrideBlockRule of rule.ruleActionOverrideBlocks) { + let overrideBlockRuleObj = { + actionToUse: { + block: {} + }, + name: overrideBlockRule } - ruleActionOverrides.push(excludedRuleObj) + ruleActionOverrides.push(overrideBlockRuleObj) } + for ( let overrideCaptchaRule of rule.ruleActionOverrideCaptchas) { + let overrideCaptchaRuleObj = { + actionToUse: { + captcha: {} + }, + name: overrideCaptchaRule + } + + ruleActionOverrides.push(overrideCaptchaRuleObj) + } + for ( let overrideChallengeRule of rule.ruleActionOverrideChallenges) { + let overrideChallengeRuleObj = { + actionToUse: { + challenge: {} + }, + name: overrideChallengeRule + } + + ruleActionOverrides.push(overrideChallengeRuleObj) + } + + let managedRuleGroup: aws_wafv2.CfnWebACL.RuleProperty = { name: "managed-rule-group-" + rule.groupName, @@ -247,16 +303,16 @@ export class ShieldStack extends Stack { rules: rules }) - const WafAutomationArn = aws_ssm.StringParameter.fromStringParameterName(this, 'WafAutomationArn', - `/${props.environment}/waf/waf_automation_arn`); + //const WafAutomationArn = aws_ssm.StringParameter.fromStringParameterName(this, 'WafAutomationArn', + // `/${props.environment}/waf/waf_automation_arn`); - const WafAutomationLambdaFunction = aws_lambda.Function.fromFunctionArn(this, "WafAutomation", WafAutomationArn.stringValue) + const WafAutomationLambdaFunction = aws_lambda.Function.fromFunctionArn(this, "WafAutomation", props.wafAutomationArn.stringValue) - const SNSTopicArn = aws_ssm.StringParameter.fromStringParameterName(this, 'SNSTopicArn', - `/${props.environment}/waf/sns_topic_arn`); + //const SNSTopicArn = aws_ssm.StringParameter.fromStringParameterName(this, 'SNSTopicArn', + // `/${props.environment}/waf/sns_topic_arn`); - const topic = aws_sns.Topic.fromTopicArn(this, "SNSTopic", SNSTopicArn.stringValue) + const topic = aws_sns.Topic.fromTopicArn(this, "SNSTopic", props.snsTopicArn.stringValue) topic.addSubscription(new aws_sns_subscriptions.LambdaSubscription(WafAutomationLambdaFunction)) diff --git a/cdk/package-lock.json b/cdk/package-lock.json index b6ca9af3d8..74ede607c4 100644 --- a/cdk/package-lock.json +++ b/cdk/package-lock.json @@ -15,7 +15,8 @@ "form-data": "^4.0.0", "knex": "^2.4.2", "pg": "^8.11.0", - "source-map-support": "^0.5.16" + "source-map-support": "^0.5.16", + "zod": "^3.23.4" }, "bin": { "opendata": "bin/opendata.js" @@ -45,19 +46,19 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.184", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.184.tgz", - "integrity": "sha512-03q3Pm/IFEJEA4QS1GH87LwU4YhN1nuvA986k7KtaMIMPTOt/YXpUsriw/Sx2XcTpUk419sPGewr5N0D2slDCg==" + "version": "2.2.202", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", + "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==" }, "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.155", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", - "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==" + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz", + "integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg==" }, "node_modules/@aws-crypto/crc32": { "version": "3.0.0", @@ -2278,9 +2279,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/aws-cdk": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.84.0.tgz", - "integrity": "sha512-XypGsMW+H6DLLIPt4zG5KWv1FuUlpS6jqEROxg5maJFFsRaQnMkfgXP/ZT1IELyIWCYAZl6xD1rz7WRS3yMueA==", + "version": "2.139.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.139.0.tgz", + "integrity": "sha512-MjMsySQbhR5yTWCphnuVQuS15UdGMV6v4XIM+C8SN7/eUOfv7BFr7QgYMUm5WXCG/f66RnY0zjJbOLRxvcjCrQ==", "dev": true, "bin": { "cdk": "bin/cdk" @@ -2293,9 +2294,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.84.0.tgz", - "integrity": "sha512-4zLtCLCIs5Ia4WRGqiXRwxSkpGaNy3NxMexO9qYHSuIYpqf4sHObzZ0tDHZCFL5Wkui3sCu3OLQWrRHrr93HvA==", + "version": "2.139.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.139.0.tgz", + "integrity": "sha512-G9yoc+VFwF10kpgf4omtrAVmUNPeAP708oF5fc7XlRTzoTXMmAdUJW9cRGOMtAkFY83SxiJP0wm8n5Z9tjAdUA==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -2306,21 +2307,23 @@ "punycode", "semver", "table", - "yaml" + "yaml", + "mime-types" ], "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.177", - "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@aws-cdk/asset-awscli-v1": "^2.2.202", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.1.1", - "ignore": "^5.2.4", + "fs-extra": "^11.2.0", + "ignore": "^5.3.1", "jsonschema": "^1.4.1", + "mime-types": "^2.1.35", "minimatch": "^3.1.2", - "punycode": "^2.3.0", - "semver": "^7.5.1", - "table": "^6.8.1", + "punycode": "^2.3.1", + "semver": "^7.6.0", + "table": "^6.8.2", "yaml": "1.10.2" }, "engines": { @@ -2434,7 +2437,7 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.1.1", + "version": "11.2.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -2452,7 +2455,7 @@ "license": "ISC" }, "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.2.4", + "version": "5.3.1", "inBundle": true, "license": "MIT", "engines": { @@ -2507,6 +2510,25 @@ "node": ">=10" } }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/aws-cdk-lib/node_modules/minimatch": { "version": "3.1.2", "inBundle": true, @@ -2519,7 +2541,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.3.0", + "version": "2.3.1", "inBundle": true, "license": "MIT", "engines": { @@ -2535,7 +2557,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.5.1", + "version": "7.6.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -2589,7 +2611,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.8.1", + "version": "6.8.2", "inBundle": true, "license": "BSD-3-Clause", "dependencies": { @@ -2604,7 +2626,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/universalify": { - "version": "2.0.0", + "version": "2.0.1", "inBundle": true, "license": "MIT", "engines": { @@ -3300,6 +3322,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5821,6 +5857,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.4.tgz", + "integrity": "sha512-/AtWOKbBgjzEYYQRNfoGKHObgfAZag6qUJX1VbHo2PRBgS+wfWagEY2mizjfyAPcGesrJOcx/wcl0L9WnVrHFw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -5835,19 +5879,19 @@ } }, "@aws-cdk/asset-awscli-v1": { - "version": "2.2.184", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.184.tgz", - "integrity": "sha512-03q3Pm/IFEJEA4QS1GH87LwU4YhN1nuvA986k7KtaMIMPTOt/YXpUsriw/Sx2XcTpUk419sPGewr5N0D2slDCg==" + "version": "2.2.202", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", + "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==" }, "@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" }, - "@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.155", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", - "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==" + "@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz", + "integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg==" }, "@aws-crypto/crc32": { "version": "3.0.0", @@ -7677,31 +7721,32 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "aws-cdk": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.84.0.tgz", - "integrity": "sha512-XypGsMW+H6DLLIPt4zG5KWv1FuUlpS6jqEROxg5maJFFsRaQnMkfgXP/ZT1IELyIWCYAZl6xD1rz7WRS3yMueA==", + "version": "2.139.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.139.0.tgz", + "integrity": "sha512-MjMsySQbhR5yTWCphnuVQuS15UdGMV6v4XIM+C8SN7/eUOfv7BFr7QgYMUm5WXCG/f66RnY0zjJbOLRxvcjCrQ==", "dev": true, "requires": { "fsevents": "2.3.2" } }, "aws-cdk-lib": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.84.0.tgz", - "integrity": "sha512-4zLtCLCIs5Ia4WRGqiXRwxSkpGaNy3NxMexO9qYHSuIYpqf4sHObzZ0tDHZCFL5Wkui3sCu3OLQWrRHrr93HvA==", + "version": "2.139.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.139.0.tgz", + "integrity": "sha512-G9yoc+VFwF10kpgf4omtrAVmUNPeAP708oF5fc7XlRTzoTXMmAdUJW9cRGOMtAkFY83SxiJP0wm8n5Z9tjAdUA==", "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.177", - "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@aws-cdk/asset-awscli-v1": "^2.2.202", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.1.1", - "ignore": "^5.2.4", + "fs-extra": "^11.2.0", + "ignore": "^5.3.1", "jsonschema": "^1.4.1", + "mime-types": "^2.1.35", "minimatch": "^3.1.2", - "punycode": "^2.3.0", - "semver": "^7.5.1", - "table": "^6.8.1", + "punycode": "^2.3.1", + "semver": "^7.6.0", + "table": "^6.8.2", "yaml": "1.10.2" }, "dependencies": { @@ -7774,7 +7819,7 @@ "bundled": true }, "fs-extra": { - "version": "11.1.1", + "version": "11.2.0", "bundled": true, "requires": { "graceful-fs": "^4.2.0", @@ -7787,7 +7832,7 @@ "bundled": true }, "ignore": { - "version": "5.2.4", + "version": "5.3.1", "bundled": true }, "is-fullwidth-code-point": { @@ -7821,6 +7866,17 @@ "yallist": "^4.0.0" } }, + "mime-db": { + "version": "1.52.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.35", + "bundled": true, + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "bundled": true, @@ -7829,7 +7885,7 @@ } }, "punycode": { - "version": "2.3.0", + "version": "2.3.1", "bundled": true }, "require-from-string": { @@ -7837,7 +7893,7 @@ "bundled": true }, "semver": { - "version": "7.5.1", + "version": "7.6.0", "bundled": true, "requires": { "lru-cache": "^6.0.0" @@ -7869,7 +7925,7 @@ } }, "table": { - "version": "6.8.1", + "version": "6.8.2", "bundled": true, "requires": { "ajv": "^8.0.1", @@ -7880,7 +7936,7 @@ } }, "universalify": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true }, "uri-js": { @@ -8375,6 +8431,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -10208,6 +10271,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.23.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.4.tgz", + "integrity": "sha512-/AtWOKbBgjzEYYQRNfoGKHObgfAZag6qUJX1VbHo2PRBgS+wfWagEY2mizjfyAPcGesrJOcx/wcl0L9WnVrHFw==" } } } diff --git a/cdk/package.json b/cdk/package.json index 8c8dd663cf..36e573b1fd 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -28,6 +28,7 @@ "form-data": "^4.0.0", "knex": "^2.4.2", "pg": "^8.11.0", - "source-map-support": "^0.5.16" + "source-map-support": "^0.5.16", + "zod": "^3.23.4" } } From ebaddc234b925e101ce445606cddc1cb762a627b Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Mon, 29 Apr 2024 09:48:28 +0300 Subject: [PATCH 2/5] AV-2221: Add evaluation period parameter for rate limits --- cdk/bin/opendata.ts | 6 ++++-- cdk/lib/cloudfront-parameter-stack.ts | 8 ++++++++ cdk/lib/shield-stack-props.ts | 3 ++- cdk/lib/shield-stack.ts | 2 ++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cdk/bin/opendata.ts b/cdk/bin/opendata.ts index e7bc48ac72..f87af40978 100644 --- a/cdk/bin/opendata.ts +++ b/cdk/bin/opendata.ts @@ -193,7 +193,8 @@ const shieldStackBeta = new ShieldStack(app, 'ShieldStack-beta', { rateLimit: cloudfrontParameterStackBeta.rateLimit, managedRulesParameterName: cloudfrontParameterStackBeta.managedRulesParameterName, snsTopicArn: cloudfrontParameterStackBeta.snsTopicArn, - wafAutomationArn: cloudfrontParameterStackBeta.wafAutomationArn + wafAutomationArn: cloudfrontParameterStackBeta.wafAutomationArn, + evaluationPeriod: cloudfrontParameterStackBeta.evaluationPeriod }) const cacheStackBeta = new CacheStack(app, 'CacheStack-beta', { @@ -511,7 +512,8 @@ const shieldStackProd = new ShieldStack(app, 'ShieldStack-prod', { rateLimit: cloudfrontParameterStackProd.rateLimit, managedRulesParameterName: cloudfrontParameterStackProd.managedRulesParameterName, snsTopicArn: cloudfrontParameterStackProd.snsTopicArn, - wafAutomationArn: cloudfrontParameterStackProd.wafAutomationArn + wafAutomationArn: cloudfrontParameterStackProd.wafAutomationArn, + evaluationPeriod: cloudfrontParameterStackProd.evaluationPeriod }) const cacheStackProd = new CacheStack(app, 'CacheStack-prod', { diff --git a/cdk/lib/cloudfront-parameter-stack.ts b/cdk/lib/cloudfront-parameter-stack.ts index a6f676cb46..f146995aa5 100644 --- a/cdk/lib/cloudfront-parameter-stack.ts +++ b/cdk/lib/cloudfront-parameter-stack.ts @@ -13,6 +13,8 @@ export class CloudfrontParameterStack extends Stack { readonly managedRulesParameterName: string; readonly wafAutomationArn: aws_ssm.IStringParameter; readonly snsTopicArn: aws_ssm.IStringParameter; + readonly evaluationPeriod: aws_ssm.IStringParameter; + constructor(scope: Construct, id: string, props: EnvStackProps ) { super(scope, id, props); @@ -73,5 +75,11 @@ export class CloudfrontParameterStack extends Stack { description: 'Arn of sns topic', parameterName: `/${props.environment}/waf/sns_topic_arn`, }) + + this.evaluationPeriod = new aws_ssm.StringParameter(this, 'evaluationPeriod', { + stringValue: '0', + description: 'Evaluation period for rate limits', + parameterName: `/${props.environment}/waf/evaluation_period` + }) } } diff --git a/cdk/lib/shield-stack-props.ts b/cdk/lib/shield-stack-props.ts index 64ae6c1d33..a933970498 100644 --- a/cdk/lib/shield-stack-props.ts +++ b/cdk/lib/shield-stack-props.ts @@ -14,5 +14,6 @@ export interface ShieldStackProps extends EnvStackProps{ rateLimit: aws_ssm.IStringParameter, managedRulesParameterName: string, wafAutomationArn: aws_ssm.IStringParameter, - snsTopicArn: aws_ssm.IStringParameter + snsTopicArn: aws_ssm.IStringParameter, + evaluationPeriod: aws_ssm.IStringParameter } diff --git a/cdk/lib/shield-stack.ts b/cdk/lib/shield-stack.ts index c3a5a89efa..519c727901 100644 --- a/cdk/lib/shield-stack.ts +++ b/cdk/lib/shield-stack.ts @@ -133,6 +133,7 @@ export class ShieldStack extends Stack { rateBasedStatement: { limit: Token.asNumber(props.highPriorityRateLimit.stringValue), aggregateKeyType: "IP", + evaluationWindowSec: Token.asNumber(props.evaluationPeriod.stringValue), scopeDownStatement: { geoMatchStatement: { countryCodes: highPriorityCountryCodesParameter.valueAsList @@ -157,6 +158,7 @@ export class ShieldStack extends Stack { rateBasedStatement: { limit: Token.asNumber(props.rateLimit.stringValue), aggregateKeyType: "IP", + evaluationWindowSec: Token.asNumber(props.evaluationPeriod.stringValue), scopeDownStatement: { notStatement: { statement: { From efd833226148abc85a2afb51bcb33d9e46f64360 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Mon, 29 Apr 2024 09:49:17 +0300 Subject: [PATCH 3/5] cleanup --- cdk/lib/shield-stack.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/cdk/lib/shield-stack.ts b/cdk/lib/shield-stack.ts index 519c727901..bb0387a744 100644 --- a/cdk/lib/shield-stack.ts +++ b/cdk/lib/shield-stack.ts @@ -16,9 +16,6 @@ export class ShieldStack extends Stack { constructor(scope: Construct, id: string, props: ShieldStackProps) { super(scope, id, props); - //const cloudfrontDistributionArn = aws_ssm.StringParameter.fromStringParameterName(this,'cloudfrontDistributionArn', - // `/${props.environment}/waf/cloudfrontDistributionArn`) - const cfnProtection = new aws_shield.CfnProtection(this, 'ShieldProtection', { name: 'Cloudfront distribution', resourceArn: props.cloudfrontDistributionArn.stringValue @@ -56,13 +53,6 @@ export class ShieldStack extends Stack { }); - //const highPriorityRateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'highPriorityRateLimit', - // `/${props.environment}/waf/high_priority_rate_limit`); - - //const rateLimit = aws_ssm.StringParameter.fromStringParameterName(this, 'rateLimit', - // `/${props.environment}/waf/rate_limit`); - - let rules = [ { name: "block-banned_ips", @@ -305,15 +295,8 @@ export class ShieldStack extends Stack { rules: rules }) - //const WafAutomationArn = aws_ssm.StringParameter.fromStringParameterName(this, 'WafAutomationArn', - // `/${props.environment}/waf/waf_automation_arn`); - const WafAutomationLambdaFunction = aws_lambda.Function.fromFunctionArn(this, "WafAutomation", props.wafAutomationArn.stringValue) - - //const SNSTopicArn = aws_ssm.StringParameter.fromStringParameterName(this, 'SNSTopicArn', - // `/${props.environment}/waf/sns_topic_arn`); - - + const topic = aws_sns.Topic.fromTopicArn(this, "SNSTopic", props.snsTopicArn.stringValue) topic.addSubscription(new aws_sns_subscriptions.LambdaSubscription(WafAutomationLambdaFunction)) From b6b6b1306ac76143fe86294ba4cae3956d28dc91 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Tue, 30 Apr 2024 15:15:03 +0300 Subject: [PATCH 4/5] Remove debug logging --- cdk/lib/shield-stack.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/cdk/lib/shield-stack.ts b/cdk/lib/shield-stack.ts index bb0387a744..7023004928 100644 --- a/cdk/lib/shield-stack.ts +++ b/cdk/lib/shield-stack.ts @@ -191,9 +191,7 @@ export class ShieldStack extends Stack { if ( managedRules !== "dummy"){ let ruleList: any[] = [] - console.log(managedRules) const validatedRules = RuleGroupSchema.parse(managedRules) - console.log(validatedRules) validatedRules.forEach((rule, index: number) => { let ruleActionOverrides = [] From 197aa71ffc349abc995148c8d28c9af5deb0a25d Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Tue, 30 Apr 2024 15:42:33 +0300 Subject: [PATCH 5/5] add valid placeholders --- cdk/lib/cloudfront-parameter-stack.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cdk/lib/cloudfront-parameter-stack.ts b/cdk/lib/cloudfront-parameter-stack.ts index f146995aa5..1fd8364863 100644 --- a/cdk/lib/cloudfront-parameter-stack.ts +++ b/cdk/lib/cloudfront-parameter-stack.ts @@ -19,7 +19,7 @@ export class CloudfrontParameterStack extends Stack { super(scope, id, props); this.cloudFrontDistributionArn = new aws_ssm.StringParameter(this, 'cloudfrontDistributionArn', { - stringValue: '', + stringValue: 'some placeholder', description: 'Arn of cloudfront distribution', parameterName: `/${props.environment}/waf/cloudfrontDistributionArn`, }) @@ -59,19 +59,19 @@ export class CloudfrontParameterStack extends Stack { this.managedRulesParameterName = `/${props.environment}/waf/managed_rules` new aws_ssm.StringParameter(this, 'managedRules', { - stringValue: '', + stringValue: 'some placeholder', description: 'JSON value for managed rules', parameterName: this.managedRulesParameterName }) this.wafAutomationArn = new aws_ssm.StringParameter(this, 'wafAutomationArn', { - stringValue: '', + stringValue: 'some placeholder', description: 'Arn of waf automation lambda', parameterName: `/${props.environment}/waf/waf_automation_arn`, }) this.snsTopicArn = new aws_ssm.StringParameter(this, 'snsTopicArn', { - stringValue: '', + stringValue: 'some placeholder', description: 'Arn of sns topic', parameterName: `/${props.environment}/waf/sns_topic_arn`, })