Restricting ELB access to Cloudfront

If you’ve ever worked in a corporate environment where the infrastructure is deployed on AWS, you should have dealt with resources protected behind VPC and sophisticated security groups. Normally we want only specific IP addresses to be able to access to our ELB to reduce the attack surface and block access from public Internet.

Many times it is a good idea to put a Cloudfront distribution in front of your ELB and exposing only the Cloudfront distribution to public Internet. In a scenario, we wanted to apply this principle in order to leverage different caching rules, apply WAF rules for more effective security and improve the performance for clients from far locations. However it was not easy to block access to ELB except Cloudfront because it is impossible to know the IP address space of Cloudfront. So how to set up a security group to allow incoming traffic only from Cloudfront?

Fortunately there is a way to do that because AWS is publishing the IP addresses for different services in JSON format. You can see this file from here. So you could write a script which downloads and parses this file and create a security group using that. Right. But there are two caveats about that:

  1. Where to run this script?
  2. How to execute this script when IP address ranges change.

A cronjob to check every minute the IP address space? No, there is a better way.

You can write a Lambda function that downloads and parses this file. Moreover, as you can see here, AWS has a dedicated SNS topic you can subscribe, and this topic will notify you whenever there is a change in IP address space.

So we can write a Lambda function using Node.js runtime and it could look like this:

This function tries to subscribe itself to arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged whenever it is executed. For sake of simplicity, it tries to subscribe itself even it already subscribed. When subscription occurs, it downloads the JSON file, parse it, extract Cloudfront IP’s from it and creates a security group in the region and VPC you gave in code.

I share the code just for explanation. We do not have to create the Lambda function manually. Here I’ve prepared a Cloudformation template that does the trick:

What you have to do is to upload this template to your account, but to us-east-1 region. The reason for that is the SNS topic is on that region. (The template will anyway check where it is being installed and will not let you install to another region.) The template takes two parameters, one is the region your ELB is deployed and the Id of the VPC your ELB is in. When this template is deployed for the first time, the Lambda will be executed once and create the security group on your account, in the specified region. It will also subscribe to future changes via SNS, so you can make sure that your security group is always up-to-date.

Of course as a last step, you have to add this security group to your ELB’s allowed groups and voilà.