Ejemplo n.º 1
0
    def add_by_arn_and_access_level(self,
                                    arn_list,
                                    access_level,
                                    conditions_block=None):
        """
        This adds the user-supplied ARN(s), service prefixes, access levels, and condition keys (if applicable) given
        by the user. It derives the list of IAM actions based on the user's requested ARNs and access levels.

        Arguments:
            arn_list: Just a list of resource ARNs.
            access_level: "Read", "List", "Tagging", "Write", or "Permissions management"
            conditions_block: Optionally, a condition block with one or more conditions
        """
        for arn in arn_list:
            service_prefix = get_service_from_arn(arn)
            service_action_data = get_action_data(service_prefix, "*")
            for service_prefix in service_action_data:
                for row in service_action_data[service_prefix]:
                    if (does_arn_match(arn, row["resource_arn_format"])
                            and row["access_level"] == access_level):
                        raw_arn_format = row["resource_arn_format"]
                        resource_type_name = get_resource_type_name_with_raw_arn(
                            raw_arn_format)
                        sid_namespace = create_policy_sid_namespace(
                            service_prefix, access_level, resource_type_name)
                        actions = get_actions_with_arn_type_and_access_level(
                            service_prefix, resource_type_name, access_level)
                        # Make supplied actions lowercase
                        # supplied_actions = [x.lower() for x in actions]
                        supplied_actions = actions.copy()
                        dependent_actions = get_dependent_actions(
                            supplied_actions)
                        # List comprehension to get all dependent actions that are not in the supplied actions.
                        dependent_actions = [
                            x for x in dependent_actions
                            if x not in supplied_actions
                        ]
                        if len(dependent_actions) > 0:
                            for dep_action in dependent_actions:
                                self.add_action_without_resource_constraint(
                                    dep_action)
                                # self.add_action_without_resource_constraint(
                                #     str.lower(dep_action)
                                # )

                        temp_sid_dict = {
                            "arn": [arn],
                            "service": service_prefix,
                            "access_level": access_level,
                            "arn_format": raw_arn_format,
                            "actions": actions,
                            "conditions": [],  # TODO: Add conditions
                        }
                        if sid_namespace in self.sids.keys():
                            # If the ARN already exists there, skip it.
                            if arn not in self.sids[sid_namespace]["arn"]:
                                self.sids[sid_namespace]["arn"].append(arn)
                        # If it did not exist before at all, create it.
                        else:
                            self.sids[sid_namespace] = temp_sid_dict
Ejemplo n.º 2
0
 def test_print_policy_with_actions_having_dependencies(self):
     desired_output = {
         "Version":
         "2012-10-17",
         "Statement": [{
             "Sid":
             "KmsPermissionsmanagementKmskey",
             "Effect":
             "Allow",
             "Action": ["kms:creategrant"],
             "Resource":
             ["arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}"]
         }, {
             "Sid":
             "MultMultNone",
             "Effect":
             "Allow",
             "Action":
             ["kms:createcustomkeystore", "cloudhsm:describeclusters"],
             "Resource": ["*"]
         }]
     }
     supplied_actions = actions_test_data_1
     supplied_actions = get_dependent_actions(db_session, supplied_actions)
     arn_action_group = ArnActionGroup()
     arn_dict = arn_action_group.process_list_of_actions(
         supplied_actions, db_session)
     self.maxDiff = None
     policy = print_policy(arn_dict, db_session)
     self.assertDictEqual(policy, desired_output)
Ejemplo n.º 3
0
 def test_get_dependent_actions(self):
     """querying.actions.get_dependent_actions"""
     dependent_actions_single = ["ec2:associateiaminstanceprofile"]
     dependent_actions_double = ["shield:associatedrtlogbucket"]
     dependent_actions_several = ["chime:getcdrbucket"]
     self.assertEqual(
         get_dependent_actions(dependent_actions_single),
         ["iam:PassRole"],
     )
     self.assertEqual(
         get_dependent_actions(dependent_actions_double),
         ["s3:GetBucketPolicy", "s3:PutBucketPolicy"],
     )
     self.assertEqual(
         get_dependent_actions(dependent_actions_several),
         [
             "s3:GetBucketAcl",
             "s3:GetBucketLocation",
             "s3:GetBucketLogging",
             "s3:GetBucketVersioning",
             "s3:GetBucketWebsite",
         ],
     )
Ejemplo n.º 4
0
 def test_get_dependent_actions(self):
     """querying.actions.get_dependent_actions"""
     dependent_actions_single = ["ec2:associateiaminstanceprofile"]
     dependent_actions_double = ["shield:associatedrtlogbucket"]
     dependent_actions_several = ["chime:getcdrbucket"]
     self.assertEqual(
         get_dependent_actions(db_session, dependent_actions_single),
         ["iam:passrole"],
     )
     self.assertEqual(
         get_dependent_actions(db_session, dependent_actions_double),
         ["s3:getbucketpolicy", "s3:putbucketpolicy"],
     )
     self.assertEqual(
         get_dependent_actions(db_session, dependent_actions_several),
         [
             "s3:getbucketacl",
             "s3:getbucketlocation",
             "s3:getbucketlogging",
             "s3:getbucketversioning",
             "s3:getbucketwebsite",
         ],
     )
Ejemplo n.º 5
0
def write_policy_with_actions(db_session, cfg, minimize_statement=None):
    """
    Writes an IAM policy given a dict containing lists of actions.
    """
    check_actions_schema(cfg)
    policy_with_actions = Roles()
    policy_with_actions.process_actions_config(cfg)
    supplied_actions = []
    for role in policy_with_actions.get_roles():
        supplied_actions.extend(role[3].copy())
    supplied_actions = get_dependent_actions(db_session, supplied_actions)
    arn_action_group = ArnActionGroup()
    arn_dict = arn_action_group.process_list_of_actions(
        supplied_actions, db_session)
    policy = print_policy(arn_dict, db_session, minimize_statement)
    return policy
Ejemplo n.º 6
0
    def add_by_list_of_actions(self, db_session, supplied_actions):
        """
        Takes a list of actions, queries the database for corresponding arns, adds them to the object.

        :param db_session: SQLAlchemy database session object
        :param supplied_actions: A list of supplied actions
        """
        # Make supplied actions lowercase
        # supplied_actions = [x.lower() for x in supplied_actions]
        # actions_list = get_dependent_actions(db_session, supplied_actions)
        dependent_actions = get_dependent_actions(db_session, supplied_actions)
        # List comprehension to get all dependent actions that are not in the supplied actions.
        dependent_actions = [
            x for x in dependent_actions if x not in supplied_actions
        ]

        arns_matching_supplied_actions = []

        # arns_matching_supplied_actions is a list of dicts.
        # It must do this rather than dictionaries because there will be duplicate
        #     values by nature of how the entries in the IAM database are structured.
        # I'll provide the example values here to improve readability.

        for action in supplied_actions:
            service_name, action_name = action.split(":")
            action_data = get_action_data(db_session, service_name,
                                          action_name)
            for row in action_data[service_name]:
                if row["resource_arn_format"] not in arns_matching_supplied_actions:
                    arns_matching_supplied_actions.append(
                        {
                            "resource_arn_format": row["resource_arn_format"],
                            "access_level": row["access_level"],
                            "action": row["action"],
                        }
                        # [row["resource_arn_format"], row["access_level"], row["action"]])
                    )

        # arns_matching_supplied_actions = [{
        #         "resource_arn_format": "*",
        #         "access_level": "Write",
        #         "action": "kms:createcustomkeystore"
        #     },{
        #         "resource_arn_format": "arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}",
        #         "access_level": "Permissions management",
        #         "action": "kms:creategrant"
        #     },{
        #         "resource_arn_format": "*",
        #         "access_level": "Permissions management",
        #         "action": "kms:creategrant"
        # }]

        # Identify the actions that do not support resource constraints
        # If that's the case, add it to the wildcard namespace. Otherwise, don't add it.

        actions_without_resource_constraints = []
        for item in arns_matching_supplied_actions:
            if item["resource_arn_format"] != "*":
                self.add_by_arn_and_access_level(db_session,
                                                 [item["resource_arn_format"]],
                                                 item["access_level"])
            else:
                actions_without_resource_constraints.append(item["action"])

        # If there are any dependent actions, we need to add them without resource constraints.
        # Otherwise, we get into issues where the amount of extra SIDs will balloon.
        # Also, the user has no way of knowing what those dependent actions are beforehand.
        # TODO: This is, in fact, a great opportunity to introduce conditions. But we aren't there yet.
        if len(dependent_actions) > 0:
            for dep_action in dependent_actions:
                self.add_action_without_resource_constraint(dep_action)
                # self.add_action_without_resource_constraint(str.lower(dep_action))
        # Now, because add_by_arn_and_access_level() adds all actions under an access level, we have to
        # remove all actions that do not match the supplied_actions. This is done in-place.
        self.remove_actions_not_matching_these(supplied_actions +
                                               dependent_actions)
        for action in actions_without_resource_constraints:
            self.add_action_without_resource_constraint(action)
        self.remove_actions_duplicated_in_wildcard_arn()
        rendered_policy = self.get_rendered_policy(db_session)
        return rendered_policy
Ejemplo n.º 7
0
    def add_by_list_of_actions(self, supplied_actions):
        """
        Takes a list of actions, queries the database for corresponding arns, adds them to the object.

        Arguments:
            supplied_actions: A list of supplied actions
        """
        # actions_list = get_dependent_actions(supplied_actions)
        dependent_actions = get_dependent_actions(supplied_actions)
        dependent_actions = [
            x for x in dependent_actions if x not in supplied_actions
        ]
        logger.debug("Adding by list of actions")
        logger.debug(f"Supplied actions: {str(supplied_actions)}")
        logger.debug(f"Dependent actions: {str(dependent_actions)}")
        arns_matching_supplied_actions = []

        # arns_matching_supplied_actions is a list of dicts.
        # It must do this rather than dictionaries because there will be duplicate
        #     values by nature of how the entries in the IAM database are structured.
        # I'll provide the example values here to improve readability.

        for action in supplied_actions:
            service_name, action_name = action.split(":")
            action_data = get_action_data(service_name, action_name)
            for row in action_data[service_name]:
                if row["resource_arn_format"] not in arns_matching_supplied_actions:
                    arns_matching_supplied_actions.append({
                        "resource_arn_format":
                        row["resource_arn_format"],
                        "access_level":
                        row["access_level"],
                        "action":
                        row["action"],
                    })

        # Identify the actions that do not support resource constraints
        # If that's the case, add it to the wildcard namespace. Otherwise, don't add it.

        actions_without_resource_constraints = []
        for item in arns_matching_supplied_actions:
            if item["resource_arn_format"] != "*":
                self.add_by_arn_and_access_level([item["resource_arn_format"]],
                                                 item["access_level"])
            else:
                actions_without_resource_constraints.append(item["action"])

        # If there are any dependent actions, we need to add them without resource constraints.
        # Otherwise, we get into issues where the amount of extra SIDs will balloon.
        # Also, the user has no way of knowing what those dependent actions are beforehand.
        # TODO: This is, in fact, a great opportunity to introduce conditions. But we aren't there yet.
        if len(dependent_actions) > 0:
            for dep_action in dependent_actions:
                self.add_action_without_resource_constraint(dep_action)
                # self.add_action_without_resource_constraint(str.lower(dep_action))
        # Now, because add_by_arn_and_access_level() adds all actions under an access level, we have to
        # remove all actions that do not match the supplied_actions. This is done in-place.
        logger.debug(
            "Purging actions that do not match the requested actions and dependent actions"
        )
        logger.debug(f"Supplied actions: {str(supplied_actions)}")
        logger.debug(f"Dependent actions: {str(dependent_actions)}")
        self.remove_actions_not_matching_these(supplied_actions +
                                               dependent_actions)
        for action in actions_without_resource_constraints:
            logger.debug(
                f"Deliberately adding the action {action} without resource constraints"
            )
            self.add_action_without_resource_constraint(action)
        logger.debug(
            "Removing actions that are in the wildcard arn (Resources = '*') as well as other statements that have "
            "resource constraints ")
        self.remove_actions_duplicated_in_wildcard_arn()
        logger.debug("Getting the rendered policy")
        rendered_policy = self.get_rendered_policy()
        return rendered_policy
Ejemplo n.º 8
0
#!/usr/bin/env python
from policy_sentry.shared.database import connect_db
from policy_sentry.querying.actions import get_dependent_actions
import json

if __name__ == '__main__':
    db_session = connect_db('bundled')
    output = get_dependent_actions(db_session, ["ec2:associateiaminstanceprofile"])
    print(json.dumps(output, indent=4))

"""
Output:

[
    "ec2:associateiaminstanceprofile",
    "iam:passrole"
]
"""
Ejemplo n.º 9
0
 def test_get_dependent_actions_several(self):
     self.assertEqual(get_dependent_actions(db_session, dependent_actions_several), ["chime:getcdrbucket", "s3:getbucketacl", "s3:getbucketlocation", "s3:getbucketlogging", "s3:getbucketversioning", "s3:getbucketwebsite"])
Ejemplo n.º 10
0
 def test_get_dependent_actions_double(self):
     self.assertEqual(get_dependent_actions(db_session, dependent_actions_double), ["shield:associatedrtlogbucket", "s3:getbucketpolicy", "s3:putbucketpolicy"])
Ejemplo n.º 11
0
 def test_get_dependent_actions_single(self):
     self.assertEqual(get_dependent_actions(db_session, dependent_actions_single), ["ec2:associateiaminstanceprofile", "iam:passrole"])