Security
Auto Update Security Groups with Cloudflare IPs
How to auto update security groups with Cloudflare IPs using AWS Lambda
When you use Cloudflare to protect a website, the origin still needs to allow Cloudflare IP addresses through to the application. In AWS that usually means allowing those IP ranges in the relevant security groups.
The challenge is that Cloudflare’s IP list can change over time. If the list is baked into an infrastructure deployment and never refreshed, the security groups can become stale and block legitimate traffic. This article walks through one way to automate that update process with AWS Lambda and OpenTofu or Terraform.
Solution Overview
The pattern is straightforward. A Lambda function runs on a schedule, fetches the latest Cloudflare IP ranges, and updates tagged security groups automatically.
The Lambda function:
- finds security groups tagged with
CF-AutoUpdate = true - looks for a
CF-Portstag containing a comma-separated list of ports - updates ingress rules to allow the latest Cloudflare IPv4 ranges on those ports
- defaults to port
443if noCF-Portstag is present
This keeps the control per security group while centralising the automation.
OpenTofu/Terraform Module Usage
You can package the setup in a reusable module:
module "cloudflare-sg-updater" {
source = "coresolutions-ltd/cloudflare-sg-updater/aws"
}
That module can create the Lambda function, IAM role, and scheduled trigger needed to keep the rules current.
How the Lambda Function Works
The Lambda function can be written in Python with boto3.
Importing the EC2 Resource
import boto3
ec2 = boto3.resource('ec2')
Using the higher-level resource interface keeps the implementation relatively clean.
Filtering Security Groups by Tags
groups = ec2.security_groups.filter(
Filters=[
{'Name': 'tag:CF-AutoUpdate', 'Values': ['true']},
]
)
For each matching security group, read CF-Ports and convert the value into a list of ports. If the tag is missing, fall back to 443.
for security_group in groups:
ports_tag = next((tag for tag in security_group.tags if tag["Key"] == "CF-Ports"), None)
if ports_tag:
ports = [int(port.strip()) for port in ports_tag['Value'].split(",")]
else:
ports = [443]
Fetching Cloudflare IP Addresses
import requests
response = requests.get('https://www.cloudflare.com/ips-v4')
cloudflare_ips = response.text.splitlines()
If you also need IPv6 support, the same pattern can be extended to ips-v6.
Adding Missing Rules
for port in ports:
for ip in cloudflare_ips:
if not rule_exists(current_rules, ip, port):
security_group.authorize_ingress(
IpProtocol='tcp',
CidrIp=ip,
FromPort=port,
ToPort=port
)
Removing Obsolete Rules
for port in ports:
for ip in current_cloudflare_rules:
if ip not in cloudflare_ips:
security_group.revoke_ingress(
IpProtocol='tcp',
CidrIp=ip,
FromPort=port,
ToPort=port
)
That keeps the rules aligned to the latest published Cloudflare ranges instead of only ever appending to them.
Conclusion
Automating AWS security group updates for Cloudflare IP ranges helps keep the origin secure and reachable without relying on manual maintenance. With a small Lambda function and a few tags, each security group can control its own ports while still benefiting from a single shared automation path.