Skip to content

Commit 53d82f0

Browse files
authored
Merge pull request #8 from ZDF-OSS/feature/add_dashboards
feat: adding cloud watch dashboard and bumb deps
2 parents be57367 + a4de90c commit 53d82f0

File tree

10 files changed

+1146
-1003
lines changed

10 files changed

+1146
-1003
lines changed

.github/workflows/upgrade-main.yml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projen/tasks.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
88
author: 'ZeroDotFive',
99
authorAddress: 'ayoub.umoru@zerodotfive.com',
1010
cdkVersion: '2.84.0',
11+
majorVersion: 1,
1112
defaultReleaseBranch: 'main',
1213
authorOrganization: true,
1314
jsiiVersion: '~5.0.0',

API.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ Third-party Language Deprecation: language version is only supported until its E
2525

2626
* AWS Managed Rules for AWS WAF is a managed service that provides protection against common application vulnerabilities or other unwanted traffic (https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html)
2727

28+
* Cloud Watch Dashboards with AWS Logs Insights
29+
30+
31+
32+
![dashboard](/assets/dashboard.png)
33+
2834

2935
***AWS Managed Rules***
3036
AWS Managed Rules for AWS WAF is a managed service that provides protection against common application vulnerabilities or other unwanted traffic. You have the option of selecting one or more rule groups from AWS Managed Rules for each web ACL, up to the maximum web ACL capacity unit (WCU) limit.
@@ -71,6 +77,7 @@ When you use a geo match statement just for the region and country labels that i
7177

7278
priority: 100,
7379
resourceArn: lb.loadBalancerArn,
80+
// Switching on CloudWatch Logs also provisions a CloudWatch Dashboard
7481
enableCloudWatchLogs: true,
7582
});
7683
```

assets/dashboard.png

285 KB
Loading

package.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/dashboard.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import { LogQueryVisualizationType, LogQueryWidget } from 'aws-cdk-lib/aws-cloudwatch';
3+
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
4+
import * as logs from 'aws-cdk-lib/aws-logs';
5+
import { Construct } from 'constructs';
6+
7+
export interface IDashboardProps {
8+
cloudwatchLogName: string;
9+
}
10+
11+
export class CloudWatchWAFDashboard extends Construct {
12+
constructor(scope: Construct, id: string, props: IDashboardProps) {
13+
super(scope, id);
14+
15+
new logs.QueryDefinition(this, 'waf-qd-allowed-requests', {
16+
queryDefinitionName: 'Allowed requests by country',
17+
logGroups: [
18+
logs.LogGroup.fromLogGroupName(
19+
this,
20+
'log-group-blocked',
21+
props.cloudwatchLogName,
22+
),
23+
],
24+
queryString: new logs.QueryString({
25+
stats: 'count(*) as countries by httpRequest.country',
26+
filterStatements: ['action = "ALLOW"'],
27+
}),
28+
});
29+
new logs.QueryDefinition(this, 'waf-qd-blocked-requests', {
30+
queryDefinitionName: 'Blocked requests by country',
31+
logGroups: [
32+
logs.LogGroup.fromLogGroupName(
33+
this,
34+
'log-group-allowed',
35+
props.cloudwatchLogName,
36+
),
37+
],
38+
queryString: new logs.QueryString({
39+
stats: 'count(*) as countries by httpRequest.country',
40+
filterStatements: ['action = "BLOCKED"'],
41+
}),
42+
});
43+
44+
const blockedWidget = new LogQueryWidget({
45+
title: 'Blocked requests by Country',
46+
height: 12,
47+
width: 12,
48+
logGroupNames: [props.cloudwatchLogName],
49+
view: LogQueryVisualizationType.PIE,
50+
queryLines: [
51+
'filter action = "BLOCK"',
52+
'stats count(*) as countries by httpRequest.country',
53+
],
54+
});
55+
const allowedWidget = new LogQueryWidget({
56+
title: 'Allowed requests by Country',
57+
height: 12,
58+
width: 12,
59+
logGroupNames: [props.cloudwatchLogName],
60+
view: LogQueryVisualizationType.PIE,
61+
queryLines: [
62+
'filter action = "ALLOW"',
63+
'stats count(*) as countries by httpRequest.country',
64+
],
65+
});
66+
const blockedCount = new LogQueryWidget({
67+
title: 'Rules blocking requests',
68+
height: 8,
69+
width: 12,
70+
logGroupNames: [props.cloudwatchLogName],
71+
view: LogQueryVisualizationType.TABLE,
72+
queryLines: [
73+
'filter action = "BLOCK"',
74+
'stats count(*) as blockded by terminatingRuleId as rule',
75+
],
76+
});
77+
78+
const blockedAgents = new LogQueryWidget({
79+
title: 'Top 30 Blocked User-Agents',
80+
height: 8,
81+
width: 12,
82+
logGroupNames: [props.cloudwatchLogName],
83+
view: LogQueryVisualizationType.TABLE,
84+
queryLines: [
85+
'fields @timestamp, @message',
86+
'parse @message /(?i)"name":"user-agent","value":"(?<httpRequestUserAgent>[^"]+)/',
87+
'filter action == "ALLOW"',
88+
'stats count() as count by httpRequestUserAgent as UserAgent',
89+
'sort by count desc',
90+
],
91+
});
92+
const allowedAgents = new LogQueryWidget({
93+
title: 'Top 30 Allowed User-Agents',
94+
height: 8,
95+
width: 12,
96+
logGroupNames: [props.cloudwatchLogName],
97+
view: LogQueryVisualizationType.TABLE,
98+
queryLines: [
99+
'fields @timestamp, @message',
100+
'parse @message /(?i)"name":"user-agent","value":"(?<httpRequestUserAgent>[^"]+)/',
101+
'filter action == "BLOCK"',
102+
'stats count() as count by httpRequestUserAgent as UserAgent',
103+
'sort by count desc',
104+
],
105+
});
106+
const dashboard = new cw.Dashboard(this, 'Dash', {
107+
defaultInterval: cdk.Duration.days(7),
108+
});
109+
110+
dashboard.addWidgets(blockedWidget);
111+
dashboard.addWidgets(allowedWidget);
112+
dashboard.addWidgets(blockedCount);
113+
dashboard.addWidgets(blockedAgents);
114+
dashboard.addWidgets(allowedAgents);
115+
116+
}
117+
}

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
66
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
77
import { Provider } from 'aws-cdk-lib/custom-resources';
88
import { Construct } from 'constructs';
9+
import { CloudWatchWAFDashboard } from './components/dashboard';
910
import { WafRulesGeoBlock } from './components/waf-rule-geoblock';
1011
import { WafRulesManagedBuilder } from './components/waf-rule-managed';
1112

@@ -21,7 +22,7 @@ export interface ICdkWafGeoLibProps {
2122
priority: number;
2223
/** Deprecated: - use enableGeoBlocking Switch to control if the rule should block or count incomming requests. */
2324
block?: boolean;
24-
/** Sends logs to a CloudWatch LogGroup with a retention on it. */
25+
/** Sends logs to a CloudWatch LogGroup with a retention on it. If enabled you also get a CloudWatch Dashboard.*/
2526
enableCloudWatchLogs?: boolean;
2627
/** Name of the CloudWatch LogGroup where requests are stored. */
2728
cloudWatchLogGroupName?: string;
@@ -54,8 +55,8 @@ export interface ICdkWafGeoLibProps {
5455
enableAWSMangedRulePHPProtect?: boolean;
5556
/** The WordPress application rule group contains rules that block request patterns associated with the exploitation of vulnerabilities specific to WordPress sites. You should evaluate this rule group if you are running WordPress. This rule group should be used in conjunction with the SQL database and PHP application rule groups. */
5657
enableAWSMangedRuleWorkpressProtect?: boolean;
57-
5858
}
59+
5960
export class CdkWafGeoLib extends Construct {
6061
public readonly customResourceResult?: string;
6162
constructor(scope: Construct, id: string, props: ICdkWafGeoLibProps) {
@@ -185,12 +186,14 @@ export class CdkWafGeoLib extends Construct {
185186
},
186187
},
187188
);
189+
188190
this.customResourceResult = customResourceResult.getAttString('Result');
189191
new wafv2.CfnWebACLAssociation(this, 'WAFAssociation', {
190192
resourceArn: props.resourceArn,
191193
webAclArn: cfnWebACL.attrArn,
192194
});
193195
// END
196+
new CloudWatchWAFDashboard(this, 'waf-cloudwatch-dashboard', { cloudwatchLogName: log_group.logGroupName });
194197
}
195198
}
196199
}

0 commit comments

Comments
 (0)