def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) prj_name = self.node.try_get_context("project_name") env_name = self.node.try_get_context("env") basic_rule = waf.CfnWebACL.RuleProperty( name='AWSManagedCommonRue', priority=0, statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesCommonRuleSet', vendor_name='AWS')), override_action=waf.CfnWebACL.OverrideActionProperty(count={}), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='AWSManagedCommonRule', sampled_requests_enabled=True)) web_acl = waf.CfnWebACL( self, 'web-acl-id', default_action=waf.CfnWebACL.DefaultActionProperty(allow={}), scope='CLOUDFRONT', visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name=prj_name + '-' + env_name, sampled_requests_enabled=True), name=prj_name + '-' + env_name + 'webacl', rules=[basic_rule]) ssm.StringParameter(self, 'web-id-ssm', parameter_name='/' + env_name + '/webacl-id', string_value=web_acl.attr_id)
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) web_acl = wafv2.CfnWebACL( scope_=self, id='WebAcl', default_action=wafv2.CfnWebACL.DefaultActionProperty(allow={}), scope='REGIONAL', visibility_config=wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, sampled_requests_enabled=True, metric_name='testwafmetric', ), name='test-web-acl', rules=[{ 'name': 'AWS-AWSManagedRulesCommonRuleSet', 'priority': 0, 'statement': { 'managedRuleGroupStatement': { 'vendorName': 'AWS', 'name': 'AWSManagedRulesCommonRuleSet' } }, 'overrideAction': { 'none': {} }, 'visibilityConfig': { 'sampledRequestsEnabled': True, 'cloudWatchMetricsEnabled': True, 'metricName': "AWS-AWSManagedRulesCommonRuleSet" } }, { 'name': 'AWS-AWSManagedRulesAmazonIpReputationList', 'priority': 1, 'statement': { 'managedRuleGroupStatement': { 'vendorName': 'AWS', 'name': 'AWSManagedRulesAmazonIpReputationList' } }, 'overrideAction': { 'count': {} }, 'visibilityConfig': { 'sampledRequestsEnabled': True, 'cloudWatchMetricsEnabled': True, 'metricName': "AWS-AWSManagedRulesAmazonIpReputationList" } }])
def __init__(self, app: core.App, id: str) -> None: super().__init__(app, id) vpc = ec2.Vpc(self, "VPC") data = open("./httpd.sh", "rb").read() httpd = ec2.UserData.for_linux() httpd.add_commands(str(data, 'utf-8')) asg = autoscaling.AutoScalingGroup( self, "ASG", vpc=vpc, instance_type=ec2.InstanceType.of( ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO ), machine_image=ec2.AmazonLinuxImage(generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2), user_data=httpd, ) lb = elbv2.ApplicationLoadBalancer( self, "LB", vpc=vpc, internet_facing=True) listener = lb.add_listener("Listener", port=80) listener.add_targets("Target", port=80, targets=[asg]) listener.connections.allow_default_port_from_any_ipv4("Open to the world") asg.scale_on_request_count("AModestLoad", target_requests_per_second=1) core.CfnOutput(self, "LoadBalancer", export_name="LoadBalancer", value=lb.load_balancer_dns_name) # === # # WAF # # === # # TODO #10 apply the web_acl to a resource # no method to apply the web_acl to a resource in version 1.75.0 web_acl = wafv2.CfnWebACL( scope_=self, id="waf", default_action=wafv2.CfnWebACL.DefaultActionProperty(), scope="REGIONAL", visibility_config=wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="waf-web-acl", sampled_requests_enabled=True ) )
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) ## ## List available Managed Rule Groups using AWS CLI ## aws wafv2 list-available-managed-rule-groups --scope REGIONAL ## managed_rules = [{ "name": "AWSManagedRulesCommonRuleSet", "priority": 10, "override_action": "none", "excluded_rules": [], }, { "name": "AWSManagedRulesAmazonIpReputationList", "priority": 20, "override_action": "none", "excluded_rules": [], }, { "name": "AWSManagedRulesKnownBadInputsRuleSet", "priority": 30, "override_action": "none", "excluded_rules": [], }, { "name": "AWSManagedRulesSQLiRuleSet", "priority": 40, "override_action": "none", "excluded_rules": [], }, { "name": "AWSManagedRulesLinuxRuleSet", "priority": 50, "override_action": "none", "excluded_rules": [], }, { "name": "AWSManagedRulesUnixRuleSet", "priority": 60, "override_action": "none", "excluded_rules": [], }] ############################################################# ## ## WAF - Regional, for use in Load Balancers ## ############################################################# wafacl = wafv2.CfnWebACL( self, id="WAF", default_action=wafv2.CfnWebACL.DefaultActionProperty(allow={}, block=None), ## ## The scope of this Web ACL. ## Valid options: CLOUDFRONT, REGIONAL. ## For CLOUDFRONT, you must create your WAFv2 resources ## in the US East (N. Virginia) Region, us-east-1 ## https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webacl.html#cfn-wafv2-webacl-scope ## scope="REGIONAL", ## ## Defines and enables Amazon CloudWatch metrics and web request sample collection. ## visibility_config=wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="waf-regional", sampled_requests_enabled=True), description="WAFv2 ACL for Regional", name="waf-regional", rules=self.make_rules(managed_rules), ) ## wafv2.CfnWebACL core.Tags.of(wafacl).add("Name", "waf-regional", priority=300) core.Tags.of(wafacl).add("Purpose", "WAF for Regional", priority=300) core.Tags.of(wafacl).add("CreatedBy", "Cloudformation", priority=300) core.CfnOutput(self, "WafAclArn", export_name="WafRegionalStack:WafAclRegionalArn", value=wafacl.attr_arn)
def __init__( self, scope: core.Construct, id: str, stack_log_level: str, secure_api_stage_arn: str, rps_limit: str, ** kwargs ) -> None: super().__init__(scope, id, **kwargs) # Your Code): # Let us create a Rate Based Rule web_acl = _wafv2.CfnWebACL( self, f"apiSentryAcl-{id}", default_action=_wafv2.CfnWebACL.DefaultActionProperty(allow={}), scope="REGIONAL", visibility_config=_wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name=f"apiSentryAcl_metrics", sampled_requests_enabled=True ), name=f"apiSentryAcl-{id}", description=f"{GlobalArgs.OWNER}: Protect API with Web Application Firewall - Rate Based Rules", rules=[] ) # Retrieve Cognito App Client Secret and Add to Secrets Manager waf_rule_creator = WafRateRuleCreatorStack( self, "wafRuleCreator", web_acl_name=web_acl.name, web_acl_id=web_acl.attr_id, web_acl_scope=web_acl.scope, rps_limit=rps_limit ) # Export Value self.waf_rule_creator_status = waf_rule_creator.response # Associate WebAcl to API Gateway add_waf_to_resource = _wafv2.CfnWebACLAssociation( self, id="addWafToApiGw", resource_arn=secure_api_stage_arn, web_acl_arn=web_acl.attr_arn ) add_waf_to_resource.node.add_dependency(waf_rule_creator) # Outputs output_0 = core.CfnOutput( self, "AutomationFrom", value=f"{GlobalArgs.SOURCE_INFO}", description="To know more about this automation stack, check out our github page." ) output_1 = core.CfnOutput( self, "WafArn", value=f"{web_acl.attr_arn}", description="The Web ACL to secure our Api" ) output_2 = core.CfnOutput( self, "RateRuleCreatorStatus", value=f"{self.waf_rule_creator_status}", description="Waf Rate Rule Creator Status" )
def __init__(self, scope: core.Construct, id: str, target_arn, **kwargs) -> None: super().__init__(scope, id, **kwargs) waf_rules = [] # 1, AWS general rules aws_managed_rules = waf.CfnWebACL.RuleProperty( name='AWS-AWSManagedRulesCommonRuleSet', priority=1, override_action=waf.CfnWebACL.OverrideActionProperty(none={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesCommonRuleSet', vendor_name='AWS', excluded_rules=[ waf.CfnWebACL.ExcludedRuleProperty( name='SizeRestrictions_BODY') ])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='awsCommonRules', sampled_requests_enabled=True, ), ) waf_rules.append(aws_managed_rules) # 2, AWS AnonIPAddress aws_anoniplist = waf.CfnWebACL.RuleProperty( name='awsAnonymousIP', priority=2, override_action=waf.CfnWebACL.OverrideActionProperty(none={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesAnonymousIpList', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='awsAnonymous', sampled_requests_enabled=True, )) waf_rules.append(aws_anoniplist) # 3 AWS ip reputation List aws_ip_rep_list = waf.CfnWebACL.RuleProperty( name='aws_Ipreputation', priority=3, override_action=waf.CfnWebACL.OverrideActionProperty(none={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesAmazonIpReputationList', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_reputation', sampled_requests_enabled=True, )) waf_rules.append(aws_ip_rep_list) # 4 GeoBlock NZ from accessing gateway geoblock_rule = waf.CfnWebACL.RuleProperty( name='geoblocking_rule', priority=4, action=waf.CfnWebACL.RuleActionProperty(block={}), statement=waf.CfnWebACL.StatementOneProperty( geo_match_statement=waf.CfnWebACL.GeoMatchStatementProperty( country_codes=['NZ'], )), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='geoblock', sampled_requests_enabled=True, )) waf_rules.append(geoblock_rule) # Create the Waf ACL WebACL = waf.CfnWebACL( self, 'WebACL', default_action=waf.CfnWebACL.DefaultActionProperty(allow={}), scope="REGIONAL", # vs 'CLOUDFRONT' visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='webACL', sampled_requests_enabled=True), name='HelloWorldACL', rules=waf_rules) # Associate it with the resource provided. waf.CfnWebACLAssociation(self, 'WAFAssnAPI', web_acl_arn=WebACL.attr_arn, resource_arn=target_arn)
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) API_ARN = self.node.try_get_context("api_arn") RATE = self.node.try_get_context("rate") if not API_ARN or not RATE: logger.error( f"Required context variables for {id} were not provided!") else: # Create the WAF IPSets doslist = wafv2.CfnIPSet( self, "Ext06DosIpSet", addresses=[], ip_address_version="IPV4", scope="REGIONAL", name="Ext06DosIpSet", ) suslist = wafv2.CfnIPSet( self, "Ext06SusIpSet", addresses=[], ip_address_version="IPV4", scope="REGIONAL", name="Ext06SusIpSet", ) # Create a WAF waf = wafv2.CfnWebACL( self, id="Ext06_WAF", name="Ext06-WAF", default_action=wafv2.CfnWebACL.DefaultActionProperty(allow={}), scope="REGIONAL", visibility_config=wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="EXT06_WAF", sampled_requests_enabled=True), rules=[], ) # Create Susunban lambda lambda_dir_path = os.path.join(os.getcwd(), "ir_cdk_stacks", "ext_06") susunban_lambda = _lambda.Function( self, "Ext06ResponseSusUnbanFunction", runtime=_lambda.Runtime.PYTHON_3_8, handler="susunban_lambda.lambda_handler", code=_lambda.Code.from_asset(lambda_dir_path), environment={ "ipset_id": suslist.attr_id, "ipset_name": suslist.name, "ipset_scope": suslist.scope, }) # Assign WAF permissions to lambda susunban_lambda.add_to_role_policy( iam.PolicyStatement( actions=["wafv2:GetIPSet", "wafv2:UpdateIPSet"], effect=iam.Effect.ALLOW, resources=[suslist.attr_arn], )) # Create Dosunban lambda lambda_dir_path = os.path.join(os.getcwd(), "ir_cdk_stacks", "ext_06") dosunban_lambda = _lambda.Function( self, "Ext06ResponseDosUnbanFunction", runtime=_lambda.Runtime.PYTHON_3_8, handler="dosunban_lambda.lambda_handler", code=_lambda.Code.from_asset(lambda_dir_path), environment={ "ipset_id": doslist.attr_id, "ipset_name": doslist.name, "ipset_scope": doslist.scope, }) # Assign WAF permissions to lambda dosunban_lambda.add_to_role_policy( iam.PolicyStatement( actions=["wafv2:GetIPSet", "wafv2:UpdateIPSet"], effect=iam.Effect.ALLOW, resources=[doslist.attr_arn], )) # Create dos stepfunction # Define a second state machine to unban the blacklisted IP after 1 hour doswait_step = sfn.Wait( self, "Ext06ResponseStepDosWait", time=sfn.WaitTime.duration(core.Duration.hours(1)), ) suswait_step = sfn.Wait( self, "Ext06ResponseStepSusWait", time=sfn.WaitTime.duration(core.Duration.hours(1)), ) dosunban_step = sfn.Task( self, "Ext06ResponseStepDosUnban", task=tasks.RunLambdaTask( dosunban_lambda, integration_pattern=sfn.ServiceIntegrationPattern. FIRE_AND_FORGET, payload={"Input.$": "$"}, ), ) susunban_step = sfn.Task( self, "Ext06ResponseStepSosUnban", task=tasks.RunLambdaTask( susunban_lambda, integration_pattern=sfn.ServiceIntegrationPattern. FIRE_AND_FORGET, payload={"Input.$": "$"}, ), ) dos_statemachine = sfn.StateMachine( self, "Ext06ResponseDosUnbanStateMachine", definition=doswait_step.next(dosunban_step), timeout=core.Duration.hours(1.5), ) sus_statemachine = sfn.StateMachine( self, "Ext06ResponseSusUnbanStateMachine", definition=suswait_step.next(susunban_step), timeout=core.Duration.hours(1.5), ) # Create lambda function lambda_func = _lambda.Function( self, "Ext06ResponseFunction", runtime=_lambda.Runtime.PYTHON_3_8, handler="response_lambda.lambda_handler", code=_lambda.Code.from_asset(lambda_dir_path), environment={ "suslist_id": suslist.attr_id, "suslist_name": suslist.name, "suslist_scope": suslist.scope, "doslist_id": doslist.attr_id, "doslist_name": doslist.name, "doslist_scope": doslist.scope, "dos_arn": dos_statemachine.state_machine_arn, "sus_arn": sus_statemachine.state_machine_arn, }, ) kinesis_log = s3.Bucket( self, id='dos_logs', access_control=s3.BucketAccessControl.PUBLIC_READ_WRITE, ) # Assign permissions to response lambda lambda_func.add_to_role_policy( iam.PolicyStatement( actions=[ "wafv2:GetIPSet", "wafv2:UpdateIPSet", "states:StartExecution", "s3:GetObject", ], effect=iam.Effect.ALLOW, resources=[ doslist.attr_arn, suslist.attr_arn, sus_statemachine.state_machine_arn, dos_statemachine.state_machine_arn, kinesis_log.bucket_arn, kinesis_log.bucket_arn, kinesis_log.bucket_arn + "/*" ], )) # Create an IAM role for the steram stream_role = iam.Role( self, id="waf-kinesis-log-role", assumed_by=iam.ServicePrincipal( service="firehose.amazonaws.com", ), ) stream_permissions = iam.Policy( self, id="Ext-06-kinesis-permissions", statements=[ iam.PolicyStatement( actions=[ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", ], effect=iam.Effect.ALLOW, resources=[ kinesis_log.bucket_arn, kinesis_log.bucket_arn + "/*" ], ) ]) stream_role.attach_inline_policy(stream_permissions) log_stream = firehose.CfnDeliveryStream( self, id="aws-waf-logs-ext06", delivery_stream_type="DirectPut", delivery_stream_name="aws-waf-logs-ext06", s3_destination_configuration=firehose.CfnDeliveryStream. S3DestinationConfigurationProperty( bucket_arn=kinesis_log.bucket_arn, buffering_hints=firehose.CfnDeliveryStream. BufferingHintsProperty(interval_in_seconds=300, size_in_m_bs=5), compression_format="UNCOMPRESSED", role_arn=stream_role.role_arn), ) kinesis_log.add_event_notification( s3.EventType.OBJECT_CREATED, dest=s3_notifications.LambdaDestination(lambda_func)) utc_time = datetime.now(tz=timezone.utc) utc_time = utc_time + timedelta(minutes=5) cron_string = "cron(" + str(utc_time.minute) + " " + str( utc_time.hour) + " " + str(utc_time.day) + " " + str( utc_time.month) + " ? " + str(utc_time.year) + ")" trigger = events.Rule( self, id="ext-06 setup", rule_name="Ext06-trigger", schedule=events.Schedule.expression(cron_string)) setup_dir_path = os.path.join(os.getcwd(), "ir_cdk_stacks", "ext_06") setup_func = _lambda.Function( self, id="Ext06Setup", runtime=_lambda.Runtime.PYTHON_3_8, handler="setup.lambda_handler", code=_lambda.Code.from_asset(setup_dir_path), environment={ "waf_arn": waf.attr_arn, "waf_id": waf.attr_id, "waf_scope": waf.scope, "waf_name": waf.name, "firehose_arn": log_stream.attr_arn, "rule_name": "Ext06-trigger", "doslist_arn": doslist.attr_arn, "rate": str(RATE), }, ) # Assign permissions to setup lambda setup_func.add_to_role_policy( iam.PolicyStatement( actions=[ "wafv2:PutLoggingConfiguration", "wafv2:GetWebACL", "wafv2:UpdateWebACL" ], effect=iam.Effect.ALLOW, resources=[waf.attr_arn, doslist.attr_arn], )) setup = targets.LambdaFunction(handler=setup_func, ) setup.bind(rule=trigger) trigger.add_target(target=setup) wafv2.CfnWebACLAssociation( self, id="API gateway association", resource_arn=API_ARN, web_acl_arn=waf.attr_arn, )
def __init__(self, scope: core.Construct, id: str, env, target_arn, **kwargs) -> None: super().__init__(scope, id, env=env, **kwargs) waf_rules = list() """ 1. Reputation List """ aws_ip_rep_list = waf.CfnWebACL.RuleProperty( name='WafIpreputation', priority=1, override_action=waf.CfnWebACL.OverrideActionProperty(count={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesAmazonIpReputationList', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_reputation', sampled_requests_enabled=True, )) waf_rules.append(aws_ip_rep_list) """ 2. AnonymousIpList """ aws_anony_list = waf.CfnWebACL.RuleProperty( name='WafAnony', priority=2, override_action=waf.CfnWebACL.OverrideActionProperty(count={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesAnonymousIpList', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_anony', sampled_requests_enabled=True, )) waf_rules.append(aws_anony_list) """ 3. CommonRule """ aws_common_rule = waf.CfnWebACL.RuleProperty( name='WafCommonRule', priority=3, override_action=waf.CfnWebACL.OverrideActionProperty(count={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesCommonRuleSet', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_common', sampled_requests_enabled=True, )) waf_rules.append(aws_common_rule) """ 4. PHP Rule """ aws_php_rule = waf.CfnWebACL.RuleProperty( name='WafPHPRule', priority=4, override_action=waf.CfnWebACL.OverrideActionProperty(count={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesPHPRuleSet', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_php', sampled_requests_enabled=True, )) waf_rules.append(aws_php_rule) """ 5. Linux Rule """ aws_linux_rule = waf.CfnWebACL.RuleProperty( name='WafLinuxRule', priority=5, override_action=waf.CfnWebACL.OverrideActionProperty(count={}), statement=waf.CfnWebACL.StatementOneProperty( managed_rule_group_statement=waf.CfnWebACL. ManagedRuleGroupStatementProperty( name='AWSManagedRulesLinuxRuleSet', vendor_name='AWS', excluded_rules=[])), visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='aws_linux', sampled_requests_enabled=True, )) waf_rules.append(aws_linux_rule) """ DefaultAction: Action of AWS WAF to perform when a web request doesn't match any of the rules in the WebACL. """ web_acl = waf.CfnWebACL( self, 'WebACL', default_action=waf.CfnWebACL.DefaultActionProperty(allow={}), scope="REGIONAL", # vs 'CLOUDFRONT' visibility_config=waf.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='webACL', sampled_requests_enabled=True), name=f'prod-acl', rules=waf_rules) """ Associate it with the resource provided. """ waf.CfnWebACLAssociation(self, 'WAFACLAssociateALB', web_acl_arn=web_acl.attr_arn, resource_arn=target_arn)
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Demo app with an ALB, which only allows traffic from the specified set of CIDRs app = DemoApp(self, 'DemoApp', allow_from_cidrs=['0.0.0.0/0']) ### # Example of adding a Web ACL with a custom rule to the load balancer of a demo app ### acl = waf.CfnWebACL custom_rule = acl.RuleProperty( name='BlockQueriesContainingSubString', priority=1, action=acl.RuleActionProperty(block={}), statement=acl.StatementOneProperty( byte_match_statement=acl.ByteMatchStatementProperty( search_string='blockme', field_to_match=acl.FieldToMatchProperty(query_string={}), positional_constraint='CONTAINS', text_transformations=[ acl.TextTransformationProperty(priority=0, type='NONE'), ], )), visibility_config=acl.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, sampled_requests_enabled=True, metric_name='custom_rule', )) # Check out the following for examples using managed rules (shield advanced) # https://web.archive.org/web/20210321152739/https://dev.to/vumdao/using-aws-waf-and-shield-to-protect-ddos-5d03 # Create the ACL web_acl = waf.CfnWebACL( self, 'WebACL', default_action=acl.DefaultActionProperty(allow={}), scope='REGIONAL', # or use CLOUDFRONT if protecting a distribution visibility_config=acl.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name='webACL', sampled_requests_enabled=True), name=f'test-acl', rules=[ custom_rule, ]) # Associate the ACL with a resource; Here the ALB for the demo app. waf.CfnWebACLAssociation( self, 'WafAclAssociationALB', web_acl_arn=web_acl.attr_arn, resource_arn=app.alb.load_balancer_arn, ) # Link that triggers a block, and another to the AWS Console showing the generated WebACL core.CfnOutput(self, 'BlockedExampleUrl', value='http://{}/?blockme'.format( app.alb.load_balancer_dns_name)) core.CfnOutput( self, 'AclConsoleUrl', value= 'https://console.aws.amazon.com/wafv2/homev2/web-acls?region={}'. format(core.Stack.of(self).region))
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) API_ARN = self.node.try_get_context("api_arn") if not API_ARN: logger.error( f"Required context variables for {id} were not provided!") else: # Create XSS rule xss_body = wafv2.CfnRuleGroup.StatementOneProperty( xss_match_statement=wafv2.CfnRuleGroup. XssMatchStatementProperty( field_to_match=BODY, text_transformations=[NO_TEXT_TRANSFORMATION])) xss_query_string = wafv2.CfnRuleGroup.StatementOneProperty( xss_match_statement=wafv2.CfnRuleGroup. XssMatchStatementProperty( field_to_match=QUERY_STRING, text_transformations=[NO_TEXT_TRANSFORMATION])) xss_uri = wafv2.CfnRuleGroup.StatementOneProperty( xss_match_statement=wafv2.CfnRuleGroup. XssMatchStatementProperty( field_to_match=URI_PATH, text_transformations=[NO_TEXT_TRANSFORMATION])) xss_header = wafv2.CfnRuleGroup.StatementOneProperty( xss_match_statement=wafv2.CfnRuleGroup. XssMatchStatementProperty( field_to_match=SINGLE_HEADER, text_transformations=[NO_TEXT_TRANSFORMATION])) xss_rule_group = wafv2.CfnRuleGroup( self, id="XSS", capacity=160, scope="REGIONAL", visibility_config=wafv2.CfnRuleGroup.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="xss_attacks", sampled_requests_enabled=False), rules=[ wafv2.CfnRuleGroup.RuleProperty( name="xss_query_string", priority=1, statement=xss_query_string, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="xss_attacks", sampled_requests_enabled=False), ), wafv2.CfnRuleGroup.RuleProperty( name="xss_body", priority=2, statement=xss_body, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="xss_attacks", sampled_requests_enabled=False)), wafv2.CfnRuleGroup.RuleProperty( name="xss_uri", priority=3, statement=xss_uri, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="xss_attacks", sampled_requests_enabled=False)), wafv2.CfnRuleGroup.RuleProperty( name="xss_header", priority=4, statement=xss_header, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="xss_attacks", sampled_requests_enabled=False), ), ], ) # Create the SQLI rule group sqli_body = wafv2.CfnRuleGroup.StatementOneProperty( sqli_match_statement=wafv2.CfnRuleGroup. SqliMatchStatementProperty( field_to_match=BODY, text_transformations=[NO_TEXT_TRANSFORMATION])) sqli_query_string = wafv2.CfnRuleGroup.StatementOneProperty( sqli_match_statement=wafv2.CfnRuleGroup. SqliMatchStatementProperty( field_to_match=QUERY_STRING, text_transformations=[NO_TEXT_TRANSFORMATION])) sqli_uri = wafv2.CfnRuleGroup.StatementOneProperty( sqli_match_statement=wafv2.CfnRuleGroup. SqliMatchStatementProperty( field_to_match=URI_PATH, text_transformations=[NO_TEXT_TRANSFORMATION])) sqli_header = wafv2.CfnRuleGroup.StatementOneProperty( sqli_match_statement=wafv2.CfnRuleGroup. SqliMatchStatementProperty( field_to_match=SINGLE_HEADER, text_transformations=[NO_TEXT_TRANSFORMATION])) sqli_rule_group = wafv2.CfnRuleGroup( self, id="SQLI", capacity=80, scope="REGIONAL", visibility_config=wafv2.CfnRuleGroup.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="sqli_attacks", sampled_requests_enabled=False), rules=[ wafv2.CfnRuleGroup.RuleProperty( name="sqli_query_string", priority=1, statement=sqli_query_string, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="sqli_attacks", sampled_requests_enabled=False), ), wafv2.CfnRuleGroup.RuleProperty( name="sqli_body", priority=2, statement=sqli_body, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="sqli_attacks", sampled_requests_enabled=False)), wafv2.CfnRuleGroup.RuleProperty( name="sqli_uri", priority=3, statement=sqli_uri, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="sqli_attacks", sampled_requests_enabled=False)), wafv2.CfnRuleGroup.RuleProperty( name="sqli_header", priority=4, statement=sqli_header, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="sqli_attacks", sampled_requests_enabled=False), ), ], ) # Create the LFI and path traversal sets regex_pattern_set = wafv2.CfnRegexPatternSet( self, id="Ext01LptSet", regular_expression_list=[".*\.\./.*", ".*://.*"], scope="REGIONAL") lpt_query_string = wafv2.CfnRuleGroup.StatementOneProperty( regex_pattern_set_reference_statement=wafv2.CfnRuleGroup. RegexPatternSetReferenceStatementProperty( arn=regex_pattern_set.attr_arn, field_to_match=QUERY_STRING, text_transformations=[NO_TEXT_TRANSFORMATION])) lpt_uri = wafv2.CfnRuleGroup.StatementOneProperty( regex_pattern_set_reference_statement=wafv2.CfnRuleGroup. RegexPatternSetReferenceStatementProperty( arn=regex_pattern_set.attr_arn, field_to_match=URI_PATH, text_transformations=[NO_TEXT_TRANSFORMATION])) lpt_rule_group = wafv2.CfnRuleGroup( self, id="LPT", capacity=50, scope="REGIONAL", visibility_config=wafv2.CfnRuleGroup.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="lpt_attacks", sampled_requests_enabled=False), rules=[ wafv2.CfnRuleGroup.RuleProperty( name="lpt_query_string", priority=1, statement=lpt_query_string, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="lpt_attacks", sampled_requests_enabled=False), ), wafv2.CfnRuleGroup.RuleProperty( name="lpt_uri", priority=2, statement=lpt_uri, action=wafv2.CfnRuleGroup.RuleActionProperty(block={}), visibility_config=wafv2.CfnRuleGroup. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="lpt_attacks", sampled_requests_enabled=False)), ], ) # Create new WAF IPSet blacklist = wafv2.CfnIPSet( self, "Ext01ResponseIpSet", addresses=[], ip_address_version="IPV4", scope="REGIONAL", name="Ext01ResponseIpSet", ) # Create reference statements xss_ref = wafv2.CfnWebACL.RuleGroupReferenceStatementProperty( arn=xss_rule_group.attr_arn) sqli_ref = wafv2.CfnWebACL.RuleGroupReferenceStatementProperty( arn=sqli_rule_group.attr_arn) lpt_ref = wafv2.CfnWebACL.RuleGroupReferenceStatementProperty( arn=lpt_rule_group.attr_arn) # Create a WAF waf = wafv2.CfnWebACL( self, id="Ext01_WAF", name="Ext01-WAF", default_action=wafv2.CfnWebACL.DefaultActionProperty(allow={}), scope="REGIONAL", visibility_config=wafv2.CfnWebACL.VisibilityConfigProperty( cloud_watch_metrics_enabled=True, metric_name="EXT01_WAF", sampled_requests_enabled=True), rules=[ wafv2.CfnWebACL.RuleProperty( name="SQLI", priority=2, statement=wafv2.CfnWebACL.StatementOneProperty( rule_group_reference_statement=sqli_ref), visibility_config=wafv2.CfnWebACL. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="sqli_requests", sampled_requests_enabled=False), override_action=wafv2.CfnWebACL.OverrideActionProperty( none={}), ), wafv2.CfnWebACL.RuleProperty( name="XSS", priority=3, statement=wafv2.CfnWebACL.StatementOneProperty( rule_group_reference_statement=xss_ref), visibility_config=wafv2.CfnWebACL. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="xss_requests", sampled_requests_enabled=False), override_action=wafv2.CfnWebACL.OverrideActionProperty( none={}), ), wafv2.CfnWebACL.RuleProperty( name="LPT", priority=4, statement=wafv2.CfnWebACL.StatementOneProperty( rule_group_reference_statement=lpt_ref), visibility_config=wafv2.CfnWebACL. VisibilityConfigProperty( cloud_watch_metrics_enabled=False, metric_name="lpt_requests", sampled_requests_enabled=False), override_action=wafv2.CfnWebACL.OverrideActionProperty( none={}), ), ], ) # Create unban lambda lambda_dir_path = os.path.join(os.getcwd(), "ir_cdk_stacks", "ext_01") unban_lambda = _lambda.Function( self, "Ext01ResponseUnbanFunction", runtime=_lambda.Runtime.PYTHON_3_8, handler="unban_lambda.lambda_handler", code=_lambda.Code.from_asset(lambda_dir_path), environment={ "ipset_id": blacklist.attr_id, "ipset_name": blacklist.name, "ipset_scope": blacklist.scope, }) # Assign WAF permissions to lambda unban_lambda.add_to_role_policy( iam.PolicyStatement( actions=["wafv2:GetIPSet", "wafv2:UpdateIPSet"], effect=iam.Effect.ALLOW, resources=[blacklist.attr_arn], )) # Create stepfunction # Define a second state machine to unban the blacklisted IP after 1 hour wait_step = sfn.Wait( self, "Ext01ResponseStepWait", time=sfn.WaitTime.duration(core.Duration.hours(1)), ) unban_step = sfn.Task( self, "Ext01ResponseStepUnban", task=tasks.RunLambdaTask( unban_lambda, integration_pattern=sfn.ServiceIntegrationPattern. FIRE_AND_FORGET, payload={"Input.$": "$"}), ) statemachine = sfn.StateMachine( self, "Ext01ResponseUnbanStateMachine", definition=wait_step.next(unban_step), timeout=core.Duration.hours(1.5), ) # Create lambda function lambda_func = _lambda.Function( self, "Ext01ResponseFunction", runtime=_lambda.Runtime.PYTHON_3_8, handler="response_lambda.lambda_handler", code=_lambda.Code.from_asset(lambda_dir_path), environment={ "ipset_id": blacklist.attr_id, "ipset_name": blacklist.name, "ipset_scope": blacklist.scope, "sfn_arn": statemachine.state_machine_arn, }, ) kinesis_log = s3.Bucket( self, id='waf_logs', access_control=s3.BucketAccessControl.PUBLIC_READ_WRITE, ) # Assign permissions to response lambda lambda_func.add_to_role_policy( iam.PolicyStatement( actions=[ "wafv2:GetIPSet", "wafv2:UpdateIPSet", "states:StartExecution", "s3:GetObject", ], effect=iam.Effect.ALLOW, resources=[ blacklist.attr_arn, statemachine.state_machine_arn, kinesis_log.bucket_arn, kinesis_log.bucket_arn, kinesis_log.bucket_arn + "/*" ], )) # Create an IAM role for the steram stream_role = iam.Role( self, id="waf-kinesis-log-role", assumed_by=iam.ServicePrincipal( service="firehose.amazonaws.com", ), ) stream_permissions = iam.Policy( self, id="Ext-01-kinesis-permissions", statements=[ iam.PolicyStatement( actions=[ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", ], effect=iam.Effect.ALLOW, resources=[ kinesis_log.bucket_arn, kinesis_log.bucket_arn + "/*" ], ) ]) stream_role.attach_inline_policy(stream_permissions) log_stream = firehose.CfnDeliveryStream( self, id="aws-waf-logs-ext01", delivery_stream_type="DirectPut", delivery_stream_name="aws-waf-logs-ext01", s3_destination_configuration=firehose.CfnDeliveryStream. S3DestinationConfigurationProperty( bucket_arn=kinesis_log.bucket_arn, buffering_hints=firehose.CfnDeliveryStream. BufferingHintsProperty(interval_in_seconds=120, size_in_m_bs=5), compression_format="UNCOMPRESSED", role_arn=stream_role.role_arn), ) kinesis_log.add_event_notification( s3.EventType.OBJECT_CREATED, dest=s3_notifications.LambdaDestination(lambda_func)) utc_time = datetime.now(tz=timezone.utc) utc_time = utc_time + timedelta(minutes=5) cron_string = "cron(" + str(utc_time.minute) + " " + str( utc_time.hour) + " " + str(utc_time.day) + " " + str( utc_time.month) + " ? " + str(utc_time.year) + ")" trigger = events.Rule( self, id="ext-01 setup", rule_name="Ext01-trigger", schedule=events.Schedule.expression(cron_string)) setup_dir_path = os.path.join(os.getcwd(), "ir_cdk_stacks", "ext_01") setup_func = _lambda.Function( self, id="Ext01Setup", runtime=_lambda.Runtime.PYTHON_3_8, handler="setup.lambda_handler", code=_lambda.Code.from_asset(setup_dir_path), environment={ "waf_arn": waf.attr_arn, "waf_id": waf.attr_id, "waf_scope": waf.scope, "waf_name": waf.name, "firehose_arn": log_stream.attr_arn, "rule_name": "Ext01-trigger", "blacklist_arn": blacklist.attr_arn, }, ) # Assign permissions to setup lambda setup_func.add_to_role_policy( iam.PolicyStatement( actions=[ "wafv2:PutLoggingConfiguration", "wafv2:GetWebACL", "wafv2:UpdateWebACL" ], effect=iam.Effect.ALLOW, resources=[ waf.attr_arn, blacklist.attr_arn, xss_rule_group.attr_arn, sqli_rule_group.attr_arn, lpt_rule_group.attr_arn ], )) setup = targets.LambdaFunction(handler=setup_func, ) setup.bind(rule=trigger) trigger.add_target(target=setup) wafv2.CfnWebACLAssociation( self, id="API gateway association", resource_arn=API_ARN, web_acl_arn=waf.attr_arn, )