How to create a security group allowing traffic from CloudFront only?

Andreas Wittig – 07 Mar 2022

It is one of those problems for which there has been no satisfactory solution for years. How do you ensure that only CloudFront is granted access to an Elastic Load Balancer - CLB, ALB, or NLB? Without the ability to restrict incoming traffic, all of CloudFront’s network layer protection does little good. You will learn how to use AWS-managed prefix list for Amazon CloudFront in the following.

How to create a security group allowing traffic from CloudFront only?

Until recently, when using a load balancer or similar endpoint as the origin for a CloudFront distribution, you had to allow incoming HTTPS traffic from anywhere (0.0.0.0/0). At least, there was no simple way to maintain a list with all the IP addresses used by the CloudFront edge locations worldwide. In my opinion, automatically updating a security group by using a Lambda function is nothing I want to run in production.

Therefore, probably many other AWS customers and we ended up with the following situation. The load balancer was accessible not only from CloudFront but from anywhere. This made it possible to bypass CloudFront’s protective measures.

ALB accessible from anywhere

Luckily, AWS announced managed prefix lists for CloudFront on February 7, 2022. The prefix list contains all IP ranges used by CloudFront edge locations. AWS updates the prefix list when needed. You can easily use the prefix list to restrict access when configuring a security group, as shown in the following figure.

This means that CloudFront’s protection measures can no longer be bypassed. It is ensured that all incoming traffic on the load balancer comes from CloudFront.

ALB accessible from CloudFront only

However, keep in mind that anyone can create a CloudFront distribution. This does not guarantee that all the requests arriving at your load balancer originate from your CloudFront distribution. Check out to learn how to restrict access at the application layer.

So how do you create a security group that only allows incoming traffic from CloudFront by using an AWS-managed prefix list? We will present the Terraform and CloudFormation code in the following.

The following snippet shows the Terraform code needed to create a security group that allows incoming HTTPS traffic from CloudFront only. The data source aws_ec2_managed_prefix_list fetches the ID of the prefix list by name.

data "aws_ec2_managed_prefix_list" "cloudfront" {
name = "com.amazonaws.global.cloudfront.origin-facing"
}

resource "aws_security_group" "lb" {
name = "loadbalancer"
vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id
}

resource "aws_security_group_rule" "lb_ingress_cloudfront" {
description = "HTTPS from CloudFront"
security_group_id = aws_security_group.lb.id
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
prefix_list_ids = [data.aws_ec2_managed_prefix_list.cloudfront.id]
}

Unfortunately, things are getting a little more complicated when using CloudFormation. The ID of the prefix list aws_ec2_managed_prefix_list varies between the regions. Luckily, it seems to be the same for all AWS accounts. Therefore, I’m using a mapping between the region and the prefix list for CloudFront in the following snippet.

Mappings:
RegionMap:
'af-south-1':
PrefixListCloudFront: 'pl-c0aa4fa9'
'eu-north-1':
PrefixListCloudFront: 'pl-fab65393'
'ap-south-1':
PrefixListCloudFront: 'pl-9aa247f3'
'eu-west-3':
PrefixListCloudFront: 'pl-75b1541c'
'eu-west-2':
PrefixListCloudFront: 'pl-93a247fa'
'eu-south-1':
PrefixListCloudFront: 'pl-1bbc5972'
'eu-west-1':
PrefixListCloudFront: 'pl-4fa04526'
'ap-northeast-2':
PrefixListCloudFront: 'pl-22a6434b'
'me-south-1':
PrefixListCloudFront: 'pl-17b2577e'
'ap-northeast-1':
PrefixListCloudFront: 'pl-58a04531'
'sa-east-1':
PrefixListCloudFront: 'pl-5da64334'
'ca-central-1':
PrefixListCloudFront: 'pl-38a64351'
'ap-east-1':
PrefixListCloudFront: 'pl-14b2577d'
'ap-southeast-1':
PrefixListCloudFront: 'pl-31a34658'
'ap-southeast-2':
PrefixListCloudFront: 'pl-b8a742d1'
'eu-central-1':
PrefixListCloudFront: 'pl-a3a144ca'
'us-east-1':
PrefixListCloudFront: 'pl-3b927c52'
'us-east-2':
PrefixListCloudFront: 'pl-b6a144df'
'us-west-1':
PrefixListCloudFront: 'pl-4ea04527'
'us-west-2':
PrefixListCloudFront: 'pl-82a045eb'
Resources:
LoadBalancerSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: 'loadbalancer'
VpcId: 'vpc-40e43c28'
LoadBalancerSecurityGroupInWorld:
Type: 'AWS::EC2::SecurityGroupIngress'
Properties:
GroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourcePrefixListId: !FindInMap ['RegionMap', !Ref 'AWS::Region', 'PrefixListCloudFront']

There is one stumbling block to consider. The prefix list is not available in ap-northeast-3 and ap-southeast-3.

I implemented this for one of our consulting clients and our open source project widdix/aws-cf-templates right away. I highly encourage you to do the same.

Andreas Wittig

Andreas Wittig

I’ve been building on AWS since 2012 together with my brother Michael. We are sharing our insights into all things AWS on cloudonaut and have written the book AWS in Action. Besides that, we’re currently working on bucketAV and marbot.

Here are the contact options for feedback and questions.