I love Cloudflare. I use them on this blog, I’ve used them for all of my domains, side-projects and in every single company I’ve worked for. I think they are an amazing company doing the internet a great service, although with some controversy about centralising the world traffic.
You think the same and wish to funnel all of your visitors traffic through them.
Easy enough, spin up your EC2 instances, preferably behind an AWS ALB
(Application Load Balancer), grab the public DNS name and put it in a CNAME
record on Cloudflare.
While this might be enough, it does not prevent anyone from reaching your website directly, skipping the mighty CDN, by just using your public DNS name or IP address.
Thank goodness, Cloudflare maintains a list of their reverse proxy IP addresses. You can even grab them in text format for IPv4 and IPv6.
Securing the infrastructure
For this guide we’ll use Terraform, as always, but you can follow along and use whatever tool you like, or even work directly on the AWS Console (👀).
We are going to create a security group exclusively allowing inbound traffic from those IP addresses.
resource "aws_security_group" "alb" {
name = "alb"
description = "Accepts ingress traffic from Cloudflare"
vpc_id = aws_vpc.main.id
ingress {
description = "Cloudflare traffic"
protocol = "tcp"
cidr_blocks = var.cloudflare_ipv4
ipv6_cidr_blocks = var.cloudflare_ipv6
# Using "Flexible" SSL? Change to port 80.
from_port = 443
to_port = 443
}
}
variable "cloudflare_ipv4" {
type = list(string)
}
variable "cloudflare_ipv6" {
type = list(string)
}
In a tfvars
file we can then keep the actual list:
cloudflare_ipv4 = [
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22"
]
cloudflare_ipv6 = [
"2400:cb00::/32",
"2606:4700::/32",
"2803:f800::/32",
"2405:b500::/32",
"2405:8100::/32",
"2a06:98c0::/29",
"2c0f:f248::/32"
]
Reducing maintenance
The above IPs don’t change much. In my experience Cloudflare has very rarely changed their IPs. And even when that is the case, no urgent action is strictly necessary.
Still, removed IPs might be recycled by other companies, potentially becoming an attack vector (to be honest, we’re being extremely paranoid here…).
Anyhow, having to check their IP list every now and then and making sure our tfvars file is in sync is a manual task. And we, the DevOps people, don’t like manual operations.
Thankfully, Cloudflare publishes it’s own Terraform provider, which can be used alongside AWS.
First of all, add the Cloudflare Terraform provider:
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 3.0"
}
}
}
Define a blank cloudflare_ip_ranges
data block:
data "cloudflare_ip_ranges" "cloudflare" {
}
You can now query this data object when defining your security group, like this:
resource "aws_security_group" "alb" {
name = "alb"
description = "Accepts ingress traffic from Cloudflare"
vpc_id = aws_vpc.main.id
ingress {
description = "Cloudflare traffic"
protocol = "tcp"
cidr_blocks = data.cloudflare_ip_ranges.cloudflare.ipv4_cidr_blocks
ipv6_cidr_blocks = data.cloudflare_ip_ranges.cloudflare.ipv6_cidr_blocks
# Using "Flexible" SSL? Change to port 80.
from_port = 443
to_port = 443
}
}
And that’s all, less variables, less static, manually managed list, more automation and magic unicorns!