Skip to content

Commit

Permalink
Add custom domain and WAF for search-api
Browse files Browse the repository at this point in the history
The WAF needs to be applied in a similar way to CloudFront WAFs which requires the domain configuration.

This applies to the API gateway we're setting up, not to internal traffic.

It permits 100 requests per client per 5 minutes, which is 1 every 3 seconds on average. This is probably sufficient for now, though if lots of users are behind a single IP, then this will need amending.
This WAF also applies across the whole deployment, so should we add another API that is likely to be more frequently used, we'll definitely want to relax it and do more management at the API layer.

This also registers the API Gateway with AWS Shield Advanced.
  • Loading branch information
sihugh committed Oct 16, 2024
1 parent c4ab0aa commit 7cfde3d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
resource "aws_api_gateway_domain_name" "search_api_domain" {
domain_name = var.search_api_domain
certificate_arn = var.publishing_certificate_arn
}

# VPC Link to allow API Gateway to connect to the search load balancer
resource "aws_api_gateway_vpc_link" "search_vpc_link" {
name = "search_api_vpc_link"
Expand All @@ -11,7 +16,7 @@ resource "aws_api_gateway_rest_api" "search_rest_api" {
description = "API Gateway for Search API"

endpoint_configuration {
types = ["EDGE"] # "Edge-optimized" routes traffic through CloudFront
types = ["EDGE"] # "Edge-optimized" routes traffic through CloudFront
}
}

Expand Down Expand Up @@ -46,6 +51,61 @@ resource "aws_api_gateway_deployment" "search_deployment" {
stage_name = "v0.1" # Version embedded in the published URL
}

# Map API Gateway stages to custom domain
resource "aws_api_gateway_base_path_mapping" "search_api_mapping" {
domain_name = aws_api_gateway_domain_name.search_api_domain.domain_name
api_id = aws_api_gateway_rest_api.search_api.id
}

# WAF settings
resource "aws_wafv2_web_acl" "search_api_waf" {
name = "search-api-waf"
description = "WAF for Search API with rate limiting"
scope = "CLOUDFRONT"

default_action {
allow {}
}

rule {
name = "rate-limit-rule"
priority = 1
action {
block {}
}

statement {
rate_based_statement {
limit = 100 # Limit 100 requests per IP in 5 minutes
aggregate_key_type = "IP"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "search-api-rate-limit-rule"
sampled_requests_enabled = true
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "search-api-waf"
sampled_requests_enabled = true
}
}

resource "aws_wafv2_web_acl_association" "waf_association" {
resource_arn = aws_api_gateway_domain_name.search_api_domain.cloudfront_domain_name
web_acl_arn = aws_wafv2_web_acl.search_api_waf.arn
}

resource "aws_shield_protection" "search_api_shield" {
name = "search-api-shield"
resource_arn = aws_api_gateway_rest_api.search_api.execution_arn
}


output "api_gateway_cname" {
value = aws_api_gateway_domain_name.search_api_domain.cloudfront_domain_name
description = "CNAME to use in your DNS settings"
Expand Down
10 changes: 10 additions & 0 deletions terraform/deployments/govuk-publishing-infrastructure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ variable "search_api_lb_dns_name" {
type = string
description = "The DNS name of the search-api-v2 load balancer"
}

variable "search_api_domain" {
type = string
description = "The domain name of the API gateway"
}

variable "publishing_certificate_arn" {
type = string
description = "The ARN of the publishing certificate"
}

0 comments on commit 7cfde3d

Please sign in to comment.