Auto Update Security Groups with Cloudflare IPs

Auto Update Security Groups with Cloudflare IPs

When leveraging Cloudflare to help protect your site from malicious actors you’ll need to allow Cloudflare to send visitor requests to your origin. This is achieved by allowing Cloudflare IP addresses.

This can be done at the time you deploy your infrastructure. However, unless you are deploying frequently the IP list will soon be outdated and obsolete.

The Module

The below Terraform module will create a Lambda function which by default runs once a day. The lambda searches for any Security Groups with a tag key of CF-AutoUpdate and a value of true. For each found security group it looks for a CF-Ports tag. The value should be a comma separated list of ports used by the origin that requires Cloudflare access. For each port in the list an ingress rule is created for all of the known ipv4 public coudflare IPs. If no CF-Ports tag is found port 443 is used.

module "cloudflare-sg-updater" {
    source  = "coresolutions-ltd/cloudflare-sg-updater/aws"
}

More information on the above module including the various inputs can be found on the Terraform Registry.

The Lambda

The lambda is fairly simple, it uses the boto3 ec2 resource. Where as the client provides a low-level interface that maps close to the AWS service APIs. Resources provide a higher-level abstraction than the calls made by service clients.

ec2 = boto3.resource('ec2')

We need to search for all security groups for the CF-AutoUpdate tag. This is done using security_groups.filter(). This creates an iterable of all SecurityGroup resources in the collection filtered by kwargs passed to method.

groups = ec2.security_groups.filter(
        Filters=[
            { 'Name': 'tag:CF-AutoUpdate', 'Values': [ 'true' ] },
        ]
    )

For each found Security Group we need to iterate through all of the associated tags looking for CF-Ports. If it’s not present we need to default to 443.

ports = next((tag for tag in security_group.tags if tag["Key"] == "CF-Ports"), [443])

If the CF-Ports tag is present we use the value to create a list of ports.

ports = [int(tag) for tag in ports['Value'].split(",")]

We have a list of Security Groups to update and a list of ports required for each Security group. We can pull down the latest Cloudflare IPs and compare these with the current ingress rules.

The current ingress rules are obtained via the ip_permissions attribute.

current_rules = group.ip_permissions

For each rule we need to add or remove we use the authorize_ingress() and revoke_ingress() actions.

group.authorize_ingress(IpProtocol="tcp", CidrIp=address, FromPort=port, ToPort=port)

Conclusion

There are multi ways to auto update Security Groups with Cloudflare IPs. This however is my preferred approach, it allows each security group to independently control which ports are required. It also allows us to have a single Lambda function which can update multi security groups. Security groups can then toggle the auto update functionality on and off via the CF-AutoUpdate tag apposed to having a single lambda for each security group.

 

No Comments

Add your comment