Sweat the small stuff! Script that itch you can’t scratch…
In the case of Amazon Web Services, access to Elastic Cloud Compute (EC2) instances is controlled through the configuration of security groups. You configure a security group with one or more ingress and egress traffic rules, indicating what traffic is allowed over which ports, from which IP addresses or subnets. Those security groups are applied to EC2 instances, and that’s how traffic to, from, or between you, those instances, and other AWS resources gets defined.
You can set up a security group initially to allow access - say, over port 22 for SSH sessions - from your home device. At the moment, AWS will conveniently determine your device’s external IP address, and set up the access you desire.
This helps in the near-term, but needs to be updated as soon as your internet service provider (ISP) changes your IP address. While spending all of 5-10 minutes accessing your AWS console to manually update security groups may not sound so bad, it would be nice if an automated solution to that problem was possible.
import sys, json, requests try: import boto3 from botocore.exceptions import ClientError except ImportError: sys.exit("Could not import 'boto3', please install this package.") if(len(sys.argv) != 2): sys.exit("USAGE: python ipme.py [security-group-id]") else: group_id = str(sys.argv[1]) client = boto3.client('ec2') try: response = client.describe_security_groups( GroupIds=[ group_id, ], DryRun=False ) except ClientError as e: sys.exit(e) except Exception as ex: sys.exit(ex) else: if response['SecurityGroups']: info = response['SecurityGroups'][0] old_access = response['SecurityGroups'][0]['IpPermissions'][0] print('Found security group (id={0[GroupId]}) named {0[GroupName]}'.format(info)) else: sys.exit('Nothing found matching that criteria (SSH rule).')
The former outlines the start of a script that accepts a single parameter, the group id for the security group you’d like to update regularly. For our purposes, we would work with whatever security group was configured already, using the home IP address at the time it was configured, and pass the group ID of that security group into our script. Let’s work with a security group with ID sg-6d3c3c26 that configures SSH access from 1.2.3.4.
... # find security group by that id try: response = client.describe_security_groups( Filters=[ # only concerned with ssh { 'Name': 'ip-permission.from-port', 'Values': [ '22' ] }, { 'Name': 'ip-permission.to-port', 'Values': [ '22' ] }, { 'Name': 'ip-permission.protocol', 'Values': [ 'tcp' ] }, ], GroupIds=[ group_id, ], DryRun=False ) # throw error or exception, whatever comes first except ClientError as e: sys.exit(e) except Exception as ex: sys.exit(ex) else: if response['SecurityGroups']: info = response['SecurityGroups'][0] old_access = response['SecurityGroups'][0]['IpPermissions'][0] print('Found security group (id={0[GroupId]}) named {0[GroupName]}'.format(info)) else: sys.exit('Nothing found matching that criteria (SSH rule).')
This ensures that, even if a security group ID for a group that does not define traffic over port 22 is passed in, no action will be taken, as the call to AWS will fail to find a security group matching the criteria defined in the Filters argument.
At this point, we have two tasks left. Rather than update the existing ingress traffic rule in place, it’s easier to just revoke that ingress traffic and then add a new rule later, with the same from- and to-port, protocol, and our updated IP address. We’re thinking ahead here, and storing the object representing the current ingress rule from our response in a variable old_access. We’ll use that object in a call soon to remove access over port 22 from our old IP address.
Here’s the code we’ll use to determine our new external IP address, revoke access over port 22 from our old IP address, and authorize access over port 22 with the new one:
... ext_ip = requests.get('https://checkip.amazonaws.com/') ext_ip = ext_ip.text.strip() + '/32' try: data = client.revoke_security_group_ingress( GroupId=group_id, IpPermissions=[ old_access ] ) data = client.authorize_security_group_ingress( GroupId=group_id, IpPermissions=[ { 'IpProtocol': 'tcp', 'FromPort': 22, 'ToPort': 22, 'IpRanges': [{'CidrIp': ext_ip}] } ] ) except ClientError as e: sys.exit(e) except Exception as ex: sys.exit(ex) else: print('Ingress Successfully Set %s' % data)
All told, our script can find the security group we defined, and update it with our current external IP address. Note the first octet of the Source IP address updating:
The final version of this script can be found here:
Return homeTags: