Security
Protecting your ALB with WAF & Cloudfront
Learn how to protect your ALB with AWS WAF and CloudFront.
Protecting your Application Load Balancer (ALB) from unwanted traffic is an important part of securing internet-facing applications. While products such as Cloudflare offer feature-rich edge protection, you can also build a strong native AWS solution with CloudFront and AWS WAF.
This post focuses on locking an ALB down so it only accepts the traffic that should be reaching it, while still allowing CloudFront to sit in front as the public edge.
Solution Overview
The solution has three main pieces:
- A CloudFront distribution in front of the ALB
- A CloudFront Web ACL for edge filtering and rate limiting
- An ALB Web ACL that only allows requests with the expected origin token header
Together they create a layered protection model:
- CloudFront handles public edge traffic
- WAF filters traffic both at the edge and at the origin
- the ALB only accepts traffic that matches the expected path into the application
CloudFront Configuration
The first step is to generate an origin token:
resource "random_string" "origin_token" {
length = 30
special = false
}
Then configure CloudFront to send that value to the ALB as a custom header:
resource "aws_cloudfront_distribution" "distribution" {
origin {
domain_name = aws_lb.example.dns_name
origin_id = "alb"
custom_header {
name = "X-Origin-Token"
value = random_string.origin_token.result
}
}
enabled = true
aliases = ["yourdomain.example.com"]
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "alb"
forwarded_values {
query_string = true
headers = ["X-Origin-Token"]
cookies {
forward = "all"
}
}
viewer_protocol_policy = "redirect-to-https"
}
}
Configuring AWS WAF
CloudFront Web ACL
The CloudFront Web ACL is where you can apply managed rule sets, reputation filtering, and rate limiting:
module "cloudfront_waf" {
source = "coresolutions-ltd/wafv2/aws"
name_prefix = "CloudFront"
default_action = "allow"
scope = "CLOUDFRONT"
rate_limit = 1000
managed_rules = [
"AWSManagedRulesCommonRuleSet",
"AWSManagedRulesAmazonIpReputationList",
"AWSManagedRulesAdminProtectionRuleSet",
"AWSManagedRulesKnownBadInputsRuleSet"
]
}
ALB Web ACL
The ALB Web ACL should be stricter. A useful pattern is to block by default and only allow traffic that includes the expected origin token:
module "alb_waf" {
source = "coresolutions-ltd/wafv2/aws"
name_prefix = "ALB"
default_action = "block"
scope = "REGIONAL"
origin_token = random_string.origin_token.result
}
resource "aws_wafv2_web_acl_association" "waf_association" {
resource_arn = aws_lb.example.arn
web_acl_arn = module.alb_waf.waf_arn
}
Configuring the ALB Security Group
As another layer of defence, restrict the ALB security group to CloudFront IP ranges:
data "aws_ip_ranges" "cloudfront" {
services = ["CLOUDFRONT"]
}
resource "aws_security_group" "alb_sg" {
name = "alb_sg"
description = "Allow traffic from CloudFront"
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = data.aws_ip_ranges.cloudfront.cidr_blocks
}
}
Because CloudFront IP ranges change over time, you may also want automation to keep those rules up to date.
Testing the Configuration
To verify the setup:
- Access the application through CloudFront and confirm it works.
- Attempt direct access to the ALB and confirm it is blocked.
- Review WAF logs and metrics to confirm non-matching requests are being denied.
Conclusion
CloudFront and AWS WAF provide a strong AWS-native way to protect an ALB. By combining edge filtering, origin token validation, and security group restrictions, you can make it much harder for unwanted traffic to reach the application directly while keeping the architecture relatively clean.