Scenario:

You have provisioned AWS CloudFront which sends your site traffic to internal classic ELB. A security group is attached with the ELB (in private subnet) which allows Inbound and Outbound traffic based on rules defined. You have opened Inbound traffic for all (0.0.0.0/0) as CloudFront do not provide any endpoint link and IPs can’t be added as they keep changing.

Keeping the security group open for (0.0.0.0/0) makes it less secure as anybody can start sending malicious messages directly to ELB bypassing CloudFront.

In this article you will see how to build an event-driven, zero-infrastructure solution using a Lambda function that is triggered in response to the SNS notification and updates security group with CloudFront CIDR ranges on required port (say 443)

 

Solution:

 

>>>Create security group to attach with ELB to control the Inbound access rules.

Add three tags (as shown in screen shot) that the Lambda function will use to identify security groups it needs to update.

 

>>>> Create an IAM policy and execution role for the Lambda function

Create IAM policy using this json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeSecurityGroups",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:CreateNetworkInterface",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DeleteNetworkInterface"
            ],
            "Resource": "*"
        }
    ]
}

The first statement allows the Lambda function to write to Amazon CloudWatch logs, which is vital for debugging and monitoring our function. The second statement allows the function to get information about existing security groups and to authorize and revoke ingress permissions. It also allows to create network interface in private subnet in VPC configuration. The Lambda function will be created in a VPC and in private subnets to access resources (ELB/SG).

After creating policy, create Lambda execution role using that policy:

In the IAM console, click Roles > Create New Role, and then name the role.
To select a role type, select AWS Service Roles > AWS Lambda.
Attach the policy you just created.
After confirming your selections, click Create Role.

 

>>>> Create Lambda function

 

To add Lambda function code ..get it from here

Make changes to this code to make it work

#update the port to add/revoke

–>  INGRESS_PORTS = { ‘https’: 443 }

#you can set your own tag and update here

–> GLOBAL_SG_TAGS = { ‘Name’: ‘Update_CloudFront_IPs’, ‘AutoUpdate’: ‘true’ }

–> REGION_SG_TAGS = { ‘Name’: ‘Update_CloudFront_IPs’, ‘AutoUpdate’: ‘true’ }

#set your own region

–> REGION= os.getenv( ‘REGION’,”ap-southeast-2″)

 

Set timeout to 10 seconds.

 

Test your Lambda

–> Confirm that the 3 tags are added to the Security Group of ELB where the CloudFront IPs need to be added as rule.

–> Enter the following test event, which will represent an SNS notification-

{
  "Records": [
    {
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:EXAMPLE",
      "EventSource": "aws:sns",
      "Sns": {
        "SignatureVersion": "1",
        "Timestamp": "1970-01-01T00:00:00.000Z",
        "Signature": "EXAMPLE",
        "SigningCertUrl": "EXAMPLE",
        "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
        "Message": "{\"create-time\": \"yyyy-mm-ddThh:mm:ss+00:00\", \"synctoken\": \"0123456789\", \"md5\": \"7fd59f5c7f5cf643036cbd4443ad3e4b\", \"url\": \"https://ip-ranges.amazonaws.com/ip-ranges.json\"}",
        "Type": "Notification",
        "UnsubscribeUrl": "EXAMPLE",
        "TopicArn": "arn:aws:sns:EXAMPLE",
        "Subject": "TestInvoke"
      }
    }
  ]
}

 

 

 

Click Test to invoke Lambda function. It will show error like below-

Edit the sample event again, and this time change the md5 hash to be the first highlighted hash provided in the log output. In this example, we would update the sample event with the hash “8b1611c8657179ffa1361d46b2d62798”.
Click Save and test, and your Lambda function will be invoked.

 

 

 

Configure Lambda Function’s Trigger

Once test of Lambda function passes, add trigger to the lambda function so that it subscribes to SNS topic to get notified for any changes to CloudFront CIDR range IPs.

Click on Add trigger

 

 

You can also create Alarm notification through email to notify you whenever lambda is invoked.