Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
theseanything committed Jan 27, 2025
1 parent 9f17c76 commit c767a78
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 0 deletions.
17 changes: 17 additions & 0 deletions terraform/deployments/govuk-publishing-infrastructure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -107,25 +107,42 @@ variable "amazonmq_govuk_chat_retry_message_ttl" {
variable "allow_high_request_rate_from_cidrs" {
type = list(string)
description = "List of CIDRs from which we allow a higher ratelimit."
default = []
}

variable "backend_public_base_rate_warning" {
type = number
description = "A warning rate limit threshold for the backend public web ACL"
default = 2000
}

variable "backend_public_base_rate_limit" {
type = number
description = "An enforced rate limit threshold for the backend public web ACL"
default = 1000
}

variable "backend_public_ja3_denylist" {
type = list(string)
description = "For the backend ALB. List of JA3 signatures for which we should block all requests."
default = []
}

variable "waf_log_retention_days" {
type = string
description = "The number of days CloudWatch will retain WAF logs for."
default = "30"
}

variable "bouncer_public_base_rate_warning" {
type = number
description = "A warning rate limit threshold for the bouncer public web ACL"
default = 2000
}

variable "bouncer_public_base_rate_limit" {
type = number
description = "An enforced rate limit threshold for the bouncer public web ACL"
default = 1000
}

177 changes: 177 additions & 0 deletions terraform/deployments/govuk-publishing-infrastructure/wafs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,180 @@ resource "aws_wafv2_web_acl_logging_configuration" "public_backend_waf" {
log_destination_configs = [aws_cloudwatch_log_group.public_backend_waf.arn]
resource_arn = aws_wafv2_web_acl.backend_public.arn
}

resource "aws_wafv2_web_acl" "bouncer_public" {
name = "bouncer_public_web_acl"
scope = "REGIONAL"

default_action {
allow {}
}

# this rule matches any request that contains the header X-Always-Block: true
# we use it as a simple sanity check / acceptance test from smokey to ensure that
# the waf is enabled and processing requests
rule {
name = "x-always-block_web_acl_rule"
priority = 1

override_action {
none {}
}

statement {
rule_group_reference_statement {
arn = aws_wafv2_rule_group.x_always_block.arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "x-always-block-rule-group"
sampled_requests_enabled = true
}
}

# This rule is intended for monitoring only
# set a base rate limit per IP looking back over the last 5 minutes
# this is checked every 30s
rule {
name = "bouncer-public-base-rate-warning"
priority = 2

action {
count {}
}

statement {
rate_based_statement {
limit = var.bouncer_public_base_rate_warning
aggregate_key_type = "FORWARDED_IP"

forwarded_ip_config {
# We expect all requests to have this header set. As we're counting,
#it's a good chance to verify that by matching any that don't
fallback_behavior = "MATCH"
header_name = "true-client-ip"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "bouncer-public-base-rate-warning"
sampled_requests_enabled = true
}
}

# set a base rate limit per IP looking back over the last 5 minutes
# this is checked every 30s
rule {
name = "bouncer-public-base-rate-limit"
priority = 3

action {
block {
custom_response {
response_code = 429

response_header {
name = "Retry-After"
value = 30
}

response_header {
name = "Cache-Control"
value = "max-age=0, private"
}

custom_response_body_key = "bouncer-public-rule-429"
}
}
}

statement {
rate_based_statement {
limit = var.bouncer_public_base_rate_limit
aggregate_key_type = "FORWARDED_IP"

forwarded_ip_config {
# We expect all requests to have this header set. As we're counting,
#it's a good chance to verify that by matching any that don't
fallback_behavior = "MATCH"
header_name = "true-client-ip"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "bouncer-public-base-rate-limit"
sampled_requests_enabled = true
}
}

custom_response_body {
key = "bouncer-public-rule-429"
content = <<HTML
<!DOCTYPE html>
<html>
<head>
<title>Welcome to GOV.UK</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; }
header { background: black; }
h1 { color: white; font-size: 29px; margin: 0 auto; padding: 10px; max-width: 990px; }
p { color: black; margin: 30px auto; max-width: 990px; }
</style>
</head>
<body>
<header><h1>GOV.UK</h1></header>
<p>Sorry, there have been too many attempts to access this page.</p>
<p>Try again in a few minutes.</p>
</body>
</html>
HTML

content_type = "TEXT_HTML"
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "bouncer-public-web-acl"
sampled_requests_enabled = true
}
}

resource "aws_cloudwatch_log_group" "public_bouncer_waf" {
# the name must start with aws-waf-logs
# https://docs.aws.amazon.com/waf/latest/developerguide/logging-cw-logs.html#logging-cw-logs-naming
name = "aws-waf-logs-bouncer-public-${var.govuk_environment}"
retention_in_days = var.waf_log_retention_days
}

resource "aws_wafv2_web_acl_logging_configuration" "public_bouncer_waf" {
log_destination_configs = [aws_cloudwatch_log_group.public_bouncer_waf.arn]
resource_arn = aws_wafv2_web_acl.bouncer_public.arn

logging_filter {
default_behavior = "DROP"

filter {
behavior = "KEEP"

condition {
action_condition {
action = "COUNT"
}
}

condition {
action_condition {
action = "BLOCK"
}
}

requirement = "MEETS_ANY"
}
}
}

0 comments on commit c767a78

Please sign in to comment.