Kaizen

Web Application Firewall

AWS WAF on the load balancer with managed rule groups and an optional rate limit.

GovCloudView source
Module
waf
Layer
Edge
Interface
7 inputs, 2 outputs
Used by
3 Kaizen apps

Why it matters

Automated scanners probe public web apps for SQL injection, cross site scripting, and known exploits soon after launch. This module puts AWS managed rule sets in front of the load balancer, so those requests are blocked before they reach application code. Teams can add a per IP rate limit and an allowlist with one setting each, without writing any rules.

Use it when

  • The app has a public load balancer from the alb module.
  • You need to slow down floods from one source IP. Set rate_limit_per_5min.
  • A few upload routes need request bodies larger than 8 KB. List them in body_size_exempt_paths.

Reach for something else when

  • You need WAF request logs in S3 or CloudWatch. The module creates no logging configuration, so add aws_wafv2_web_acl_logging_configuration in the app stack.
  • The site sits behind CloudFront. This ACL has REGIONAL scope and attaches only to a load balancer.

What it creates

  • aws_wafv2_web_acl (REGIONAL scope, default action allow)
  • AWSManagedRulesAmazonIpReputationList (priority 10)
  • AWSManagedRulesCommonRuleSet (priority 20)
  • AWSManagedRulesKnownBadInputsRuleSet (priority 30)
  • RateLimitPerIp rule (priority 5, only when rate_limit_per_5min is set)
  • aws_wafv2_ip_set and AllowListedIps rule (priority 0, only when allowed_ip_cidrs is set)
  • BlockOversizeBodyExceptPaths rule (priority 25, only when body_size_exempt_paths is set)
  • aws_wafv2_web_acl_association (attaches the ACL to alb_arn)

Secure by default

  • AWS IP reputation list, core rule set, and known bad inputs rule set are on for every ACL.
  • CloudWatch metrics and request sampling are on for every rule.

Commercial and GovCloud

main.tf
module "waf" {
  source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//waf?ref=v1.7.0"

  app_name            = local.app_name
  alb_arn             = module.alb.alb_arn
  rate_limit_per_5min = 2000

  tags = local.tags
}

How it connects

  • From alb: alb_arn

Inputs

NameTypeDefaultRequiredDescription
alb_arnstringyesARN of the ALB to associate with the WAF ACL
app_namestringyesApplication name used in resource naming
allowed_ip_cidrsset(string)[]noIPv4 CIDRs allowed before every other rule, skipping rate limiting and managed rule groups. Empty adds no rule.
body_size_exempt_pathsset(string)[]noExact URI paths exempt from the over-8192-byte request-body limit. Empty preserves the managed CommonRuleSet behavior.
environmentstring""noEnvironment suffix. Leave empty for accounts with no env concept.
rate_limit_per_5minnumbernullnoL7-DDoS rate-based rule threshold: requests per source IP over a 5-minute window. Null disables the rule. AWS WAF minimum is 100.
tagsmap(string){}noTags to apply to all resources

Outputs

NameDescription
web_acl_arnValue: aws_wafv2_web_acl.main.arn
web_acl_idValue: aws_wafv2_web_acl.main.id

Used by

AppPinned ref
evergreenv1.0.0
oakv1.5.0
saplingsv1.4.0

On this page