AWSIdentifySGPublicExposure

Automation to determine which interface on an EC2 instance has a given public IP and identify associated security groups.

python · Cloud Security Policy Management

Source

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

import traceback


def ec2_instance_info(
    account_id: str, instance_id: str, public_ip: str, region: str, integration_instance: str
) -> tuple[str, list, str]:
    """
    Finds interface with public_ip and from this creates interface ID/SG mapping

    Args:
        account_id (str): AWS Account ID
        instance_id (str): EC2 Instance ID
        public_ip (str): Public IP address of the EC2 instance
        region (str):  AWS Region where the instance is located
        integration_instance (str): The AWS Integration Instance to use

    Returns:
        tuple: A tuple containing:
            - The identified network interface
            - List of its security groups
            - The AWS integration instance used to retrieve the information
    """
    cmd_args: dict[str, str] = {
        "instance_ids": instance_id,
        "region": region,
        "account_id": account_id,
        "using": integration_instance,
    }

    remove_nulls_from_dictionary(cmd_args)

    result = demisto.executeCommand("aws-ec2-instances-describe", cmd_args)

    instance_info = []

    if result and len(result) > 1:
        # If multiple entries were returned, such as when multiple AWS integration instances are configured,
        # Identify the first entry with valid results.
        for entry in result:
            if not isError(entry):
                instance_info = [entry]
                break
        else:
            # If all entries are errors, use the first entry
            instance_info = [result[0]]
    else:
        instance_info = result

    if not instance_info:
        raise DemistoException(
            "Error retrieving instance network interface details with command 'aws-ec2-instances-describe'.\n"
            "Error: No results returned."
        )
    if isError(instance_info):
        raise DemistoException(
            f"Error retrieving instance network interface details with command 'aws-ec2-instances-describe'.\n"
            f"Error: {json.dumps(instance_info[0].get('Contents', ''))}"
        )

    interfaces = dict_safe_get(instance_info, (0, "Contents", "Reservations", 0, "Instances", 0, "NetworkInterfaces"))
    instance_to_use = dict_safe_get(instance_info, (0, "Metadata", "instance"))

    if interfaces:
        for interface in interfaces:
            if interface.get("Association") and interface.get("Association").get("PublicIp") == public_ip:
                group_list = []
                for sg in interface.get("Groups", []):
                    group_list.append(sg.get("GroupId", ""))
                return interface.get("NetworkInterfaceId", ""), group_list, instance_to_use

    # Raise an error if no interface was found with the given public IP
    raise ValueError("Unable to find interface associated with the given public IP address.")


def identify_sgs(args: dict[str, str]) -> CommandResults:
    """
    Main command that determines what EC2 network interface has a given public IP address and lists its security groups.

    Args:
        args (Dict[str, Any]): Demisto.args() object

    Returns:
        CommandResults: Demisto CommandResults object containing:
            - EC2InstanceID: The original EC2 instance ID
            - NetworkInterfaceID: The network interface ID
            - PublicIP: The public IP address
            - SecurityGroups: List of associated security group IDs
    """

    account_id = args["account_id"]
    instance_id = args["instance_id"]
    public_ip = args["public_ip"]
    region = args["region"]
    integration_instance = args.get("integration_instance", "")

    ec2_interface, sg_list, instance_to_use = ec2_instance_info(account_id, instance_id, public_ip, region, integration_instance)

    outputs = {
        "EC2InstanceID": instance_id,
        "NetworkInterfaceID": ec2_interface,
        "PublicIP": public_ip,
        "SecurityGroups": sg_list,
        "IntegrationInstance": instance_to_use,
    }

    readable_output = (
        f"EC2 instance {instance_id} has public IP {public_ip} on ENI {ec2_interface}:\n"
        f"Associated Security Groups: {', '.join(sg_list)}."
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_key_field="EC2InstanceID",
        outputs=outputs,
        outputs_prefix="AWSPublicExposure.SGAssociations",
        raw_response=outputs,
    )


def main():
    try:
        return_results(identify_sgs(demisto.args()))
    except Exception as ex:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute AWSIdentifySGPublicExposure. Error: {str(ex)}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Automation to determine which interface on an EC2 instance has a given public IP and identify associated security groups.

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 6.10.0

Dependencies


This script uses the following commands and scripts.

  • AWS
  • aws-ec2-instances-describe

Inputs


Argument Name Description
account_id The AWS Account ID.
instance_id EC2 Instance ID.
public_ip Public IP address whose network interface to identify.
region Region where EC2 instance resides.
integration_instance The AWS Integration Instance to use.

Outputs


Path Description Type
AWSPublicExposure.SGAssociations.EC2InstanceID ID of the EC2 instance with public exposure. String
AWSPublicExposure.SGAssociations.NetworkInterfaceID ID of the Elastic Network Interface with public exposure. String
AWSPublicExposure.SGAssociations.SecurityGroups Security Group IDs associated with this interface. Unknown
AWSPublicExposure.SGAssociations.PublicIP Public IP address exposed. String
AWSPublicExposure.SGAssociations.IntegrationInstance The AWS Integration Instance used for identification. String