예제 #1
0
def has_compliant_password_policy(resource):
    iam = get_session_for_account(resource["account"], resource["region"],
                                  "iam")
    is_compliant = True

    try:
        policy = iam.get_account_password_policy()
        policy = policy["PasswordPolicy"]
        if policy.get("MinimumPasswordLength", 0) < 8:
            print(
                "Password policy does not have the minimum number of characters"
            )
            is_compliant = False
        if not policy.get("RequireNumbers", False):
            print("Password policy does not require numbers")
            is_compliant = False
        if not policy.get("RequireSymbols", False):
            print("Password policy does not require symbols")
            is_compliant = False
        if not policy.get("RequireLowercaseCharacters", False):
            print("Password policy does not require lowercase characters")
            is_compliant = False
        if not policy.get("RequireUppercaseCharacters", False):
            print("Password policy does not require uppercase characters")
            is_compliant = False
    except iam.exceptions.NoSuchEntityException:
        print("No password policy set")
        return False
    except Exception as e:
        print("Exception: {}".format(e))
        return False

    return is_compliant
예제 #2
0
def is_guardduty_enabled(resource):
    guardduty = get_session_for_account(resource["account"],
                                        resource["region"], "guardduty")
    detectors_list = guardduty.list_detectors()["DetectorIds"]

    if len(detectors_list) == 0:
        print("No detectors found, please enable GuardDuty for this account")
        return False

    for d in detectors_list:
        master_account = guardduty.get_master_account(DetectorId=d)

        if ("Master" in master_account.keys()
                and master_account["Master"]["RelationshipStatus"] == "Enabled"
                and master_account["Master"]["AccountId"]
                == GUARDDUTY_MASTER_ACCOUNT):
            print("GuardDuty is enabled")
            return True
        else:
            if account == GUARDDUTY_MASTER_ACCOUNT:
                print("This is the GuardDuty master account")
                return True
            else:
                print("GuardDuty is not connected with the master account")
    return False
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
def audit(resource, remediate=False):
    is_compliant = True
    if resource["type"] != "elbv2":
        raise Exception("Mismatched type. Expected {} but received {}".format(
            "elbv2", resource["type"]))

    # Get a session in the account where this resource is
    elbv2 = get_session_for_account(resource["account"], resource["region"],
                                    "elbv2")
    # polling based is easy - you just enumerate all loadbalancers and their attached target groups with instances.
    # making it event driven for elbv2 is complicated (as for some cases multiple api calls would make it public) than expected - we would need to address below cases.
    # 1. creating/updating a new elb listener with target group(new or existing with or without instance)
    # 2. updating already existing(attached to elb listener) target group with new instances, without modifying elb listener
    # 3. creating a new target group (not attached) with instances AND then attaching to elb listener

    if ":loadbalancer/" in resource["id"]:
        loadbalancer_arn = resource["id"]
        is_compliant = enumerate_instances(elbv2, loadbalancer_arn, resource,
                                           remediate)
    if ":targetgroup/" in resource["id"]:
        load_balancers = elbv2.describe_target_groups(
            TargetGroupArns=[resource["id"]
                             ])["TargetGroups"][0]["LoadBalancerArns"]
        for loadbalancer_arn in load_balancers:
            is_compliant = enumerate_instances(elbv2, loadbalancer_arn,
                                               resource, remediate)

    return is_compliant
예제 #6
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
예제 #7
0
def root_has_mfa(resource):
    iam = get_session_for_account(resource["account"], resource["region"],
                                  "iam")
    summary = iam.get_account_summary()
    if summary["SummaryMap"]["AccountMFAEnabled"] != 1:
        # Root user does not have MFA
        return False

    return True
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
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
예제 #10
0
def is_config_enabled(resource):
    config = get_session_for_account(resource["account"], resource["region"],
                                     "config")
    delivery_channels = config.describe_delivery_channels()["DeliveryChannels"]
    if len(delivery_channels) == 0:
        print("No delivery channels configured for Config Service")
        return False

    logger.debug("Delivery Channels configured:")
    for c in delivery_channels:
        logger.debug(json.dumps(c, sort_keys=True, indent=4))

    print("Config Service has Delivery Channels configured")
    return True
예제 #11
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
예제 #12
0
def is_cloudtrail_enabled(resource):
    cloudtrail = get_session_for_account(resource["account"],
                                         resource["region"], "cloudtrail")
    trails = cloudtrail.describe_trails()["trailList"]

    if len(trails) == 0:
        print("No CloudTrail trails configured in this region")
        return False

    logger.debug("CloudTrail trails configured in this region:")
    for t in trails:
        logger.debug(json.dumps(t, sort_keys=True, indent=4))

    print("CloudTrail trails are configured in this region")

    return True
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
예제 #14
0
def is_flow_logs_enabled(resource):
    ec2 = get_session_for_account(resource["account"], resource["region"],
                                  "ec2")
    vpc_ids = [item["VpcId"] for item in ec2.describe_vpcs()["Vpcs"]]
    flowlog_vpcs = [
        item["ResourceId"] for item in ec2.describe_flow_logs()["FlowLogs"]
    ]
    """
    the full list of VPC IDs should be present in the flow logs list, but there could be more
    flow logs, since ResourceId is not limited to a VPC
    """
    diff = list(set(vpc_ids).difference(set(flowlog_vpcs)))
    if len(diff) == 0:
        print("All VPCs have Flow Logs enabled")
        return True

    logger.debug("VPCs that do not have Flow Logs associated with them:")
    logger.debug(json.dumps(diff))

    print("Not all VPC have Flow logs associated with them")

    return False
예제 #15
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
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
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
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
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
예제 #20
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
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
예제 #22
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
예제 #23
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