コード例 #1
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "region":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "region", resource["type"]))

    issues = []
    if not is_guardduty_enabled(resource):
        issues.append("GuardDuty not enabled")
        is_compliant = False
    if not is_config_enabled(resource):
        issues.append("Config is not enabled")
        is_compliant = False
    if not is_cloudtrail_enabled(resource):
        issues.append("CloudTrail is not enabled")
        is_compliant = False
    if not is_flow_logs_enabled(resource):
        issues.append("VPC Flow Logs are not enabled everywhere")
        is_compliant = False

    if resource["region"] == "us-east-1":
        if not root_has_mfa(resource):
            issues.append("Root user does not have MFA")
            is_compliant = False
        if not has_compliant_password_policy(resource):
            issues.append("Password policy is not compliant")
            is_compliant = False

    if not is_compliant:
        # No remediations are peformed for these issues
        send_notification(", ".join(issues), "", resource)

    return is_compliant
コード例 #2
0
def check_public_dev_elb(loadbalancer, elb, EC2_INSTANCE_IGNORE_LIST, resource, remediate):
    # enumerate instances, enumeration can be skipped if we can change how we do event translation
    # by adding few moer fields in resource[] which is sent to remeditor
    is_compliant = True
    loadbalancer_name = loadbalancer["LoadBalancerName"]
    for instance in loadbalancer["Instances"]:

        instanceid = instance["InstanceId"]
        instance_elb_info = {
            "Id": instanceid,
            "loadbalancer": loadbalancer_name,
            "type": "classic",
        }

        if instanceid not in str(EC2_INSTANCE_IGNORE_LIST):
            is_compliant = False
            issue = "Dev EC2 {} is Public - via elbv1 {}".format(
                instanceid, loadbalancer_name
            )
            if remediate:
                if not remediate_instance(loadbalancer_name, instanceid, elb):
                    issue += " - Not remediated"
            send_notification(
                issue,
                "Instance ELB Information: {}".format(", ".join(instance_elb_info)),
                resource,
            )

    return is_compliant
コード例 #3
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "iam_role":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "iam_role", resource["type"]))

    # Get a session in the account where this resource is
    iam = get_session_for_account(resource["account"], resource["region"],
                                  "iam")
    role_is_permissive = False

    try:
        role = iam.get_role(RoleName=resource["id"])["Role"]
        policy = role["AssumeRolePolicyDocument"]
        policy = Policy(policy)
        role_is_permissive = policy.is_internet_accessible()

    except Exception as e:
        print(e)
        print("No role policy: {}".format(resource["id"]))

    if role_is_permissive:
        is_compliant = False
        issue = "IAM role {} is publicly exposed".format(resource["id"])

        if remediate:
            if not remediation_make_role_restricted(resource, iam):
                issue += " - Not remediated"

        send_notification(issue, "", resource)

    if is_compliant:
        print("Role is compliant: {}".format(resource["id"]))

    return is_compliant
コード例 #4
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "security_group":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "security_group", resource["type"]
            )
        )

    # Get a session in the account where this resource is
    ec2 = get_session_for_account(resource["account"], resource["region"], "ec2")
    security_group = ec2.describe_security_groups(GroupIds=[resource["id"]])
    security_group = security_group["SecurityGroups"][0]

    # Check if this allows ingress from 0.0.0.0/0 or the IPv6 equivalent ::/0
    allows_public_ingress = False
    for permission in security_group.get("IpPermissions", []):
        for ip_range in permission.get("IpRanges", []):
            if "/0" in ip_range.get("CidrIp", "") or "/0" in ip_range.get(
                "CidrIpv6", ""
            ):
                allows_public_ingress = True
                print("Security Group allows public ingress: {}".format(permission))

    if allows_public_ingress:
        is_compliant = False
        issue = "Security Group {} not compliant - Allows public ingress - Not remediated".format(
            resource["id"]
        )
        send_notification(issue, "", resource)

    return is_compliant
コード例 #5
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "ecs_service":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "ecs_service", resource["type"]
            )
        )

    # Get a session in the account where this resource is
    ecs = get_session_for_account(resource["account"], resource["region"], "ecs")

    ecs_is_public = False

    try:
        ## List all ECS Clusters
        ecs_clusters =  ecs.list_clusters()

        ## Now get all cluster ARNs from ECS clusters json
        cluster_arns = ecs_clusters["clusterArns"]

        ## For each cluster, try to find the named service
        ecs_description = []
        service_cluster = ""
        for cluster in cluster_arns:
            svc_description = ecs.describe_services(
                cluster = cluster,
                services = [resource["id"]]
            )
            ## If the services array contains an object then set ecs_description to the services array of the cluster
            ## Set cluster variable to the cluster ARN
            if len(svc_description['services']) > 0:
                ecs_description = svc_description['services']
                service_cluster = cluster

        for ecs_svc in ecs_description:
            if ecs_svc['networkConfiguration']['awsvpcConfiguration']['assignPublicIp'] == 'ENABLED':
                ecs_is_public = True

    except Exception as e:
        print(e)
        print("No ECS Services Definition: {}".format(resource["id"]))


    if ecs_is_public:
        is_compliant = False

        issue = "ECS {} is public via Public IP".format(resource["id"])
        if remediate:
            for ecs_svc in ecs_description:
                is_compliant = remediation_make_ecs_private(resource, ecs, service_cluster,ecs_svc['networkConfiguration']['awsvpcConfiguration'])
                if not is_compliant:
                    issue += " - Not remediated"
        send_notification(issue, "", resource)

    if is_compliant:
        print("ECS is private: {}".format(resource["id"]))

    return is_compliant
コード例 #6
0
def enumerate_instances(elbv2, loadbalancer_arn, resource, remediate):
    is_compliant = True
    # get list of dev account(s) - keep it as list to add more dev accounts
    if os.environ.get("DEV_ACCOUNTS", None) is not None:
        dev_accounts = os.environ["DEV_ACCOUNTS"].split(",")
    else:
        dev_accounts = None
    if os.environ.get("EC2_INSTANCE_IGNORE_LIST", None) is not None:
        EC2_INSTANCE_IGNORE_LIST = os.environ[
            "EC2_INSTANCE_IGNORE_LIST"].split(",")
    else:
        EC2_INSTANCE_IGNORE_LIST = None

    load_balancer = elbv2.describe_load_balancers(
        LoadBalancerArns=[loadbalancer_arn])["LoadBalancers"][0]
    loadbalancer_name = load_balancer["LoadBalancerName"]
    loadbalncer_type = load_balancer["Type"]
    loadbalancer_arn = load_balancer["LoadBalancerArn"]
    loadbalancer_scheme = load_balancer["Scheme"]
    if loadbalancer_scheme == "internet-facing" and resource[
            "account"] in dev_accounts:

        target_attribute = elbv2.describe_target_groups(
            LoadBalancerArn=loadbalancer_arn)

        for target_group in target_attribute["TargetGroups"]:
            target_group_arn = target_group["TargetGroupArn"]

            target_group_health = elbv2.describe_target_health(
                TargetGroupArn=target_group_arn)

            for target in target_group_health["TargetHealthDescriptions"]:
                target_id = target["Target"].get("Id")
                if target_id.startswith("i-"):

                    instance_elb_info = {
                        "Id": target_id,
                        "loadbalancer": loadbalancer_arn,
                        "type": loadbalncer_type,
                        "targetgroup": target_group_arn,
                    }
                    # check if instance is not whitelisted
                    if target_id not in str(EC2_INSTANCE_IGNORE_LIST):
                        is_compliant = False
                        issue = "Dev EC2 {} is Public - via elbv2 {}".format(
                            target_id, loadbalancer_name)
                        if remediate:
                            if not deregister_targets(elbv2, target_group_arn,
                                                      target_id):
                                issue += " - Not remediated"
                        send_notification(
                            issue,
                            "Instance ELB Information: {}".format(
                                ", ".join(instance_elb_info)),
                            resource,
                        )

    return is_compliant
コード例 #7
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "ami":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "ami", resource["type"]))

    # Get a session in the account where this resource is
    ec2 = get_session_for_account(resource["account"], resource["region"],
                                  "ec2")

    image_attribute = ec2.describe_image_attribute(
        Attribute="launchPermission", ImageId=resource["id"])

    # Get the accounts in the org
    all_account_in_org = fetch_all_accounts()

    description = ("image_attribute:" + str(image_attribute))

    # Check the permissions
    launchpermission = image_attribute["LaunchPermissions"]
    # Check if it is shared publicly
    if "all" in str(launchpermission):
        is_compliant = False

        issue = "EC2 AMI %s in account %s is public" % (
            resource["id"],
            resource["account"],
        )

        if remediate:
            if not remediation_remove_all(ec2, resource):
                issue += " - Not remediated"
        send_notification(issue, description, resource)

    # Check if it is shared with any accounts that are not in the org
    if "UserId" in str(launchpermission):
        for userid in launchpermission:
            if userid["UserId"] not in str(all_account_in_org):
                is_compliant = False

                issue = "AMI %s in account %s is shared with unknown account %s" % (
                    resource["id"],
                    resource["account"],
                    userid["UserId"],
                )

                if remediate:
                    if not remediation_remove_userid(ec2, resource, userid):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)

    return is_compliant
コード例 #8
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "rds_snapshot":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "rds_snapshot", resource["type"]))

    # Get a session in the account where this resource is
    rds = get_session_for_account(resource["account"], resource["region"],
                                  "rds")
    snapshot = rds.describe_db_snapshot_attributes(
        DBSnapshotIdentifier=resource["id"])
    if not snapshot:
        print("Resource {} not found".format(resource["id"]))
        return True

    attributes = snapshot.get("DBSnapshotAttributesResult",
                              {}).get("DBSnapshotAttributes", {})
    description = ("db_snapshot_details:" + str(snapshot) + "\n\n" +
                   "snapshot_attribute:" + str(attributes))

    all_account_in_org = fetch_all_accounts()

    # Look at the attributes to see if this snapshot is shared publicly or with unknown accounts
    for attribute in attributes:
        if attribute["AttributeName"] != "restore":
            continue

        for shared_id in attribute["AttributeValues"]:
            if shared_id == "all":
                is_compliant = False
                issue = "RDS DB snapshot %s in account %s is public" % (
                    resource["id"],
                    resource["account"],
                )

                if remediate:
                    if not remediation_remove_all(rds, resource):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)
            elif shared_id not in str(all_account_in_org):
                is_compliant = False
                issue = (
                    "RDS DB snapshot %s in account %s is shared with unknown account"
                    % (resource["id"], resource["account"]))

                if remediate:
                    if not remediation_remove_shared(rds, resource, shared_id):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)

    return is_compliant
コード例 #9
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "elb":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "elb", resource["type"]
            )
        )

    # get list of dev account(s) - keep it as list to add staging and future dev accounts
    if os.environ.get("DEV_ACCOUNTS", None) is not None:
        dev_accounts = os.environ["DEV_ACCOUNTS"].split(",")
    else:
        dev_accounts = None
    if os.environ.get("EC2_INSTANCE_IGNORE_LIST", None) is not None:
        EC2_INSTANCE_IGNORE_LIST = os.environ["EC2_INSTANCE_IGNORE_LIST"].split(",")
    else:
        EC2_INSTANCE_IGNORE_LIST = None
    # Get a session in the account where this resource is
    elb = get_session_for_account(resource["account"], resource["region"], "elb")
    load_balancer = elb.describe_load_balancers(LoadBalancerNames=[resource["id"]])[
        "LoadBalancerDescriptions"
    ][0]
    load_balancer_scheme = load_balancer["Scheme"]

    # Add check for public facing dev ec2 instances
    if (
        load_balancer_scheme == "internet-facing"
        and resource["account"] in dev_accounts
    ):
        is_compliant = check_public_dev_elb(
            load_balancer, elb, EC2_INSTANCE_IGNORE_LIST, resource, remediate
        )

    # Check all required tags have been set
    assigned_tags = elb.describe_tags(LoadBalancerNames=[resource["id"]])[
        "TagDescriptions"
    ][0].get("Tags", [])

    if is_missing_tags(assigned_tags):
        is_compliant = False
        issue = "ELB {} not compliant - Missing required tags - Not remediated".format(
            resource["id"]
        )
        send_notification(
            issue, "Required tags: {}".format(", ".join(get_required_tags())), resource
        )

    return is_compliant
コード例 #10
0
def dev_public_ec2_remediation(ec2, resource, remediate):
    filters = [
        {
            "Name": "instance-id",
            "Values": [
                resource["id"],
            ]
        },
    ]
    addresses = ec2.describe_addresses(Filters=filters).get("Addresses")
    if addresses:
        is_compliant = False
        for address in addresses:
            association_id = address.get("AssociationId")
            issue = "Dev EC2 {} is Public - via elastic ip".format(
                resource["id"])
            if remediate:
                if not remediation_private_dev_instance(
                        ec2, resource, association_id):
                    issue += " - Not remediated"
            send_notification(issue, "", resource)

    filters_iface = [
        {
            "Name": "attachment.instance-id",
            "Values": [
                resource["id"],
            ]
        },
    ]
    network_ifaces = ec2.describe_network_interfaces(Filters=filters_iface, )
    for network_iface in network_ifaces["NetworkInterfaces"]:
        if "AssociationId" not in str(network_iface) and "PublicIp" in str(
                network_iface):
            is_compliant = False
            issue = "Dev EC2 {} is public via primary interface".format(
                resource["id"])
            if remediate:
                if not remediation_terminate_ec2(ec2, resource, False):
                    issue += " - Not remediated"
            send_notification(issue, "", resource)

    return is_compliant
コード例 #11
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "sqs":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "sqs", resource["type"]
            )
        )

    # Get a session in the account where this resource is
    sqs = get_session_for_account(resource["account"], resource["region"], "sqs")

    # Get the policy
    policy_string = sqs.get_queue_attributes(
        QueueUrl=resource["id"], AttributeNames=["Policy"]
    )
    if policy_string is None:
        return is_compliant
    policy_string = policy_string.get("Attributes", {}).get("Policy", {})
    if len(policy_string) == 0:
        # Policy is empty or not present
        return is_compliant

    policy = json.loads(policy_string)

    description = "Policy " + policy_string

    # Check if it is public
    policy = Policy(policy)
    if policy.is_internet_accessible():
        is_compliant = False
        issue = "SQS {} is public".format(resource["id"])
        if remediate:
            if not remediation_make_private(sqs, resource):
                issue += " - Not remediated"
        send_notification(issue, description, resource)

    # TODO Check for unknown accounts being allowed access

    return is_compliant
コード例 #12
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "kms_key":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "kms_key", resource["type"]))

    # Get a session in the account where this resource is

    kms = get_session_for_account(resource["account"], resource["region"],
                                  "kms")

    # Remediation for Key Policy
    key_policy_is_non_compliant = False
    try:
        bad_policies = find_bad_policies(resource, kms)

        if bad_policies:
            key_policy_is_non_compliant = True

    except Exception as e:
        print(e)
        print("No KMS Keys: {}".format(resource["id"]))

    if key_policy_is_non_compliant:
        is_compliant = False
        issue = "KMS Key: {} - has a non restrictive key policy".format(
            resource["id"])
        if remediate:
            for policy in bad_policies:
                is_compliant = remediation_kms_policy(resource, kms, policy)
            if not is_compliant:
                issue += " - Not remediated"

        send_notification(issue, "", resource)

    if is_compliant:
        print("KMS Key is compliant: {}".format(resource["id"]))

    return is_compliant
コード例 #13
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "lambda_function":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "lambda_function", resource["type"]
            )
        )

    # Get a session in the account where this resource is
    lambda_ = get_session_for_account(resource["account"], resource["region"], "lambda")

    lambda_is_public = False
    try:
        response_policy = lambda_.get_policy(FunctionName=resource["id"])
        policy = json.loads(response_policy["Policy"])
        policy = Policy(policy)
        lambda_is_public = policy.is_internet_accessible()

    except Exception as e:
        print(e)
        print("No lambda policy: {}".format(resource["id"]))

    if lambda_is_public:
        is_compliant = False

        issue = "Lambda {} is public via resource policy".format(resource["id"])
        if remediate:
            for statement in policy.statements:
                if '*' in statement.principals:
                    if not remediation_make_lambda_private(resource, lambda_, statement.statement["Sid"]):
                        issue += " - Not remediated"
        send_notification(issue, "", resource)

    if is_compliant:
        print("lambda is private: {}".format(resource["id"]))

    return is_compliant
コード例 #14
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "ecs_task_set":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "ecs_task_set", resource["type"]))

    # Get a session in the account where this resource is
    ecs = get_session_for_account(resource["account"], resource["region"],
                                  "ecs")

    ecs_is_public = False

    try:
        ## List all ECS Clusters
        ecs_clusters = ecs.list_clusters()

        ## Now get all cluster ARNs from ECS clusters json
        cluster_arns = ecs_clusters["clusterArns"]

        ## For each cluster  X Service: try to find the named taskSet
        identified_cluster = ""
        identified_service = ""
        task_sets_to_audit = []
        for cluster in cluster_arns:
            print("ECS: Cluster ARN {}".format(cluster))
            ## Get the ServiceARNs for each Cluster

            paginator = ecs.get_paginator('list_services')
            resp = paginator.paginate(cluster=cluster)
            service_arns = resp['serviceArns']
            while 'nextToken' in resp:
                resp = paginator.paginate(cluster=cluster,
                                          nextToken=resp['nextToken'])
                service_arns = service_arns + resp['serviceArns']

            ## Now Loop through every possible cluster arn x service arn for the the possible ECS Task Set:
            for service in service_arns:
                temp_task_set = ecs.describe_task_sets(
                    cluster=cluster,
                    service=service,
                    task_sets=[resource['id']])['taskSets']
                if len(temp_task_set) > 0:
                    identified_cluster = cluster
                    identified_service = service
                    task_sets_to_audit += temp_task_set

        ## Using the task set that was identified
        ## Audit the task set to make sure that the there is no public ip assignment.
        ## If public ip is assigned then the only thing that can be done
        ## is deleting the task set
        bad_task_sets = []
        for task_set in task_sets_to_audit:
            if task_set['networkConfiguration']['awsvpcConfiguration'][
                    'assignPublicIp'] == 'ENABLED':
                bad_task_sets.append(task_set["id"])
                ecs_is_public = True

    except Exception as e:
        print(e)
        print("No ECS Task Set Definition: {}".format(resource["id"]))

    if ecs_is_public:
        is_compliant = False

        for bad_task_set in bad_task_sets:
            issue = "ECS Task Set {} is public via Public IP Assignment".format(
                resource["id"])
            if remediate:
                is_compliant = remediation_delete_task_set(
                    resource, bad_task_set, ecs, identified_cluster,
                    identified_service)
                if not is_compliant:
                    issue += " - Not remediated"
            send_notification(issue, "", resource)

    if is_compliant:
        print("ECS is private: {}".format(resource["id"]))

    return is_compliant
コード例 #15
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "s3_bucket":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "s3_bucket", resource["type"]))

    buckets_to_ignore = os.environ.get("S3_BUCKET_IGNORE_LIST", "")
    if resource["id"] in buckets_to_ignore.split(","):
        return True

    # Get a session in the account where this resource is
    s3 = get_session_for_account(resource["account"], resource["region"], "s3")

    policy_is_public = False
    try:
        status = s3.get_bucket_policy_status(Bucket=resource["id"])
        policy_is_public = status["PolicyStatus"]["IsPublic"]
    except Exception as e:
        print(e)
        print("No bucket policy: {}".format(resource["id"]))

    if policy_is_public:
        is_compliant = False

        issue = "S3 bucket {} is public".format(resource["id"])
        if remediate:
            if not remediation_make_policy_private(s3, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    acl_is_public = False
    acl = s3.get_bucket_acl(Bucket=resource["id"])
    for i in range(len(acl["Grants"])):
        grantee_id = acl["Grants"][i]["Grantee"]
        if "http://acs.amazonaws.com/groups/global/AllUsers" in str(
                grantee_id
        ) or "http://acs.amazonaws.com/groups/global/AuthenticatedUsers" in str(
                grantee_id):
            acl_is_public = True
            break

    if acl_is_public:
        is_compliant = False

        issue = "S3 bucket {} is public".format(resource["id"])
        if remediate:
            if not remediation_make_acl_private(s3, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    if is_compliant:
        print("bucket is private: {}".format(resource["id"]))

    # Ensure required tags exist
    assigned_tags = []
    try:
        assigned_tags = s3.get_bucket_tagging(Bucket=resource["id"])["TagSet"]
    except Exception as e:
        # If no tags exist, we get an exception that doesn't appear to be defined to catch, so we generically
        # catch the exception and look for the key phrase to indicate this problem, and if we can't find it, we re-raise it
        if "NoSuchTagSet" not in str(e):
            raise e

    if is_missing_tags(assigned_tags):
        is_compliant = False
        issue = "S3 bucket {} not compliant - Missing required tags - Not remediated".format(
            resource["id"])
        send_notification(
            issue, "Required tags: {}".format(", ".join(get_required_tags())),
            resource)

    # Check the bucket policy for some things
    policy = None

    try:
        policy_string = s3.get_bucket_policy(Bucket=resource["id"])["Policy"]
        policy = json.loads(policy_string)
    except Exception as e:
        if "NoSuchBucketPolicy" in str(e):
            print("No bucket policy for {}".format(resource["id"]))
        else:
            print(e)
            raise e

    if not denies_unencrypted_uploads(policy):
        #To-Do add a check for bucket encryption setting
        is_compliant = False

        return False
        issue = "S3 bucket {} not compliant - Does not deny unencrypted uploads".format(
            resource["id"])
        if remediate:
            if not remediation_make_policy_private(s3, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    if not denies_lack_of_tls(policy):
        is_compliant = False
        #To-Do add a check for bucket encryption setting
        return False
        issue = "S3 bucket {} not compliant - Does not deny non-TLS communications".format(
            resource["id"])
        if remediate:
            if not remediation_make_policy_private(s3, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    return is_compliant
コード例 #16
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "ecs_task":
        raise Exception(
            "Mismatched type. Expected {} but received {}".format(
                "ecs_task_set", resource["type"]
            )
        )

    # Get a session in the account where this resource is
    ecs = get_session_for_account(resource["account"], resource["region"], "ecs")

    ## In order to check if ECS is public, we need to perform an ENI lookup against EC2
    ec2 = get_session_for_account(resource["account"], resource["region"], "ec2")

    ecs_is_public = False

    try:
        ## List all ECS Clusters
        ecs_clusters =  ecs.list_clusters()

        ## Now get all cluster ARNs from ECS clusters json
        cluster_arns = ecs_clusters["clusterArns"]

        ## For each cluster list all tasks and find noncompliant tasks.
        non_compliant_tasks = []
        task_cluster = ""
        for cluster in cluster_arns:
            all_cluster_tasks = ecs.list_tasks(cluster=cluster)["taskArns"]
            for task_ in all_cluster_tasks:
                ecs_tasks = ecs.describe_tasks(cluster=cluster,tasks=[task_])["tasks"]
                print("ECS TASK: {}".format(ecs_tasks))
                # Check if ENI is public
                for task in ecs_tasks:
                    # for each Task, look for the ElasticNetworkInterface (ENI) attachement and if there is, then check if ENI is public
                    eni_is_public = False
                    for attachment in task["attachments"]:
                        if attachment["type"] == "ElasticNetworkInterface":
                            eni_id = ""
                            for el in attachment["details"]:
                                if el["name"] == "networkInterfaceId":
                                    eni_id = el["value"]
                            eni_desc = ec2.describe_network_interfaces(NetworkInterfaceIds=[eni_id])
                            ## Determine if ENI is public:
                            for eni in eni_desc["NetworkInterfaces"]:
                                # If there is a public IP assignment to the ENI, then the ENI is considered public
                                if eni["Association"]["PublicIp"] != "":
                                    ecs_is_public = True
                                    task_cluster = cluster
                                    task_data = {
                                        "cluster": cluster,
                                        "taskArn": task["taskArn"]
                                    }
                                    non_compliant_tasks.append(task_data)
                                    break

    except Exception as e:
        print(e)
        print("No ECS Tasks: {}".format(resource["id"]))


    if ecs_is_public:
        is_compliant = False

        # Remediate every issue found in non_compliant_tasks
        for bad_task in non_compliant_tasks:
            issue = "ECS Task {} is public via public IP Assignment".format(bad_task["taskArn"])
            if remediate:
                is_compliant = remediation_make_ecs_task_private(bad_task["taskArn"],ecs,bad_task["cluster"])
                if not is_compliant:
                        issue += " - Not remediated"
            send_notification(issue, "", resource)

    if is_compliant:
        print("ECS is private: {}".format(resource["id"]))

    return is_compliant
コード例 #17
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "ec2":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "ec2", resource["type"]))
    # get list of dev account(s) - keep it as list to add staging and future dev accounts
    if os.environ.get("DEV_ACCOUNTS", None) is not None:
        dev_accounts = os.environ["DEV_ACCOUNTS"].split(",")
    else:
        dev_accounts = None
    if os.environ.get("EC2_INSTANCE_IGNORE_LIST", None) is not None:
        EC2_INSTANCE_IGNORE_LIST = os.environ[
            "EC2_INSTANCE_IGNORE_LIST"].split(",")
    else:
        EC2_INSTANCE_IGNORE_LIST = None

    # Get a session in the account where this resource is
    ec2 = get_session_for_account(resource["account"], resource["region"],
                                  "ec2")
    instances = ec2.describe_instances(InstanceIds=[resource["id"]])
    # We looked for a specific instance id, so ensure it has been returned.
    if (len(instances["Reservations"]) != 1
            or len(instances["Reservations"][0]["Instances"]) != 1):
        print("Resource {} not found".format(resource["id"]))
        return True

    instance = instances["Reservations"][0]["Instances"][0]

    # check for dev instances and include the whitelist for ec2
    if resource["account"] in dev_accounts and resource[
            "id"] not in EC2_INSTANCE_IGNORE_LIST:
        is_compliant = dev_public_ec2_remediation(ec2, resource, remediate)

    if instance["State"]["Name"] != "running":
        # Instance is stopped, or still starting
        # TODO If the instance is still starting, we should recheck it later.
        return True

    # Check if IMDSv2 is enforced
    if (instance["MetadataOptions"]["HttpEndpoint"] == "enabled"
            and instance["MetadataOptions"]["HttpTokens"] != "required"):
        # IMDS v1 is still allowed

        is_compliant = False
        issue = "EC2 {} not compliant - IMDSv1 still allowed".format(
            resource["id"])
        if remediate:
            if not remediation_enforce_IMDSv2(ec2, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    # Check the required tags have been set
    assigned_tags = instance.get("Tags", [])
    if is_missing_tags(assigned_tags):
        is_compliant = False
        issue = "EC2 {} not compliant - Missing required tags".format(
            resource["id"])
        if remediate:
            if not remediation_stop_instance(ec2, resource):
                issue += " - Not remediated"
        send_notification(
            issue, "Required tags: {}".format(", ".join(get_required_tags())),
            resource)

    return is_compliant
コード例 #18
0
def audit(resource, remediate=False):
    MAX_INACTIVE_PASSWORD_DAYS = 90
    MAX_INACTIVE_ACCESS_KEY_DAYS = 90

    is_compliant = True
    if resource["type"] != "iam_user":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "iam_user", resource["type"]))

    # Get a session in the account where this resource is
    iam = get_session_for_account(resource["account"], resource["region"],
                                  "iam")

    description = ""

    user = iam.get_user(UserName=resource["id"])["User"]
    create_date = user["CreateDate"]

    utc = UTC()

    t_minus_1_days = datetime.now(utc) - timedelta(days=1)
    login_create_date = False

    # If there is a login profile, then this user has a console login (ie. a password)
    try:
        login_profile = iam.get_login_profile(UserName=resource["id"])
        login_create_date = login_profile.get("LoginProfile",
                                              {}).get("CreateDate", False)
    except iam.exceptions.NoSuchEntityException:
        # No login profile
        print("No login profile found")
        pass

    if login_create_date:
        # User has a password login

        # Ensure they have an MFA
        user_mfa = iam.list_mfa_devices(UserName=resource["id"])
        if (len(user_mfa["MFADevices"]) == 0 and login_profile
                and login_create_date < t_minus_1_days):
            # User has no MFA device, but does have a password login, and their password login was created more than 1 day ago
            is_compliant = False
            issue = "IAM user {} in account {} has a password login but no MFA".format(
                resource["id"], resource["account"])
            print(issue)

            if remediate:
                if not remediation_remove_password(iam, resource,
                                                   "lack of MFA"):
                    issue += " - Not remediated"
            send_notification(issue, description, resource)

        # Ensure they've logged in within MAX_INACTIVE_PASSWORD_DAYS
        last_login = user.get("PasswordLastUsed", None)
        t_minus_max_inactive_password_days = datetime.now(utc) - timedelta(
            days=MAX_INACTIVE_PASSWORD_DAYS)

        if last_login is None:
            # User has never logged in. Check how old this user is.
            if login_create_date < t_minus_max_inactive_password_days:
                is_compliant = False
                issue = "IAM user {} in account {} has not logged in ever, and their user was created more than {} days ago".format(
                    resource["id"], resource["account"],
                    MAX_INACTIVE_PASSWORD_DAYS)
                print(issue)

                if remediate:
                    if not remediation_remove_password(
                            iam,
                            resource,
                            "password inactive for over {} days".format(
                                MAX_INACTIVE_PASSWORD_DAYS),
                    ):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)
        else:
            # User has logged in. Check long ago it was.
            if last_login < t_minus_max_inactive_password_days:
                # User has not logged in for more than MAX_INACTIVE_PASSWORD_DAYS
                is_compliant = False
                issue = "IAM user {} in account {} has a password, but has not logged in for over {} days".format(
                    resource["id"], resource["account"],
                    MAX_INACTIVE_PASSWORD_DAYS)

                if remediate:
                    if not remediation_remove_password(
                            iam,
                            resource,
                            "password inactive for over {} days".format(
                                MAX_INACTIVE_PASSWORD_DAYS),
                    ):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)

    # Get access keys for the user
    keys = iam.list_access_keys(UserName=resource["id"])

    t_minus_max_inactive_key_days = datetime.now(utc) - timedelta(
        days=MAX_INACTIVE_ACCESS_KEY_DAYS)

    for k in keys["AccessKeyMetadata"]:
        last_used_response = iam.get_access_key_last_used(
            AccessKeyId=k["AccessKeyId"])
        last_used_date = last_used_response["AccessKeyLastUsed"].get(
            "LastUsedDate", None)
        if last_used_date is None:
            if k["CreateDate"] < t_minus_max_inactive_key_days:
                # Access key is old and unused
                is_compliant = False
                issue = "IAM user {} in account {} has an access key that has not been used for over {} days".format(
                    resource["id"], resource["account"],
                    MAX_INACTIVE_ACCESS_KEY_DAYS)

                if remediate:
                    if not remediation_remove_access_key(
                            iam, resource, k["AccessKeyId"]):
                        issue += " - Not remediated"
                send_notification(issue, description, resource)

        elif last_used_date < t_minus_max_inactive_key_days:
            # Access key has not been used for over 100 days
            is_compliant = False
            issue = "IAM user {} in account {} has an access key that has not been used for over {} days".format(
                resource["id"], resource["account"],
                MAX_INACTIVE_ACCESS_KEY_DAYS)

            if remediate:
                if not remediation_remove_access_key(iam, resource,
                                                     k["AccessKeyId"]):
                    issue += " - Not remediated"
            send_notification(issue, description, resource)

    return is_compliant
コード例 #19
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "redshift":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "redshift", resource["type"]))

    # Get a session in the account where this resource is
    redshift = get_session_for_account(resource["account"], resource["region"],
                                       "redshift")

    cluster = redshift.describe_clusters(
        ClusterIdentifier=resource["id"])["Clusters"][0]

    if cluster[
            "ClusterStatus"] != "available" or "ClusterCreateTime" not in cluster:
        # For clusters that are still starting we should check again
        repeat_invocation(resource)
        return True

    if cluster["PubliclyAccessible"]:
        is_compliant = False

        issue = "Redshift {} is not compliant - Is public".format(
            resource["id"])
        if remediate:
            if not remediation_make_private(redshift, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    if not cluster["Encrypted"]:
        is_compliant = False

        issue = "Redshift {} is not compliant - Not encrypted".format(
            resource["id"])
        if remediate:
            if not remediation_make_encrypted(redshift, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    if is_missing_tags(cluster["Tags"]):
        is_compliant = False
        issue = "Redshift {} not compliant - Missing required tags - Not remediated".format(
            resource["id"])
        # You cannot stop a redshift cluster, so we just file the issue
        send_notification(
            issue, "Required tags: {}".format(", ".join(get_required_tags())),
            resource)

    # Check that access requires TLS
    requires_tls = False
    for param_group in cluster["ClusterParameterGroups"]:
        # You can have multiple parameter groups applied to a redshift cluster that have different settings.
        # I believe if one requires TLS then that must win, so I just ensure that at least one of the active
        # parameters groups has this setting.

        # Only look at parameter groups that are in-sync
        if param_group["ParameterApplyStatus"] != "in-sync":
            continue

        # Look through the parameters for require_ssl and ensure it is set to "true"
        parameters = redshift.describe_cluster_parameters(
            ParameterGroupName=param_group["ParameterGroupName"])["Parameters"]
        for parameter in parameters:
            if (parameter["ParameterName"] == "require_ssl"
                    and parameter["ParameterValue"] == "true"):
                requires_tls = True

    if not requires_tls:
        is_compliant = False
        issue = "Redshift {} not compliant - Not enforcing TLS - Not remediated".format(
            resource["id"])
        # You cannot stop a redshift cluster, so we just file the issue
        send_notification(issue, "", resource)

    return is_compliant
コード例 #20
0
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "rds":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "rds", resource["type"]))

    # Get a session in the account where this resource is
    rds = get_session_for_account(resource["account"], resource["region"],
                                  "rds")
    instance = rds.describe_db_instances(DBInstanceIdentifier=resource["id"])
    if not instance:
        print("Resource {} not found".format(resource["id"]))
        return True

    instance = instance["DBInstances"][0]

    encrypted = instance["StorageEncrypted"]
    public_access = instance["PubliclyAccessible"]
    dbstatus = instance["DBInstanceStatus"]

    # Only check databases that were started within the past 60 minutes
    time_difference = int(os.environ.get("db_check_time", 60))
    utc = UTC()
    threshold_check = datetime.now(utc) - timedelta(minutes=time_difference)

    # Ignore instances that are not running
    if dbstatus in ["stopped"]:
        return True
    if dbstatus not in ["available"]:
        # For databases that are still starting we should check again
        repeat_invocation(resource)
        return True

    if "InstanceCreateTime" in instance:
        db_create_time = instance["InstanceCreateTime"]

        if db_create_time <= threshold_check:
            if not encrypted:
                is_compliant = False
                issue = "RDS {} not compliant - Storage not Encrypted".format(
                    resource["id"])

                if remediate:
                    if not remediation_stop_instance(rds, resource, instance):
                        issue += " - Not remediated"
                send_notification(issue, "", resource)

    if public_access:
        is_compliant = False
        issue = "RDS {} not compliant - PubliclyAccessible".format(
            resource["id"])
        if remediate:
            if not remediation_make_private(rds, resource):
                issue += " - Not remediated"
        send_notification(issue, "", resource)

    # Get tags for database
    assigned_tags = rds.list_tags_for_resource(
        ResourceName=instance["DBInstanceArn"])["TagList"]
    if is_missing_tags(assigned_tags):
        is_compliant = False
        issue = "RDS {} not compliant - Missing required tags".format(
            resource["id"])
        #only notify if rds has no tags, uncomment to stop instances with no tag
        if remediate:
            if not remediation_stop_instance(rds, resource, instance):
                issue += " - Not remediated"
        send_notification(
            issue, "Required tags: {}".format(", ".join(get_required_tags())),
            resource)

    return is_compliant