Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.2.2 #287

Merged
merged 25 commits into from
Jan 26, 2024
Merged

4.2.2 #287

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Released

## 4.2.2
### Added
- Guidance Helper v1: This Helper is designed to provide comprehensive assistance in implementing Best Practices for AWS Firewalls. Additionally, it addresses [Issue279](https://github.com/globaldatanet/aws-firewall-factory/issues/279), ensuring a more robust and effective implementation. Guidances have severities: ℹ️ - can be adapted, ⚠️ should be adapted, 🚨 must be adapted - exceptions of course confirm the rules.
### Fixed
- The conversion of rules from CDK to SDK for RateBasedStatement was experiencing issues, impacting the proper functioning essential for WCU Calculation. I'm pleased to inform you that this issue has been successfully addressed and resolved.

## 4.2.1
### Fixed
- [Issue285](https://github.com/globaldatanet/aws-firewall-factory/issues/285) - Resolved an issue where the redeployment of changed capacity was not functioning correctly due to inconsistencies in the writing of ProcessProperties for DeployedRuleGroups.
Expand Down
5 changes: 3 additions & 2 deletions bin/aws-firewall-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { WafStack } from "../lib/_web-application-firewall-stack";
import { PrerequisitesStack } from "../lib/_prerequisites-stack";
import * as cdk from "aws-cdk-lib";
import { Config, Prerequisites, PriceRegions, RegionString } from "../lib/types/config";
import { wafHelper, afwfHelper, pricingHelper, cloudformationHelper } from "../lib/tools/helpers";
import { wafHelper, afwfHelper, pricingHelper, cloudformationHelper, guidanceHelper } from "../lib/tools/helpers";
import * as values from "../values";

/**
Expand Down Expand Up @@ -58,7 +58,7 @@ void (async () => {
await cloudformationHelper.setOutputsFromStack(deploymentRegion, runtimeProperties, config);
if(config.General.DeployHash){
console.log("#️⃣ Deployment Hash for this WAF: "+ config.General.DeployHash);
console.log(" ⚠️ Legacy functionality ⌛️\n\n");
guidanceHelper.getGuidance("deploymentHash", runtimeProperties);
}

console.log(`🔥 Deploy FMS Policy: ${config.General.Prefix.toUpperCase()}-WAF-${config.WebAcl.Name.toUpperCase()}-${config.General.Stage.toUpperCase()}${config.General.DeployHash ? "-"+config.General.DeployHash.toUpperCase() : ""}\n ⦂ Type:
Expand Down Expand Up @@ -116,5 +116,6 @@ void (async () => {
});

await pricingHelper.isWafPriceCalculated(PriceRegions[deploymentRegion as RegionString], runtimeProperties, config,deploymentRegion);
await guidanceHelper.outputGuidance(config, runtimeProperties);
}
})();
4 changes: 2 additions & 2 deletions lib/_web-application-firewall-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,15 @@ export class WafStack extends cdk.Stack {
const MANAGEDRULEGROUPSINFO: string[]= [""];
let subVariables : SubVariables = {};
if (props.config.WebAcl.PreProcess.ManagedRuleGroups) {
const preProcessmanagedRgs = wafHelper.buildServiceDataManagedRgs(this, props.config.WebAcl.PreProcess.ManagedRuleGroups, managedRuleGroupVersionProvider, props.config.WebAcl.Scope);
const preProcessmanagedRgs = wafHelper.buildServiceDataManagedRgs(this, props.config.WebAcl.PreProcess.ManagedRuleGroups, managedRuleGroupVersionProvider, props.config.WebAcl.Scope, props.runtimeProperties);
preProcessRuleGroups.push(...preProcessmanagedRgs.ServiceData);
MANAGEDRULEGROUPSINFO.push(...preProcessmanagedRgs.ManagedRuleGroupInfo);
subVariables = {...preProcessmanagedRgs.SubVariables};
} else {
console.log("\nℹ️ No ManagedRuleGroups defined in PreProcess.");
}
if (props.config.WebAcl.PostProcess.ManagedRuleGroups) {
const postProcessmanagedRgs = wafHelper.buildServiceDataManagedRgs(this, props.config.WebAcl.PostProcess.ManagedRuleGroups, managedRuleGroupVersionProvider, props.config.WebAcl.Scope);
const postProcessmanagedRgs = wafHelper.buildServiceDataManagedRgs(this, props.config.WebAcl.PostProcess.ManagedRuleGroups, managedRuleGroupVersionProvider, props.config.WebAcl.Scope, props.runtimeProperties);
postProcessRuleGroups.push(...postProcessmanagedRgs.ServiceData);
MANAGEDRULEGROUPSINFO.push(...postProcessmanagedRgs.ManagedRuleGroupInfo);
subVariables = {...postProcessmanagedRgs.SubVariables};
Expand Down
14 changes: 14 additions & 0 deletions lib/tools/helpers/aws-firewall-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ export const outputInfoBanner = (config?:Config) => {
*/
export function initRuntimeProperties() : RuntimeProperties {
return {
GuidanceSummary: [],
Guidance: {
rateBasedStatementCount: 0,
nestedRateStatementCount: 0,
nestedRateStatementInfo: [],
overrideActionManagedRuleGroupCount: 0,
overrideActionManagedRuleGroupInfo: [],
byteMatchStatementPositionalConstraintCount: 0,
byteMatchStatementPositionalConstraintInfo: [],
noRuleLabelsCount: 0,
noRuleLabelsInfo: []
},
ManagedRuleCapacity: 0,
PostProcess: {
Capacity: 0,
Expand All @@ -62,6 +74,7 @@ export function initRuntimeProperties() : RuntimeProperties {
ManagedRuleBotControlCount: 0,
ManagedRuleATPCount: 0,
CustomRuleCount: 0,
IpReputationListCount: 0,
CustomRuleGroupCount: 0,
CustomCaptchaRuleCount: 0
},
Expand All @@ -74,6 +87,7 @@ export function initRuntimeProperties() : RuntimeProperties {
ManagedRuleGroupCount: 0,
ManagedRuleBotControlCount: 0,
ManagedRuleATPCount: 0,
IpReputationListCount: 0,
CustomRuleCount: 0,
CustomRuleGroupCount: 0,
CustomCaptchaRuleCount: 0
Expand Down
88 changes: 88 additions & 0 deletions lib/tools/helpers/guidance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { RuntimeProperties } from "../../types/runtimeprops";
import { Config } from "../../types/config";

/**
This function will help you to get guidance on implementing Best Practices for AWS Firewalls.
@param context - The context of the guidance. For example, nestedRateStatement.
@param source - The source of the guidance. For example, ManagedRuleGroup.
*/
export function getGuidance(context: string, runtimeProperties: RuntimeProperties, source?: string) {
switch(context){
case "nestedRateStatement":
runtimeProperties.Guidance.nestedRateStatementCount++;
source ? runtimeProperties.Guidance.nestedRateStatementInfo.push(source) : undefined;
break;
case "overrideActionManagedRuleGroup":
runtimeProperties.Guidance.overrideActionManagedRuleGroupCount++;
source ? runtimeProperties.Guidance.overrideActionManagedRuleGroupInfo.push(source) : undefined;
break;
case "noManageRuleGroups":
runtimeProperties.GuidanceSummary.push("\x1b[31m","\n 🚨 No ManagedRuleGroups are used in your Firewall.\n https://docs.aws.amazon.com/waf/latest/developerguide/waf-managed-rule-groups.html.","\x1b[0m");
break;
case "deploymentHash":
runtimeProperties.GuidanceSummary.push("\x1b[33m"," ⚠️ Legacy functionality ⌛️ \n This functionality will be removed soon. \n","\x1b[0m");
break;
case "byteMatchStatementPositionalConstraint":
runtimeProperties.Guidance.byteMatchStatementPositionalConstraintCount++;
source ? runtimeProperties.Guidance.byteMatchStatementPositionalConstraintInfo.push(source) : undefined;
break;
case "noBotControlRuleSetProperty":
runtimeProperties.GuidanceSummary.push("\x1b[33m","\n ⚠️ No BotControlRuleSetProperty is used in your ManagedRulesBotControlRuleSet.\n https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafv2-webacl-awsmanagedrulesbotcontrolruleset.html.","\x1b[0m");
break;
case "noRuleLabels":
runtimeProperties.Guidance.noRuleLabelsCount++;
source ? runtimeProperties.Guidance.noRuleLabelsInfo.push(source) : undefined;
break;
case "noAWSManagedIPDDoSList":
runtimeProperties.GuidanceSummary.push("\x1b[33m","\n ⚠️ No AWSManagedRulesAmazonIpReputationList is used in your Firewall - These Rules identify and block IPs acting as bots, conducting reconnaissance on AWS resources, or involved in DDoS activities. AWSManagedIPDDoSList rule has effectively blocked over 90% of malicious request floods.","\x1b[0m");
break;
default:
break;
}
}


/**
This function will print out the collected guidance for your Firewall.
@param runtimeProperties - The runtimeProperties object.
*/
export function outputGuidance(config: Config, runtimeProperties: RuntimeProperties) {
if(runtimeProperties.GuidanceSummary.length !== 0 || runtimeProperties.Guidance.nestedRateStatementCount !== 0 || runtimeProperties.Guidance.overrideActionManagedRuleGroupCount !== 0 || runtimeProperties.Guidance.noRuleLabelsCount !== 0 || runtimeProperties.Guidance.byteMatchStatementPositionalConstraintCount !== 0){
console.log("\x1b[0m","\n🛟 Guidance:","\x1b[0m");
runtimeProperties.GuidanceSummary.forEach(element => {
console.log(element);
});
}
if(runtimeProperties.Guidance.nestedRateStatementCount !== 0){
console.log("\x1b[31m",`\n 🚨 Found ${runtimeProperties.Guidance.nestedRateStatementCount} Nested RateBasedStatement - You cannot nest a RateBasedStatement inside another statement, for example inside a NotStatement or OrStatement.\n You can define a RateBasedStatement inside a web ACL and inside a rule group.`,"\x1b[0m");
console.log("\x1b[1m"," Affected Statements:\n","\x1b[0m");
runtimeProperties.Guidance.nestedRateStatementInfo.forEach(element => {
console.log(" − "+element);
});
}
if(runtimeProperties.Guidance.overrideActionManagedRuleGroupCount !== 0){
console.log("\x1b[31m",`\n 🚨 Found OverrideAction in ManagedRuleGroup - OverrideAction of ${runtimeProperties.Guidance.overrideActionManagedRuleGroupCount} ManagedRuleGroup is set to COUNT, which simply tallies all rules within the group.\n However, this practice may create a vulnerability in your firewall and is not recommended.`,"\x1b[0m");
console.log("\x1b[1m"," Affected ManagedRuleGroups:\n","\x1b[0m");
runtimeProperties.Guidance.overrideActionManagedRuleGroupInfo.forEach(element => {
console.log(" − "+element);
});
}
if(runtimeProperties.Guidance.noRuleLabelsCount !== 0){
console.log("\x1b[0m",`\n ℹ️ Found ${runtimeProperties.Guidance.noRuleLabelsCount} CustomRules without RuleLabels - Rule Labels help you to mitigate False/Positives.`,"\x1b[0m");
console.log("\x1b[1m"," Affected CustomRules:\n","\x1b[0m");
runtimeProperties.Guidance.noRuleLabelsInfo.forEach(element => {
console.log(" − "+element);
});
}
if(runtimeProperties.Guidance.byteMatchStatementPositionalConstraintCount !== 0){
console.log("\x1b[0m",`\n ℹ️ Found ${runtimeProperties.Guidance.byteMatchStatementPositionalConstraintCount} ByteMatchStatements with PositionalConstraint - It is cheaper from WCU perspektive to use a RegexMatchStatement in this Case.`,"\x1b[0m");
console.log("\x1b[1m"," Affected CONTSTRAINT Information:\n","\x1b[0m");
runtimeProperties.Guidance.byteMatchStatementPositionalConstraintInfo.forEach(element => {
console.log(" − "+element);
});
}
if(runtimeProperties.Guidance.rateBasedStatementCount === 0 && config.WebAcl.Type === "AWS::ElasticLoadBalancingV2::LoadBalancer"){
console.log("\x1b[0m","\n ℹ️ You are securing a LoadBalancer with your Firewall with usage of a RateBasedStatement.\n RateBasedStatements empower you to automatically block requests originating from problematic source IPs until their request rate diminishes below a predetermined threshold.","\x1b[0m");
}
console.log("\n\n");
}
3 changes: 2 additions & 1 deletion lib/tools/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
export * as cloudformationHelper from "./cloudformation";
export * as wafHelper from "./web-application-firewall";
export * as afwfHelper from "./aws-firewall-factory";
export * as pricingHelper from "./pricing";
export * as pricingHelper from "./pricing";
export * as guidanceHelper from "./guidance";
28 changes: 18 additions & 10 deletions lib/tools/helpers/web-application-firewall/quotas-and-capacity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Scope, WAFV2Client, CheckCapacityCommand, CheckCapacityCommandInput, De
import { FMSClient, ListPoliciesCommand, ListPoliciesCommandInput } from "@aws-sdk/client-fms";
import { RuntimeProperties, ProcessProperties } from "../../../types/runtimeprops";
import { Config } from "../../../types/config";
import { cloudformationHelper } from "../../helpers";
import { cloudformationHelper, guidanceHelper } from "../../helpers";
import * as lodash from "lodash";
import {transformCdkRuletoSdkRule} from "../../transformer";
import { Rule as FmsRule, ManagedRuleGroup } from "../../../types/fms";
Expand Down Expand Up @@ -161,7 +161,7 @@ async function calculateCapacities(
console.log(" 🥇 PreProcess: ");
runtimeProperties.PreProcess.CustomRuleCount = config.WebAcl.PreProcess.CustomRules.length;
runtimeProperties.PreProcess.CustomCaptchaRuleCount = config.WebAcl.PreProcess.CustomRules.filter(rule => rule.action.captcha).length;
runtimeProperties.PreProcess.Capacity = (await calculateCustomRulesCapacities(config.WebAcl.PreProcess.CustomRules, deploymentRegion, config.WebAcl.Scope)).reduce((a,b) => a+b, 0);
runtimeProperties.PreProcess.Capacity = (await calculateCustomRulesCapacities(config.WebAcl.PreProcess.CustomRules, deploymentRegion, config.WebAcl.Scope, runtimeProperties)).reduce((a,b) => a+b, 0);
}
if (!config.WebAcl.PostProcess.CustomRules) {
console.log(
Expand All @@ -171,7 +171,7 @@ async function calculateCapacities(
console.log("\n 🥈 PostProcess: ");
runtimeProperties.PostProcess.CustomRuleCount = config.WebAcl.PostProcess.CustomRules.length;
runtimeProperties.PostProcess.CustomCaptchaRuleCount = config.WebAcl.PostProcess.CustomRules.filter(rule => rule.action.captcha).length;
runtimeProperties.PostProcess.Capacity = (await calculateCustomRulesCapacities(config.WebAcl.PostProcess.CustomRules, deploymentRegion, config.WebAcl.Scope)).reduce((a,b) => a+b, 0);
runtimeProperties.PostProcess.Capacity = (await calculateCustomRulesCapacities(config.WebAcl.PostProcess.CustomRules, deploymentRegion, config.WebAcl.Scope, runtimeProperties)).reduce((a,b) => a+b, 0);
}
console.log("\n👀 Get ManagedRule Capacity:\n");
if (!config.WebAcl.PreProcess.ManagedRuleGroups || config.WebAcl.PreProcess.ManagedRuleGroups?.length === 0) {
Expand Down Expand Up @@ -208,6 +208,7 @@ async function calculateManagedRuleGroupCapacities(type: "Pre" | "Post",deployme
processProperties = runtimeProperties.PostProcess;
break;
}
config.WebAcl.PreProcess.ManagedRuleGroups !== undefined && config.WebAcl.PostProcess.ManagedRuleGroups !== undefined ? guidanceHelper.getGuidance("noManageRuleGroups", runtimeProperties) : null;
const managedcapacitieslog = [];
managedcapacitieslog.push(["➕ RuleName", "Capacity", "🏷 Specified Version", "🔄 EnforceUpdate"]);
for (const managedrule of managedrules) {
Expand Down Expand Up @@ -239,6 +240,10 @@ async function calculateManagedRuleGroupCapacities(type: "Pre" | "Post",deployme
processProperties.ManagedRuleATPCount += 1;
break;
}
case "AWSManagedRulesAmazonIpReputationList": {
processProperties.IpReputationListCount += 1;
break;
}
}
}
console.log(table(managedcapacitieslog));
Expand Down Expand Up @@ -283,7 +288,7 @@ function filterStatements(statement: wafv2.CfnWebACL.StatementProperty){
* @param scope the scope of the WebACL, e.g. REGIONAL or CLOUDFRONT
* @returns an array with the capacities of the supplied custom rules
*/
async function calculateCustomRulesCapacities(customRules: FmsRule[], deploymentRegion: string, scope: "REGIONAL" | "CLOUDFRONT") {
async function calculateCustomRulesCapacities(customRules: FmsRule[], deploymentRegion: string, scope: "REGIONAL" | "CLOUDFRONT", runtimeProperties: RuntimeProperties) {
const capacities = [];
const capacitieslog = [];
capacitieslog.push(["🔺 Priority", "➕ RuleName", "Capacity"]);
Expand Down Expand Up @@ -318,7 +323,7 @@ async function calculateCustomRulesCapacities(customRules: FmsRule[], deployment
capacities.push(regexPatternSetsStatementsCapacity(notregexPatternSetReferenceStatement));
}
else{
capacities.push(await calculateCustomRuleStatementsCapacity(customRule, deploymentRegion, scope));
capacities.push(await calculateCustomRuleStatementsCapacity(customRule, deploymentRegion, scope, runtimeProperties));
}
}
else if(andStatement && andStatement.statements) {
Expand Down Expand Up @@ -373,7 +378,7 @@ async function calculateCustomRulesCapacities(customRules: FmsRule[], deployment
filterStatements(statement))};
if (filteredAndStatements && filteredAndStatements.statements && filteredAndStatements.statements.length > 0) {
const calcRule = buildCustomRuleWithoutReferenceStatements(customRule, filteredAndStatements, false);
const capacity = await calculateCustomRuleStatementsCapacity(calcRule, deploymentRegion, scope);
const capacity = await calculateCustomRuleStatementsCapacity(calcRule, deploymentRegion, scope, runtimeProperties);
capacities.push(capacity);
}
}
Expand Down Expand Up @@ -406,12 +411,12 @@ async function calculateCustomRulesCapacities(customRules: FmsRule[], deployment
};
if (filteredOrStatements && filteredOrStatements.statements && filteredOrStatements.statements.length > 0) {
const calcRule = buildCustomRuleWithoutReferenceStatements(customRule, filteredOrStatements, true);
const capacity = await calculateCustomRuleStatementsCapacity(calcRule, deploymentRegion, scope);
const capacity = await calculateCustomRuleStatementsCapacity(calcRule, deploymentRegion, scope, runtimeProperties);
capacities.push(capacity);
}
}
else {
capacities.push(await calculateCustomRuleStatementsCapacity(customRule, deploymentRegion, scope));
capacities.push(await calculateCustomRuleStatementsCapacity(customRule, deploymentRegion, scope, runtimeProperties));
}
capacitieslog.push([customRule.priority, customRule.name,capacities[capacities.length-1]]);
}
Expand Down Expand Up @@ -439,9 +444,9 @@ function calculateIpsSetStatementCapacity(ipSetReferenceStatement: wafv2.CfnWebA
* @param scope "REGIONAL" | "CLOUDFRONT"
* @returns
*/
async function calculateCustomRuleStatementsCapacity(customRule: FmsRule, deploymentRegion: string, scope: "REGIONAL" | "CLOUDFRONT") {
async function calculateCustomRuleStatementsCapacity(customRule: FmsRule, deploymentRegion: string, scope: "REGIONAL" | "CLOUDFRONT", runtimeProperties: RuntimeProperties) {
const ruleCalculatedCapacityJson = [];
const rule = transformCdkRuletoSdkRule(customRule);
const rule = transformCdkRuletoSdkRule(customRule, runtimeProperties);
ruleCalculatedCapacityJson.push(rule);
const capacity = await getTotalCapacityOfRules(
deploymentRegion,
Expand Down Expand Up @@ -553,5 +558,8 @@ export async function isWcuQuotaReached(deploymentRegion: string, runtimeProps:
console.log(" 💡 Account WAF-WCU Quota: " +Number(quoteWcu).toString());
console.log(" 🧮 Calculated Custom Rule Capacity is: [" + customCapacity + "] (🥇[" + runtimeProps.PreProcess.Capacity + "] + 🥈[" + runtimeProps.PostProcess.Capacity + "]) \n ➕ ManagedRulesCapacity: ["+ runtimeProps.ManagedRuleCapacity +"] \n = Total Waf Capacity: " + totalWcu.toString() + "\n");
}
if(runtimeProps.PostProcess.IpReputationListCount === 0 && runtimeProps.PreProcess.IpReputationListCount === 0){
guidanceHelper.getGuidance("noIpReputationList", runtimeProps);
}
return wcuLimitReached;
}
Loading
Loading