Back to blog

Security

Auto Update Security Groups with Cloudflare IPs

How to auto update security groups with Cloudflare IPs using AWS Lambda

January 1, 2020 Platform Engineering 3 min read

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-Ports tag containing a comma-separated list of ports
  • updates ingress rules to allow the latest Cloudflare IPv4 ranges on those ports
  • defaults to port 443 if no CF-Ports tag 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.