Modifying EC2 security groups via AWS Lambda functions

Grig Gheorghiu
Jan 11, 2018 · 2 min read

One task that comes up again and again is adding, removing or updating source CIDR blocks in various security groups in an EC2 infrastructure. This can be automated either fully or partially with the help of simple AWS Lambda functions.

Example 1: Checking a Dynamic DNS IP and replacing it in an EC2 security group

This scenario arises when you have a user without a static IP. They can still get a Dynamic DNS name and have it automatically point to their local dynamic IP. You can check for that name periodically, and update the appropriate rules within EC2 security group(s).

Here is an AWS Lambda function named UpdateSecurityGroupWithHomeIP and written in Python 2.7 that achieves this goal:

A few observations:

  • it’s not trivial to do DNS lookups within Lambda, so I preferred to do the DNS lookup in the caller, and pass the resulting IP address as the sole argument to the above Lambda function — which is retrieved as new_ip_address in the lambda_handler function
  • in the update_security_group function I iterate through all permission objects in the IpPermissions list associated to the given security group and I create a deep copy of every permission
  • if any IP range in a permission object has the description “My Home IP”, I change its CidrIp property to the CIDR block corresponding to new_ip_address
  • finally, I revoke the old permission and authorize the new (deep-copied) permission

This Lambda function needs the proper permissions to modify security groups in EC2. I associated it with an IAM role which allows that. Here is the policy associated with that role:

{
"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"
],
"Resource": "*"
}
]
}

I call this Lambda function from a Jenkins job set to run periodically which does the DNS lookup first, then calls the above AWS Lambda function:

IPADDRESS=`dig my.homeip.example.com  | grep IN | grep -v ';' | awk '{print $5}'`aws lambda invoke \
--invocation-type RequestResponse \
--function-name UpdateSecurityGroupWithHomeIP \
--region us-west-2 \
--log-type Tail \
--payload "{\"ip\":\"$IPADDRESS\"}" \
outputfile.txt

Example 2: adding a new IP/CIDR block to a several security groups

This is useful when you have several security groups and you need to add a new source CIDR block to all of them.

Here is a Lambda function for this purpose:

This function acts on security groups tagged “web” and “ssh”. For the ones tagged “web”, it adds new rules allowing the IP/CIDR block access to ports 80 and 443. For the groups tagged “ssh”, it does the same but for port 22.

The input for this function is {“ip1”: “$IPAddressBlock”} where IPAddressBlock is a Jenkins parameter that the user specifies when running the appropriate Jenkins job. In this case, I used the AWS Lambda Invocation build step in Jenkins.

Grig Gheorghiu

Written by

Head of DevOps at Reaction Commerce. System infrastructure scaling, cloud computing, Golang and Python programming, automated testing.