Skip to main content

WAF & Route53

The multi-region shopping mall platform protects web applications with AWS WAF v2 and connects users to the nearest region using Route53 latency-based routing.

Architecture​

WAF v2 Web ACL​

Rule Configuration​

PriorityRule NameTypeActionDescription
1AWSManagedRulesCommonRuleSetManagedOverrideCommon web vulnerability protection
2AWSManagedRulesKnownBadInputsRuleSetManagedOverrideKnown malicious input patterns
3AWSManagedRulesSQLiRuleSetManagedOverrideSQL injection protection
4AWSManagedRulesBotControlRuleSetManagedOverrideBot traffic control
5RateLimitCustomBlock2000 req/5min per IP
6GeoBlockCustomBlockSanctioned country blocking

Terraform Configuration​

resource "aws_wafv2_web_acl" "main" {
name = "${var.environment}-cloudfront-waf"
description = "WAF Web ACL for CloudFront distribution"
scope = "CLOUDFRONT" # CloudFront WAF must be created in us-east-1 only

default_action {
allow {}
}

# Rule 1: AWS Managed Common Rule Set
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1

override_action {
none {}
}

statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-common-rules"
sampled_requests_enabled = true
}
}

# Rule 2: Known Bad Inputs
rule {
name = "AWSManagedRulesKnownBadInputsRuleSet"
priority = 2

override_action {
none {}
}

statement {
managed_rule_group_statement {
name = "AWSManagedRulesKnownBadInputsRuleSet"
vendor_name = "AWS"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-bad-inputs"
sampled_requests_enabled = true
}
}

# Rule 3: SQL Injection
rule {
name = "AWSManagedRulesSQLiRuleSet"
priority = 3

override_action {
none {}
}

statement {
managed_rule_group_statement {
name = "AWSManagedRulesSQLiRuleSet"
vendor_name = "AWS"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-sqli"
sampled_requests_enabled = true
}
}

# Rule 4: Bot Control
rule {
name = "AWSManagedRulesBotControlRuleSet"
priority = 4

override_action {
none {}
}

statement {
managed_rule_group_statement {
name = "AWSManagedRulesBotControlRuleSet"
vendor_name = "AWS"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-bot-control"
sampled_requests_enabled = true
}
}

# Rule 5: Rate Limiting
rule {
name = "RateLimit"
priority = 5

action {
block {}
}

statement {
rate_based_statement {
limit = 2000 # 2000 requests per 5 minutes
aggregate_key_type = "IP"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-rate-limit"
sampled_requests_enabled = true
}
}

# Rule 6: Geo Block (Sanctioned Countries)
rule {
name = "GeoBlock"
priority = 6

action {
block {}
}

statement {
geo_match_statement {
country_codes = ["KP", "IR", "CU", "SY"] # Sanctioned countries
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-geo-block"
sampled_requests_enabled = true
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-cloudfront-waf"
sampled_requests_enabled = true
}
}

Rate Limiting Details​

ParameterValueDescription
limit2000Maximum requests allowed per 5 minutes
aggregate_key_typeIPAggregate by IP address
evaluation_window300 secondsDefault 5-minute window

Route53 Configuration​

DNS Records​

RecordTypeRouting PolicyValue
mall.atomai.clickCNAMESimpledXXXXXXXXXXXXX.cloudfront.net
api-internal.atomai.clickA (Alias)LatencyALB (us-east-1)
api-internal.atomai.clickA (Alias)LatencyALB (us-west-2)

Latency-Based Routing​

Latency-based routing connects users to the region with the lowest latency.

resource "aws_route53_record" "api_latency" {
for_each = { for k, v in var.alb_dns_names : k => v if v != "" }

zone_id = var.zone_id
name = "api-internal.${data.aws_route53_zone.main.name}"
type = "A"

alias {
name = each.value
zone_id = var.alb_zone_ids[each.key]
evaluate_target_health = true
}

set_identifier = each.key

latency_routing_policy {
region = each.key
}

health_check_id = aws_route53_health_check.regional[each.key].id
}

Health Checks​

Monitor the health status of ALBs in each region.

resource "aws_route53_health_check" "regional" {
for_each = var.alb_dns_names

fqdn = each.value
port = 443
type = "HTTPS"
resource_path = var.health_check_path # /health
request_interval = var.health_check_interval # 30 seconds
failure_threshold = var.health_check_failure_threshold # 3

tags = {
Name = "${var.environment}-${each.key}-health-check"
Region = each.key
}
}

Health Check Parameters​

ParameterValueDescription
typeHTTPSHTTPS endpoint check
port443HTTPS port
resource_path/healthHealth check path
request_interval30 secondsCheck interval
failure_threshold3Failure threshold

CloudWatch Alarm Integration​

resource "aws_cloudwatch_metric_alarm" "health_check" {
for_each = var.alb_dns_names

alarm_name = "${var.environment}-${each.key}-health-check-alarm"
comparison_operator = "LessThanThreshold"
evaluation_periods = 2
metric_name = "HealthCheckStatus"
namespace = "AWS/Route53"
period = 60
statistic = "Minimum"
threshold = 1
alarm_description = "Health check alarm for ${each.key} region"
treat_missing_data = "breaching"

dimensions = {
HealthCheckId = aws_route53_health_check.regional[each.key].id
}
}

Failover Scenarios​

Single Region Failure​

Recovery Process​

  1. us-east-1 ALB returns to normal
  2. Health Check succeeds (1 time)
  3. Route53 adds us-east-1 back to the routing pool
  4. Users are routed to the nearest region based on latency

Monitoring​

WAF Metrics​

MetricDescription
AllowedRequestsNumber of allowed requests
BlockedRequestsNumber of blocked requests
CountedRequestsNumber of counted requests
PassedRequestsNumber of passed requests

WAF Logging​

resource "aws_wafv2_web_acl_logging_configuration" "main" {
log_destination_configs = [aws_cloudwatch_log_group.waf_logs.arn]
resource_arn = aws_wafv2_web_acl.main.arn

logging_filter {
default_behavior = "DROP"

filter {
behavior = "KEEP"
requirement = "MEETS_ANY"

condition {
action_condition {
action = "BLOCK"
}
}

condition {
action_condition {
action = "COUNT"
}
}
}
}
}

Route53 Health Check Metrics​

MetricDescription
HealthCheckStatus1 = Healthy, 0 = Unhealthy
HealthCheckPercentageHealthyHealthy percentage (%)
ConnectionTimeConnection time
SSLHandshakeTimeSSL handshake time

Security Recommendations​

WAF Rule Tuning​

  1. Log Analysis: Monitor if legitimate requests are being blocked
  2. Count Mode: Test new rules in Count mode first
  3. Exception Rules: Whitelist specific IPs/paths as needed

Rate Limiting Adjustment​

# Different rate limits per API endpoint
rule {
name = "APIRateLimit"
priority = 7

action {
block {}
}

statement {
rate_based_statement {
limit = 100 # Stricter for auth APIs
aggregate_key_type = "IP"

scope_down_statement {
byte_match_statement {
search_string = "/api/auth/"
field_to_match {
uri_path {}
}
positional_constraint = "STARTS_WITH"
text_transformation {
priority = 0
type = "LOWERCASE"
}
}
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.environment}-api-rate-limit"
sampled_requests_enabled = true
}
}

Next Steps​