From bd06c9c119719d7d303886f973b3a5e6e4e635ac Mon Sep 17 00:00:00 2001 From: roch Date: Tue, 2 Sep 2025 09:09:50 +0100 Subject: [PATCH] Add separate IP based rate limiting for chat domain At the moment all traffic that comes into the backend load balancer is subject to an IP based rate limiting at the WAF level. This is a problem for chat as we expect all traffic to some from a single source of traffic -the gov.uk app- therefore to solve this issue and add more flexibility in controlling the traffic for chat we add new rules that introduce a separate rate limiting when traffic is going towards the chat domain --- .../variables.tf | 11 +++ .../govuk-publishing-infrastructure/wafs.tf | 84 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/terraform/deployments/govuk-publishing-infrastructure/variables.tf b/terraform/deployments/govuk-publishing-infrastructure/variables.tf index 7c3a09961..2b7a5d300 100644 --- a/terraform/deployments/govuk-publishing-infrastructure/variables.tf +++ b/terraform/deployments/govuk-publishing-infrastructure/variables.tf @@ -64,6 +64,11 @@ variable "search_api_domain" { description = "The domain name of the API gateway" } +variable "publishing_service_domain" { + type = string + description = "FQDN of the user-facing domain for the publishing apps, e.g. staging.publishing.service.gov.uk. This domain is included as a wildcard SAN on the TLS cert for Ingresses etc." +} + variable "publishing_certificate_arn" { type = string description = "The ARN of the publishing certificate" @@ -139,6 +144,12 @@ variable "backend_public_base_rate_limit" { default = 1000 } +variable "chat_domain_base_rate_limit" { + type = number + description = "An enforced rate limit threshold for the chat domain" + default = 75000 +} + variable "backend_public_ja3_denylist" { type = list(string) description = "For the backend ALB. List of JA3 signatures for which we should block all requests." diff --git a/terraform/deployments/govuk-publishing-infrastructure/wafs.tf b/terraform/deployments/govuk-publishing-infrastructure/wafs.tf index f7ff846f7..e98996f6f 100644 --- a/terraform/deployments/govuk-publishing-infrastructure/wafs.tf +++ b/terraform/deployments/govuk-publishing-infrastructure/wafs.tf @@ -217,6 +217,90 @@ resource "aws_wafv2_web_acl" "backend_public" { } } + rule { + name = "chat-domain-rate-limit" + priority = 25 + + 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 = "backend-public-rule-429" + } + } + } + + statement { + rate_based_statement { + limit = var.chat_domain_base_rate_limit + aggregate_key_type = "IP" + + scope_down_statement { + byte_match_statement { + search_string = "chat.${var.publishing_service_domain}" + field_to_match { + single_header { + name = "host" + } + } + positional_constraint = "EXACTLY" + text_transformation { + priority = 0 + type = "LOWERCASE" + } + } + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "chat-domain-rate-limit" + sampled_requests_enabled = true + } + } + + rule { + name = "chat-domain-allow" + priority = 26 + + action { + allow {} + } + + statement { + byte_match_statement { + search_string = "chat.${var.publishing_service_domain}" + field_to_match { + single_header { + name = "host" + } + } + positional_constraint = "EXACTLY" + text_transformation { + priority = 0 + type = "LOWERCASE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "chat-domain-allow" + 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