Restricting ELB access to Cloudfront

Update 02/18/2018: I’ve upgraded the Javascript file to ES6 and promises, updated the Cloudformation template to YAML for better readability, and improved the script to handle Cloudformation template deletion.

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 trigger this script whenever AWS adss or removes new IP address ranges?

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.

We can leverage Cloudformation Custom Resources to create a Lambda backed custom resource that will manage a special security group. Below you can see the Javascript file we will use for this custom resource:

This function will execute in two different cases:

  1. When Cloudformation template is installed, to create the security group.
  2. Whenever arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged SNS topic triggers it. In every run, it will subscribe itself to that SNS topic.

Whenever the function runs, it will download the JSON file, parse it, extract Cloudfront IP’s from it and updates the security group which it created in the region and VPC you gave in code.

You do not have to create the Lambda function above manually. Here I’ve prepared a Cloudformation template that includes the code and installs everything for you:

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. You can use the following command to install the template easily:

aws cloudformation create-stack — region=us-east-1 — capabilities=CAPABILITY_IAM — template-body file://${PWD}/template.yaml — stack-name cloudfront-security-group — parameters ParameterKey=ResourceRegion,ParameterValue=eu-west-1 ParameterKey=ResourceVpcId,ParameterValue=vpc-6a0e7a0d

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

If you delete the Cloudformation template, Lambda function will handle this situation and automatically remove the Security Group it created.

If you liked the post, please use the like button to show it :)