AWS EC2 Instance No Public IP Associated Audit: SOC2 Compliance

This runbook checks all EC2 instances in an AWS environment to confirm they do not have public IP addresses. This audit is key to SOC2 compliance, aiming to protect against unauthorized access and minimize cyber threats. Its goal is to ensure that EC2 instances are secured within private networks, aligning with SOC2's focus on system security and integrity.

region_name=None #Hardcoded for single execution result, Use None when you want to run the script for all regions.
copied
  1. 1

    Amazon Elastic Compute Cloud (EC2) is a service offered by Amazon Web Services (AWS) that provides resizable compute capacity in the cloud. Through Boto3's EC2 client, the describe_instances() method provides detailed information about each instance, including its ID, type, launch time, and current state. This capability assists users in effectively monitoring and managing their cloud resources.

    import boto3 from botocore.exceptions import NoCredentialsError, PartialCredentialsError, BotoCoreError, ClientError creds = _get_creds(cred_label)['creds'] access_key = creds['username'] secret_key = creds['password'] def list_all_regions(): ec2 = boto3.client('ec2',aws_access_key_id=access_key,aws_secret_access_key=secret_key, region_name = 'us-east-1') return [region['RegionName'] for region in ec2.describe_regions()['Regions']] def list_ec2_instances(region=None): # If no region is provided, fetch instances from all regions regions = [region] if region else list_all_regions() # Create an empty list to store instance details instance_details = [] for region in regions: # Try initializing the Boto3 EC2 client for the specific region try: ec2_client = boto3.client('ec2', aws_access_key_id=access_key,aws_secret_access_key=secret_key,region_name=region) except (NoCredentialsError, PartialCredentialsError): print(f"Failed for {region}: No AWS credentials found or incomplete credentials provided.") continue except BotoCoreError as e: print(f"Failed for {region}: Error initializing the EC2 client due to BotoCore Error: {e}") continue except Exception as e: print(f"Failed for {region}: Unexpected error initializing the EC2 client: {e}") continue #print(f"Fetching EC2 instance details for region: {region}...") # Try to paginate through the EC2 instance responses for the specific region try: paginator = ec2_client.get_paginator('describe_instances') for page in paginator.paginate(): for reservation in page['Reservations']: for instance in reservation['Instances']: # Extract the desired attributes instance_id = instance['InstanceId'] instance_type = instance['InstanceType'] launch_time = instance['LaunchTime'] state = instance['State']['Name'] # Append the details to the list instance_details.append({ 'InstanceId': instance_id, 'InstanceType': instance_type, 'LaunchTime': launch_time, 'State': state, 'Region': region }) #print(f"Fetched all instance details for region: {region} successfully!") except ClientError as e: print(f"Failed for {region}: AWS Client Error while fetching EC2 instance details: {e}") except Exception as e: print(f"Failed for {region}: Unexpected error while fetching EC2 instance details: {e}") return instance_details def display_instance_details(data): # Initialize table with the desired structure and headers table = context.newtable() table.title = "EC2 Instance Details" table.num_cols = 5 # Number of columns according to headers table.num_rows = 1 # Starts with one row for headers table.has_header_row = True # Define header names based on the new structure headers = ["Instance ID", "Instance Type", "Launch Time", "State", "Region"] # Set headers in the first row for col_num, header in enumerate(headers): table.setval(0, col_num, header) # Sort the instance data by launch time for better organization data.sort(key=lambda x: x["LaunchTime"], reverse=True) # Populate the table with instance data for row_num, instance in enumerate(data, start=1): # Starting from the second row table.num_rows += 1 # Add a row for each instance values = [ instance["InstanceId"], instance["InstanceType"], instance["LaunchTime"].strftime('%Y-%m-%d %H:%M:%S'), # Format the datetime instance["State"], instance["Region"] ] for col_num, value in enumerate(values): table.setval(row_num, col_num, value) # You can replace None with a specific region string like 'us-east-1' to get instances from a specific region # Hardcoded region_name for One time Execution Result region_name=None instances_list = list_ec2_instances(region_name) if instances_list: ''' print("\nEC2 Instance Details:") for instance in instances_list: print("-" * 50) # Separator line for key, value in instance.items(): print(f"{key}: {value}")''' display_instance_details(instances_list) else: print("No instances found or an error occurred.")
    copied
    1
  2. 2

    This task is focused on identifying EC2 instances in an AWS environment that are assigned public IP addresses. It plays a crucial role in maintaining SOC2 compliance by identifying potential security risks associated with public internet exposure.

    import boto3 from botocore.exceptions import ClientError, BotoCoreError creds = _get_creds(cred_label)['creds'] access_key = creds['username'] secret_key = creds['password'] def check_public_ip(ec2_client, region): """ Check EC2 instances in a region for public IPs and categorize them as compliant or non-compliant. :param ec2_client: The boto3 EC2 client. :param region: The AWS region to check. :return: A compliance report dictionary. """ compliance_report = {'compliant': [], 'non_compliant': []} try: # Retrieve all instances in the specified region response = ec2_client.describe_instances() # Iterate over each instance to check for public IP for reservation in response.get('Reservations', []): for instance in reservation.get('Instances', []): instance_id = instance.get('InstanceId') public_ip = instance.get('PublicIpAddress') # Categorize based on public IP presence if public_ip: compliance_report['non_compliant'].append({'InstanceId': instance_id, 'Region': region, 'PublicIP': public_ip}) else: compliance_report['compliant'].append({'InstanceId': instance_id, 'Region': region}) except ClientError as e: print(f"ClientError checking instances in region {region}: {e}") except BotoCoreError as e: print(f"BotoCoreError occurred: {e}") except Exception as e: print(f"Unexpected error occurred in region {region}: {e}") return compliance_report def evaluate_ec2_instances(region_name=None): """ Evaluate EC2 instances for public IP compliance in a specific region or all regions. :param region_name: Specific region name or None for all regions. :return: A global compliance report dictionary. """ global_compliance_report = {'compliant': [], 'non_compliant': []} try: ec2_client = boto3.client('ec2',aws_access_key_id=access_key,aws_secret_access_key=secret_key, region_name='us-east-1') # Determine regions to check regions = [region_name] if region_name else [region['RegionName'] for region in ec2_client.describe_regions()['Regions']] # Check each region for public IP compliance for region in regions: print(f"Checking instances in region: {region}") ec2_region_client = boto3.client('ec2',aws_access_key_id=access_key,aws_secret_access_key=secret_key, region_name=region) region_compliance_report = check_public_ip(ec2_region_client, region) # Aggregate results global_compliance_report['compliant'].extend(region_compliance_report['compliant']) global_compliance_report['non_compliant'].extend(region_compliance_report['non_compliant']) except ClientError as e: print(f"ClientError while evaluating EC2 instances: {e}") except BotoCoreError as e: print(f"BotoCoreError occurred: {e}") except Exception as e: print(f"Unexpected error occurred: {e}") return global_compliance_report # Example usage #region_name = None #'ap-south-1' # Specify a region or set to None for all regions compliance_report = evaluate_ec2_instances(region_name) # Display compliance summary print("\nCompliance Summary:") # Print details for compliant and non-compliant instances if compliance_report['compliant']: print("\nCompliant Instances:") for instance in compliance_report['compliant']: print(f"InstanceId: {instance['InstanceId']}, Region: {instance['Region']}") else: print("\nNo Compliant Instances Found.") if compliance_report['non_compliant']: print("\nNon-Compliant Instances (with Public IP):") for instance in compliance_report['non_compliant']: print(f"InstanceId: {instance['InstanceId']} \nRegion: {instance['Region']} \nPublic IP: {instance['PublicIP']}\n" + "-"*40) else: print("\nNo Non-Compliant Instances Found.") context.skip_sub_tasks=True
    copied
    2