Esempio n. 1
0
 def test_nodes(self):
     with self.assertRaises(ValueError):
         Node(arn='arn:aws:iam::000000000000:group/notauser', id_value='AIDA00000000000000000', attached_policies=[],
              group_memberships=[], trust_policy=None, instance_profile=None, num_access_keys=0,
              active_password=False, is_admin=False, permissions_boundary=None, has_mfa=False, tags={})
     try:
         Node(arn='arn:aws:iam::000000000000:user/auser', id_value='AIDA00000000000000001', attached_policies=[],
              group_memberships=[], trust_policy=None, instance_profile=None, num_access_keys=0,
              active_password=False, is_admin=False, permissions_boundary=None, has_mfa=False, tags={})
     except Exception as ex:
         self.fail('Unexpected error: ' + str(ex))
Esempio n. 2
0
def build_playground_graph() -> Graph:
    """Constructs and returns a Graph objects with many nodes, edges, groups, and policies"""
    common_iam_prefix = 'arn:aws:iam::000000000000:'

    # policies to use and add
    admin_policy = Policy('arn:aws:iam::aws:policy/AdministratorAccess', 'AdministratorAccess', _get_admin_policy())
    ec2_for_ssm_policy = Policy('arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM', 'AmazonEC2RoleforSSM',
                                _get_ec2_for_ssm_policy())
    s3_full_access_policy = Policy('arn:aws:iam::aws:policy/AmazonS3FullAccess', 'AmazonS3FullAccess',
                                   _get_s3_full_access_policy())
    jump_policy = Policy('arn:aws:iam::000000000000:policy/JumpPolicy', 'JumpPolicy', _get_jump_policy())
    policies = [admin_policy, ec2_for_ssm_policy, s3_full_access_policy, jump_policy]

    # IAM role trust docs to be used
    ec2_trusted_policy_doc = _make_trust_document({'Service': 'ec2.amazonaws.com'})
    root_trusted_policy_doc = _make_trust_document({'AWS': 'arn:aws:iam::000000000000:root'})
    alt_root_trusted_policy_doc = _make_trust_document({'AWS': '000000000000'})
    other_acct_trusted_policy_doc = _make_trust_document({'AWS': '999999999999'})

    # nodes to add
    nodes = []
    # Regular admin user
    nodes.append(Node(common_iam_prefix + 'user/admin', 'AIDA00000000000000000', [admin_policy], [], None, None, 1, True, True))

    # Regular ec2 role
    nodes.append(Node(common_iam_prefix + 'role/ec2_ssm_role', 'AIDA00000000000000001', [ec2_for_ssm_policy], [],
                      ec2_trusted_policy_doc, common_iam_prefix + 'instance-profile:/ec2_ssm_role', 0, False, False))

    # ec2 role with admin
    nodes.append(Node(common_iam_prefix + 'role/ec2_admin_role', 'AIDA00000000000000002', [ec2_for_ssm_policy], [], ec2_trusted_policy_doc,
                      common_iam_prefix + 'instance-profile/ec2_admin_role', 0, False, True))

    # assumable role with s3 access
    nodes.append(Node(common_iam_prefix + 'role/s3_access_role', 'AIDA00000000000000003', [s3_full_access_policy], [], root_trusted_policy_doc,
                      None, 0, False, False))

    # second assumable role with s3 access with alternative trust policy
    nodes.append(Node(common_iam_prefix + 'role/s3_access_role_alt', 'AIDA00000000000000004', [s3_full_access_policy], [],
                 alt_root_trusted_policy_doc, None, 0, False, False))

    # externally assumable role with s3 access
    nodes.append(Node(common_iam_prefix + 'role/external_s3_access_role', 'AIDA00000000000000005', [s3_full_access_policy], [],
                      other_acct_trusted_policy_doc, None, 0, False, False))

    # jump user with access to sts:AssumeRole
    nodes.append(Node(common_iam_prefix + 'user/jumpuser', 'AIDA00000000000000006', [jump_policy], [], None, None, 1, True, False))

    # edges to add
    edges = obtain_edges(None, checker_map.keys(), nodes, sys.stdout, True)

    return Graph(nodes, edges, policies, [], _get_default_metadata())
Esempio n. 3
0
def _build_user_with_policy(policy_dict, policy_name='single_user_policy', user_name='asdf', number='0') -> Node:
    """Helper function: builds an IAM User with a given input policy."""
    policy = Policy('arn:aws:iam::000000000000:policy/{}'.format(policy_name), policy_name, policy_dict)
    result = Node(
        'arn:aws:iam::000000000000:user/{}'.format(user_name),
        'AIDA0000000000000000{}'.format(number),
        [policy],
        [],
        None,
        None,
        1,
        True,
        False
    )
    return result
Esempio n. 4
0
    def test_local_mfa_handling(self):
        mfa_policy = Policy(
            arn='arn:aws:iam::000000000000:policy/mfa_policy',
            name='mfa_policy',
            policy_doc={
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Sid":
                    "AllowManageOwnUserMFA",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:DeactivateMFADevice", "iam:EnableMFADevice",
                        "iam:GetUser", "iam:ListMFADevices",
                        "iam:ResyncMFADevice"
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}"
                }, {
                    "Sid":
                    "DenyAllExceptListedIfNoMFA",
                    "Effect":
                    "Deny",
                    "NotAction": [
                        "iam:CreateVirtualMFADevice", "iam:EnableMFADevice",
                        "iam:GetUser", "iam:ListMFADevices",
                        "iam:ListVirtualMFADevices", "iam:ResyncMFADevice",
                        "sts:GetSessionToken"
                    ],
                    "Resource":
                    "*",
                    "Condition": {
                        "BoolIfExists": {
                            "aws:MultiFactorAuthPresent": "false"
                        }
                    }
                }]
            })
        s3_policy = Policy(
            'arn:aws:iam::000000000000:policy/s3access', 's3access', {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Sid": "AllowViewAccountInfo",
                    "Effect": "Allow",
                    "Action": "s3:ListAllMyBuckets",
                    "Resource": "*"
                }]
            })
        test_node = Node('arn:aws:iam::000000000000:user/uses_mfa',
                         'AIDA00000000000000000', [mfa_policy, s3_policy], [],
                         None, None, 1, False, False, None, True, None)

        print(mfa_policy.to_dictionary())
        print(s3_policy.to_dictionary())
        print(test_node.to_dictionary())

        # Test that lack of MFA conditions is not allowed (note the function call diff)
        # This matches policy sim feedback
        auth_result = local_check_authorization(test_node,
                                                's3:ListAllMyBuckets', '*', {})
        self.assertFalse(auth_result)

        # Test that MFA set to false is disallowed
        auth_result, mfa_result = local_check_authorization_handling_mfa(
            test_node, 's3:ListAllMyBuckets', '*',
            {'aws:MultiFactorAuthPresent': 'false'})
        self.assertFalse(auth_result)
        self.assertFalse(mfa_result)

        # Test that testing both with and without MFA yields correct results
        auth_result, mfa_result = local_check_authorization_handling_mfa(
            test_node, 's3:ListAllMyBuckets', '*', {})
        self.assertTrue(auth_result)
        self.assertTrue(mfa_result)

        # Test that iam:EnableMFADevice is allowed despite lack of MFA
        auth_result, mfa_result = local_check_authorization_handling_mfa(
            test_node, 'iam:EnableMFADevice',
            'arn:aws:iam::000000000000:user/uses_mfa', {})

        self.assertFalse(mfa_result)
        self.assertTrue(auth_result)
Esempio n. 5
0
    def create_graph_from_local_disk(cls, root_directory: str):
        """Generates a Graph object by pulling data from disk at root_directory.

        Structure:
        | <root_directory parameter>
        |---- metadata.json
        |---- graph/
        |-------- nodes.json
        |-------- edges.json
        |-------- policies.json
        |-------- groups.json

        Loads metadata, then policies, then groups, then nodes, then edges. Specific ordering is for handling
        different dependencies when generating the objects.

        Validates, using metadata, that the version of Principal Mapper that created the graph is the same
        major/minor version of the current version of Principal Mapper. Raises a ValueError otherwise.
        """
        rootpath = root_directory
        if not os.path.exists(rootpath):
            raise ValueError('Did not find file at: {}'.format(rootpath))
        graphdir = os.path.join(rootpath, 'graph')
        metadatafilepath = os.path.join(rootpath, 'metadata.json')
        nodesfilepath = os.path.join(graphdir, 'nodes.json')
        edgesfilepath = os.path.join(graphdir, 'edges.json')
        policiesfilepath = os.path.join(graphdir, 'policies.json')
        groupsfilepath = os.path.join(graphdir, 'groups.json')

        with open(metadatafilepath) as f:
            metadata = json.load(f)

        current_pmapper_version = packaging.version.parse(principalmapper.__version__)
        loaded_graph_version = packaging.version.parse(metadata['pmapper_version'])
        if current_pmapper_version.release[0] != loaded_graph_version.release[0] or \
                current_pmapper_version.release[1] != loaded_graph_version.release[1]:
            raise ValueError('Loaded Graph data was from a different version of Principal Mapper ({}), but the current '
                             'version of Principal Mapper ({}) may not support it. Either update the stored Graph data '
                             'and its metadata, or regraph the account.'.format(loaded_graph_version,
                                                                                current_pmapper_version))

        policies = []
        with open(policiesfilepath) as f:
            policies_file_contents = json.load(f)

        for policy in policies_file_contents:
            policies.append(Policy(arn=policy['arn'], name=policy['name'], policy_doc=policy['policy_doc']))

        with open(groupsfilepath) as f:
            unresolved_groups = json.load(f)
        groups = []
        for group in unresolved_groups:
            # dig through string list of attached policies to match up with policy objects with matching ARNs
            group_policies = []
            for policy_ref in group['attached_policies']:
                for policy in policies:
                    if policy_ref['arn'] == policy.arn and policy_ref['name'] == policy.name:
                        group_policies.append(policy)
                        break
            groups.append(Group(arn=group['arn'], attached_policies=group_policies))

        with open(nodesfilepath) as f:
            unresolved_nodes = json.load(f)
        nodes = []
        for node in unresolved_nodes:
            # dig through string list of groups and policies to match up with group and policy objects
            node_policies = []
            group_memberships = []
            for policy_ref in node['attached_policies']:
                for policy in policies:
                    if policy_ref['arn'] == policy.arn and policy_ref['name'] == policy.name:
                        node_policies.append(policy)
                        break
            for group in groups:
                if group.arn in node['group_memberships']:
                    group_memberships.append(group)
                    break
            nodes.append(Node(arn=node['arn'], id_value=node['id_value'], attached_policies=node_policies,
                              group_memberships=group_memberships, trust_policy=node['trust_policy'],
                              instance_profile=node['instance_profile'], num_access_keys=node['access_keys'],
                              active_password=node['active_password'], is_admin=node['is_admin']))

        with open(edgesfilepath) as f:
            unresolved_edges = json.load(f)
        edges = []
        for edge in unresolved_edges:
            # dig through nodes to find matching ARNs
            source = None
            destination = None
            for node in nodes:
                if source is None and node.arn == edge['source']:
                    source = node
                if destination is None and node.arn == edge['destination']:
                    destination = node
                if source is not None and destination is not None:
                    break
            edges.append(Edge(source=source, destination=destination, reason=edge['reason']))

        return Graph(nodes=nodes, edges=edges, policies=policies, groups=groups, metadata=metadata)
Esempio n. 6
0
 def test_service_linked_role_avoids_scp_restriction(self):
     principal = Node(
         'arn:aws:iam::000000000000:role/AWSServiceRoleForSupport',
         'AROAASDF', [
             Policy(
                 'arn:aws:iam::000000000000:role/AWSServiceRoleForS3Support',
                 'inline-1', {
                     'Version':
                     '2012-10-17',
                     'Statement': [{
                         'Effect': 'Allow',
                         'Action': 's3:*',
                         'Resource': '*'
                     }]
                 })
         ], None, {
             'Version':
             '2012-10-17',
             'Statement': [{
                 'Effect': 'Allow',
                 'Action': 'sts:AssumeRole',
                 'Principal': {
                     'Service': 's3support.amazonaws.com'
                 }
             }]
         }, None, 0, False, False, None, False, None)
     # SCP list of lists, this would be akin to an account in the root OU with the S3 service denied
     scp_collection = [[{
         "Version":
         "2012-10-17",
         "Statement": [{
             "Effect": "Allow",
             "Action": "*",
             "Resource": "*"
         }]
     }],
                       [{
                           "Version":
                           "2012-10-17",
                           "Statement": [{
                               "Effect": "Allow",
                               "Action": "*",
                               "Resource": "*"
                           }]
                       }, {
                           "Version":
                           "2012-10-17",
                           "Statement": [{
                               "Effect": "Deny",
                               "Action": ["s3:*"],
                               "Resource": "*",
                               "Sid": "Statement1"
                           }]
                       }]]
     self.assertTrue(
         local_check_authorization_full(principal, 's3:CreateBucket',
                                        'arn:aws:s3:::fakebucket', {}, None,
                                        None, scp_collection, None),
         'AWSServiceRoleFor... check failed, this role should have access DESPITE the SCPs'
     )
     self.assertFalse(
         local_check_authorization_full(principal, 'ec2:RunInstances', '*',
                                        {}, None, None, scp_collection,
                                        None))
Esempio n. 7
0
def build_graph_with_one_admin() -> Graph:
    """Constructs and returns a Graph object with one node that is an admin"""
    admin_user_arn = 'arn:aws:iam::000000000000:user/admin'
    policy = Policy(admin_user_arn, 'InlineAdminPolicy', _get_admin_policy())
    node = Node(admin_user_arn, 'AIDA00000000000000000', [policy], [], None, None, 1, True, True)
    return Graph([node], [], [policy], [], _get_default_metadata())