Skip to content

Commit 05d0db4

Browse files
ran-isenbergRan Isenberg
andauthored
feature: add WAF to production (#769)
Co-authored-by: Ran Isenberg <ran.isenberg@ranthebuilder.cloud>
1 parent e13b5a8 commit 05d0db4

File tree

9 files changed

+957
-36
lines changed

9 files changed

+957
-36
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ This project aims to reduce cognitive load and answer these questions for you by
9090
- AWS Lambda handler 3 layer architecture: handler layer, logic layer and data access layer
9191
- Features flags and configuration based on AWS AppConfig
9292
- Idempotent API
93+
- REST API protected by WAF with four AWS managed rules in production deployment
9394
- CloudWatch dashboards - High level and low level including CloudWatch alarms
9495
- Unit, infrastructure, security, integration and end to end tests.
9596

cdk/service/api_construct.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
import cdk.service.constants as constants
1010
from cdk.service.api_db_construct import ApiDbConstruct
1111
from cdk.service.monitoring import CrudMonitoring
12+
from cdk.service.waf_construct import WafToApiGatewayConstruct
1213

1314

1415
class ApiConstruct(Construct):
15-
def __init__(self, scope: Construct, id_: str, appconfig_app_name: str) -> None:
16+
def __init__(self, scope: Construct, id_: str, appconfig_app_name: str, is_production_env: bool) -> None:
1617
super().__init__(scope, id_)
1718
self.id_ = id_
1819
self.api_db = ApiDbConstruct(self, f'{id_}db')
@@ -25,6 +26,10 @@ def __init__(self, scope: Construct, id_: str, appconfig_app_name: str) -> None:
2526
)
2627
self.monitoring = CrudMonitoring(self, id_, self.rest_api, self.api_db.db, self.api_db.idempotency_db, [self.create_order_func])
2728

29+
if is_production_env:
30+
# add WAF
31+
self.waf = WafToApiGatewayConstruct(self, f'{id_}waf', self.rest_api)
32+
2833
def _build_api_gw(self) -> aws_apigateway.RestApi:
2934
rest_api: aws_apigateway.RestApi = aws_apigateway.RestApi(
3035
self,

cdk/service/service_stack.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(self, scope: Construct, id: str, is_production_env: bool, **kwargs)
2727
self,
2828
get_construct_name(stack_prefix=id, construct_name='Crud'),
2929
self.dynamic_configuration.app_name,
30+
is_production_env=is_production_env,
3031
)
3132

3233
# add security check

cdk/service/waf_construct.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from aws_cdk import aws_apigateway as apigateway
2+
from aws_cdk import aws_wafv2 as waf
3+
from constructs import Construct
4+
5+
6+
class WafToApiGatewayConstruct(Construct):
7+
def __init__(self, scope: Construct, id: str, api: apigateway.RestApi, **kwargs) -> None:
8+
super().__init__(scope, id, **kwargs)
9+
10+
# Create WAF WebACL with AWS Managed Rules
11+
web_acl = waf.CfnWebACL(
12+
self,
13+
'ProductApiGatewayWebAcl',
14+
scope='REGIONAL', # Change to CLOUDFRONT if you're using edge-optimized API
15+
default_action=waf.CfnWebACL.DefaultActionProperty(allow={}),
16+
name=f'{id}-Waf',
17+
visibility_config=waf.CfnWebACL.VisibilityConfigProperty(
18+
sampled_requests_enabled=True, cloud_watch_metrics_enabled=True, metric_name='ProductApiGatewayWebAcl'
19+
),
20+
rules=[
21+
waf.CfnWebACL.RuleProperty(
22+
name='Product-AWSManagedRulesCommonRuleSet',
23+
priority=0,
24+
override_action={'none': {}},
25+
statement=waf.CfnWebACL.StatementProperty(
26+
managed_rule_group_statement=waf.CfnWebACL.ManagedRuleGroupStatementProperty(
27+
name='AWSManagedRulesCommonRuleSet', vendor_name='AWS'
28+
)
29+
),
30+
visibility_config=waf.CfnWebACL.VisibilityConfigProperty(
31+
sampled_requests_enabled=True,
32+
cloud_watch_metrics_enabled=True,
33+
metric_name='Product-AWSManagedRulesCommonRuleSet',
34+
),
35+
),
36+
# Block Amazon IP reputation list managed rule group
37+
waf.CfnWebACL.RuleProperty(
38+
name='Product-AWSManagedRulesAmazonIpReputationList',
39+
priority=1,
40+
override_action={'none': {}},
41+
statement=waf.CfnWebACL.StatementProperty(
42+
managed_rule_group_statement=waf.CfnWebACL.ManagedRuleGroupStatementProperty(
43+
name='AWSManagedRulesAmazonIpReputationList', vendor_name='AWS'
44+
)
45+
),
46+
visibility_config=waf.CfnWebACL.VisibilityConfigProperty(
47+
sampled_requests_enabled=True,
48+
cloud_watch_metrics_enabled=True,
49+
metric_name='Product-AWSManagedRulesAmazonIpReputationList',
50+
),
51+
),
52+
# Block Anonymous IP list managed rule group
53+
waf.CfnWebACL.RuleProperty(
54+
name='Product-AWSManagedRulesAnonymousIpList',
55+
priority=2,
56+
override_action={'none': {}},
57+
statement=waf.CfnWebACL.StatementProperty(
58+
managed_rule_group_statement=waf.CfnWebACL.ManagedRuleGroupStatementProperty(
59+
name='AWSManagedRulesAnonymousIpList', vendor_name='AWS'
60+
)
61+
),
62+
visibility_config=waf.CfnWebACL.VisibilityConfigProperty(
63+
sampled_requests_enabled=True,
64+
cloud_watch_metrics_enabled=True,
65+
metric_name='Product-AWSManagedRulesAnonymousIpList',
66+
),
67+
),
68+
# rule for blocking known Bad Inputs
69+
waf.CfnWebACL.RuleProperty(
70+
name='Product-AWSManagedRulesKnownBadInputsRuleSet',
71+
priority=3,
72+
override_action={'none': {}},
73+
statement=waf.CfnWebACL.StatementProperty(
74+
managed_rule_group_statement=waf.CfnWebACL.ManagedRuleGroupStatementProperty(
75+
name='AWSManagedRulesKnownBadInputsRuleSet', vendor_name='AWS'
76+
)
77+
),
78+
visibility_config=waf.CfnWebACL.VisibilityConfigProperty(
79+
sampled_requests_enabled=True,
80+
cloud_watch_metrics_enabled=True,
81+
metric_name='Product-AWSManagedRulesKnownBadInputsRuleSet',
82+
),
83+
),
84+
],
85+
)
86+
87+
# Associate WAF with API Gateway
88+
waf.CfnWebACLAssociation(self, 'ApiGatewayWafAssociation', resource_arn=api.deployment_stage.stage_arn, web_acl_arn=web_acl.attr_arn)

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ This project aims to reduce cognitive load and answer these questions for you by
4444
- Features flags and configuration based on AWS AppConfig
4545
- CloudWatch dashboards - High level and low level including CloudWatch alarms
4646
- Idempotent API
47+
- REST API protected by WAF with four AWS managed rules in production deployment
4748
- Unit, infrastructure, security, integration and E2E tests.
4849

4950
The GitHub template project can be found at [https://github.com/ran-isenberg/aws-lambda-handler-cookbook](https://github.com/ran-isenberg/aws-lambda-handler-cookbook){:target="_blank" rel="noopener"}.

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"dependencies": {
3-
"aws-cdk": "2.103.1"
3+
"aws-cdk": "2.104.0"
44
}
55
}

0 commit comments

Comments
 (0)