Keep your AWS EC2 Security Group Updated with a Dynamic IP

If you are like the majority of AWS server administrators, your home ISP does not provide you with a static IP address. Which means every time your IP address changes you have to log back into the AWS Management Console, Shoot over to the EC2 Service, Find your IP, and update the policy in your Security Group. This isn’t the end of the world, but it is mildly annoying depending on how frequently your IP address changes. However, when you have members of your team that do not have the knowledge (or permissions, usually because of the former) to make this change, and they need access at all hours of the night, this very quickly gets upgraded to a high level priority. Fortunately, I’ve done the hard part for you.

Prerequisites

Before we continue, let’s assume the following. If this isn’t true, stop right here and get these tasks completed first.

  • You have installed the Amazon SDK for PHP.
  • You have properly configured the necessary access and secret keys in your /.aws/credentials file.
  • You have some sort of dynamic DNS service with a hostname from a provider like DynDNS, No-Ip, etc.
  • You have a basic understanding of PHP

The Code

What we have is a single file, you can name it anything you like as long as it has a .php extension, let’s just call it ec2Security.php. When run, the code below will first check to see if the IP address which resolves to the dynamic DNS hostname(s) provided in the $dynDNS array. If the IP address is not found, it will be added with the ports specified (in the example below we are allowing port 22, and a range of 10000-10100 for SSH communication), if the IP address is found then nothing happens. Copy the code below and be sure to replace all of the generic or XXXXX’d out variables with your own information.

<?php
/**
* Update AWS Security Groups with my teams IP addresses
* if they are not already in the AWS security group policy.
*
* @param author Arthur Guy
* @param website http://www.ajginteractive.com
* @param create-date 2018-05-09
* @param modify-date 2018-05-09
*/
 
date_default_timezone_set('America/Los_Angeles');
 
//Init the AWS SDK
require '/path/to/aws/sdk/aws-autoloader.php';
 
//Require the AWS Libraries
use Aws\Ec2\Ec2Client;
 
//DynDNS Hosts
$dynDNS['name1'] = "host1.ddns.net"; //Name is not relevant, it's just a descriptive name for your own identification. The value of each array element is the URL to your Dynamic DNS hostname.
$dynDNS['name2'] = "host2.ddns.net";
$dynDNS['name3'] = "host3.ddns.net";
 
 
//Clients and Regions
$client['client1']['profile'] = "default"; //Corresponds to the credentials file
$client['client1']['regions'] = array('us-west-1', 'us-east-2');
 
$client['client2']['profile'] = "XXXXXXXX"; //If you don't need to manage multiple AWS accounts, you can remove this index
$client['client2']['regions'] = array('us-west-1', 'us-west-2');
 
//Loop through the array
foreach ($client as $key => $val){
 
//Set Profile
$profile = $val['profile'];
 
//Loop through the regions
foreach ($val['regions'] as $k => $v){
 
$ec2Client = new Ec2Client([
'region' => $v,
'version' => '2016-11-15',
'profile' => $profile
]);
 
//Describe the Policy
$result = $ec2Client->describeSecurityGroups(array(
'GroupNames' => ['XXXXXXXX'], //The name of the group you want to manage, you can also sub this out for the GroupID by changing the key value.
));
 
//Get all of the IP's attached to the policy
foreach ($result['SecurityGroups']['0']['IpPermissions'] as $policy) {
foreach ($policy['IpRanges'] as $listedIP) {
$ipRange[] = explode("/", $listedIP['CidrIp'])['0'];
}
 
}
 
//Sanatize the IP array
$ipListClean = array_unique($ipRange);
 
//Compare the IP addresses against the policy
foreach ($dynDNS as $clientHostName){
$resolvedIP = gethostbyname($clientHostName);
if (!in_array($resolvedIP, $ipListClean)) {
 
//Add this IP to the Policy
$result = $ec2Client->authorizeSecurityGroupIngress(array(
'GroupName' => 'XXXXXXXX', //Same GroupName or GroupID as above on line 50
'IpPermissions' => [
[
'FromPort' => '22', //You can configuire the fromPort and ToPorts to declare a range, this example allows up SSH.
'ToPort' => '22',
'IpProtocol' => 'tcp',
'IpRanges' => [
[
'CidrIp' => $resolvedIP . '/32',
'Description' => $clientHostName . ' ' . date("m-d-Y H:i:s"),
],
],
],
[
'FromPort' => '10000', //Pass multiple values as an array so that all of your policies are defined at once.
'ToPort' => '10100',
'IpProtocol' => 'tcp',
'IpRanges' => [
[
'CidrIp' => $resolvedIP . '/32',
'Description' => $clientHostName . ' ' . date("m-d-Y H:i:s"),
],
],
],
],
));
 
if ($profile == "client2"){ //Want to make an additional change to only a specific group, do it here. If not, you can omit this section entirely.
$result = $ec2Client->authorizeSecurityGroupIngress(array(
'GroupName' => 'XXXXXXXXXXXXX',
'IpPermissions' => [
[
'FromPort' => '80',
'ToPort' => '80',
'IpProtocol' => 'tcp',
'IpRanges' => [
[
'CidrIp' => $resolvedIP . '/32',
'Description' => $clientHostName . ' ' . date("m-d-Y H:i:s"),
],
],
],
[
'FromPort' => '443',
'ToPort' => '443',
'IpProtocol' => 'tcp',
'IpRanges' => [
[
'CidrIp' => $resolvedIP . '/32',
'Description' => $clientHostName . ' ' . date("m-d-Y H:i:s"),
],
],
],
],
));
 
}
 
}
}
 
unset($ipRange);
}
 
}
 
?>

Scheduling The Script

Once the code is up and running, and you have completed a successful test, the last step is to schedule the code to run. You can choose the interval that you feel best, but we like to use a 15 minute interval, which in the crontab, looks something like this. */15 * * * * wget -q -O - http://www.domain.com/path/to/ec2Security.php >>/dev/null 2>&1 .

That’s all there is to it.