Skip to content

Commit

Permalink
REKDAT-17: Add AWS Shield Advanced to load balancers
Browse files Browse the repository at this point in the history
  • Loading branch information
Zharktas committed Nov 30, 2023
1 parent 3924c49 commit 3b16c3a
Show file tree
Hide file tree
Showing 10 changed files with 698 additions and 308 deletions.
2 changes: 2 additions & 0 deletions cdk/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ node_modules
# CDK asset staging directory
.cdk.staging
cdk.out

cdk.context.json
32 changes: 32 additions & 0 deletions cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {NginxStack} from "../lib/nginx-stack";
import {SolrStack} from "../lib/solr-stack";
import {FileSystemStack} from "../lib/filesystem-stack";
import {CkanStack} from "../lib/ckan-stack";
import {ShieldStack} from "../lib/shield-stack";

const app = new cdk.App();

Expand Down Expand Up @@ -253,6 +254,21 @@ const CkanStackDev = new CkanStack(app, 'CkanStack-dev', {

})

const ShieldStackDev = new ShieldStack(app, 'ShieldStack-dev', {
env: {
account: devStackProps.account,
region: devStackProps.region,
},
envProps: envProps,
environment: devStackProps.environment,
loadBalancer: LoadBalancerStackDev.loadBalancer,
rateLimitRequestSamplingEnabled: false,
highPriorityRequestSamplingEnabled: false,
bannedIpsRequestSamplingEnabled: false,
requestSampleAllTrafficEnabled: false,
vpc: VpcStackDev.vpc
})


// Production

Expand Down Expand Up @@ -427,3 +443,19 @@ const CkanStackProd = new CkanStack(app, 'CkanStack-prod', {
},
vpc: VpcStackProd.vpc
})


const ShieldStackProd = new ShieldStack(app, 'ShieldStack-prod', {
env: {
account: prodStackProps.account,
region: prodStackProps.region,
},
envProps: envProps,
environment: prodStackProps.environment,
loadBalancer: LoadBalancerStackProd.loadBalancer,
rateLimitRequestSamplingEnabled: false,
highPriorityRequestSamplingEnabled: false,
bannedIpsRequestSamplingEnabled: false,
requestSampleAllTrafficEnabled: false,
vpc: VpcStackProd.vpc
})
12 changes: 0 additions & 12 deletions cdk/cdk.context.json

This file was deleted.

2 changes: 1 addition & 1 deletion cdk/lib/lambda-stack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import {CreateDatabasesAndUsers} from "./create-databases-and-users";
import {CreateDatabasesAndUsers} from "./lambdas/create-databases-and-users";
import {Stack} from "aws-cdk-lib";
import {Construct} from "constructs";
import {LambdaStackProps} from "./lambda-stack-props";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {aws_kms, aws_rds, StackProps} from "aws-cdk-lib";
import {CommonStackProps} from "./common-stack-props";
import {CommonStackProps} from "../common-stack-props";

export interface CreateDatabasesAndUsersProps extends CommonStackProps{
ckanInstance: aws_rds.IDatabaseInstance,
Expand Down
File renamed without changes.
10 changes: 10 additions & 0 deletions cdk/lib/shield-stack-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {aws_elasticloadbalancingv2} from "aws-cdk-lib";
import {CommonStackProps} from "./common-stack-props";

export interface ShieldStackProps extends CommonStackProps{
loadBalancer: aws_elasticloadbalancingv2.IApplicationLoadBalancer,
bannedIpsRequestSamplingEnabled: boolean,
requestSampleAllTrafficEnabled: boolean,
highPriorityRequestSamplingEnabled: boolean,
rateLimitRequestSamplingEnabled: boolean
}
263 changes: 263 additions & 0 deletions cdk/lib/shield-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import {
aws_cloudwatch,
aws_lambda,
aws_lambda_event_sources,
aws_shield,
aws_sns,
aws_ssm,
aws_wafv2,
Stack
} from "aws-cdk-lib";
import {Construct} from "constructs";

import {ShieldStackProps} from "./shield-stack-props";
import any = jasmine.any;


export class ShieldStack extends Stack {
constructor(scope: Construct, id: string, props: ShieldStackProps) {
super(scope, id, props);

const cfnProtection = new aws_shield.CfnProtection(this, 'ShieldProtection', {
name: 'Application Load Balancers',
resourceArn: props.loadBalancer.loadBalancerArn
})

const banned_ips = aws_ssm.StringListParameter.fromStringListParameterName(this, 'bannedIpsList',
`/${props.environment}/waf/banned_ips`);

const whitelisted_ips = aws_ssm.StringListParameter.fromStringListParameterName(this, 'whitelistedIpsList',
`/${props.environment}/waf/whitelisted_ips`);


const cfnBannedIPSet = new aws_wafv2.CfnIPSet(this, 'BannedIPSet', {
name: 'banned-ips',
scope: 'regional',
ipAddressVersion: "IPV4",
addresses: banned_ips.stringListValue
})

const cfnWhiteListedIpSet = new aws_wafv2.CfnIPSet(this, 'WhitelistedIPSet', {
name: 'whitelisted-ips',
scope: 'regional',
ipAddressVersion: "IPV4",
addresses: whitelisted_ips.stringListValue
})


const highPriorityCountryCodes = aws_ssm.StringListParameter.fromStringListParameterName(this, 'highPriorityCountryCodes',
`/${props.environment}/waf/high_priority_country_codes`);

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 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",
priority: 0,
action: {
block: {
}
},
statement: {
ipSetReferenceStatement: {
arn: cfnBannedIPSet.attrArn
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "banned-ips",
sampledRequestsEnabled: props.bannedIpsRequestSamplingEnabled
},
},
{
name: "allow-whitelisted_ips",
priority: 1,
action: {
allow: {
}
},
statement: {
ipSetReferenceStatement: {
arn: cfnWhiteListedIpSet.attrArn
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "whitelisted-ips",
sampledRequestsEnabled: false
},
},
{
name: "WAFAutomationProtectionRule",
priority: 2,
action: {
count: {
}
},
statement: {
notStatement: {
statement: {
geoMatchStatement: {
countryCodes: highPriorityCountryCodes.stringListValue
}
}
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "waf-automation-geoblocked-requests",
sampledRequestsEnabled: true
},
},
{
name: "rate-limit-finland",
priority: 3,
action: {
block: {
}
},
statement: {
rateBasedStatement: {
limit: parseInt(highPriorityRateLimit.stringValue, 10),
aggregateKeyType: "IP",
scopeDownStatement: {
geoMatchStatement: {
countryCodes: highPriorityCountryCodes.stringListValue
}
}
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "request-rate-limit-finland",
sampledRequestsEnabled: props.highPriorityRequestSamplingEnabled
},
},
{
name: "rate-limit-world",
priority: 4,
action: {
block: {
}
},
statement: {
rateBasedStatement: {
limit: parseInt(rateLimit.stringValue, 10),
aggregateKeyType: "IP",
scopeDownStatement: {
notStatement: {
statement: {
geoMatchStatement: {
countryCodes: highPriorityCountryCodes.stringListValue
}
}
}
}
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "request-rate-limit-world",
sampledRequestsEnabled: props.rateLimitRequestSamplingEnabled
},
},
]

type RuleGroup = {
groupName: string,
vendorName: string,
excludedRules: string[],
enableRequestSampling: boolean
}


if ( managedRules !== "dummy"){
let ruleList: any[] = []

managedRules.forEach((rule: RuleGroup, index: number) => {

let ruleActionOverrides = []

for (let excludedRule in rule.excludedRules) {
let excludedRuleObj = {
actionToUse: {
count: {}
},
name: excludedRule
}

ruleActionOverrides.push(excludedRuleObj)
}

let managedRuleGroup: aws_wafv2.CfnWebACL.RuleProperty = {
name: "managed-rule-group-" + rule.groupName,
priority: 5 + index,
overrideAction: {
none: {}
},
statement: {
managedRuleGroupStatement: {
name: rule.groupName,
vendorName: rule.vendorName,
ruleActionOverrides: ruleActionOverrides
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "request-managed-rule-group-" + rule.groupName,
sampledRequestsEnabled: rule.enableRequestSampling
}
}


ruleList.push(managedRuleGroup)

})

rules = rules.concat(ruleList)
}


const cfnWebAcl = new aws_wafv2.CfnWebACL(this, 'WAFWebACL', {
scope: "regional",
defaultAction: {
allow: {}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "DVVWAF",
sampledRequestsEnabled: props.requestSampleAllTrafficEnabled
},
rules: rules
})


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 CloudWatchAlarmArn = aws_ssm.StringParameter.fromStringParameterName(this, 'CloudWatchAlarmArn',
`/${props.environment}/waf/cloudwatch_alarm_arn`);

const DDoSDetectedAlarm = aws_cloudwatch.Alarm.fromAlarmArn(this, "DDosDetectedAlarm", CloudWatchAlarmArn.stringValue)

const topic = new aws_sns.Topic(this, 'DDoSProtectionTopic', {
displayName: 'DDoS protection',
});

const eventSource = new aws_lambda_event_sources.SnsEventSource(topic);

WafAutomationLambdaFunction.addEventSource(eventSource)

}
}
Loading

0 comments on commit 3b16c3a

Please sign in to comment.