Skip to content

Policy Minimization

The documentation on policy minimization is relevant to this library content. That documentation can be found here.

Note: The most relevant content from here is the minimize_statement_actions function.

Real-life usage of this function can be found in the command.write_policy module.

writing.minimize

Functions for Minimizing statements, heavily borrowed from policyuniverse. https://github.com/Netflix-Skunkworks/policyuniverse/

IAM Policies have character limits, which apply to individual policies, and there are also limits on the total aggregate policy sizes. As such, it is not possible to use exhaustive list of explicit IAM actions. To have granular control of specific IAM policies, we must use wildcards on IAM Actions, only in a programmatic manner.

This is typically performed by humans by reducing policies to s3:Get, ec2:Describe, and other approaches of the sort.

Netflix's PolicyUniverse has addressed this in their minimization code. We borrowed this logic from their code and modified it a bit to fit our needs.

https://aws.amazon.com/iam/faqs/ Q: How many policies can I attach to an IAM role? * For inline policies: You can add as many inline policies as you want to a user, role, or group, but the total aggregate policy size (the sum size of all inline policies) per entity cannot exceed the following limits: - User policy size cannot exceed 2,048 characters. - Role policy size cannot exceed 10,240 characters. - Group policy size cannot exceed 5,120 characters. * For managed policies: You can add up to 10 managed policies to a user, role, or group. * The size of each managed policy cannot exceed 6,144 characters.

check_min_permission_length(permission, minchars=None)

Adapted version of policyuniverse's _check_permission_length. We are commenting out the skipping prefix message https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L111

Source code in policy_sentry/writing/minimize.py
def check_min_permission_length(
    permission, minchars=None
):  # pylint: disable=missing-function-docstring
    """
    Adapted version of policyuniverse's _check_permission_length. We are commenting out the skipping prefix message
    https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L111
    """
    if minchars and len(permission) < int(minchars) and permission != "":
        # print(
        #     "Skipping prefix {} because length of {}".format(
        #         permission, len(permission)
        #     ),
        #     file=sys.stderr,
        # )
        return True
    return False

get_denied_prefixes_from_desired(desired_actions, all_actions)

Adapted version of policyuniverse's _get_denied_prefixes_from_desired, here: https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L101

Source code in policy_sentry/writing/minimize.py
def get_denied_prefixes_from_desired(
    desired_actions, all_actions
):  # pylint: disable=missing-function-docstring
    """
    Adapted version of policyuniverse's _get_denied_prefixes_from_desired, here: https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L101
    """
    denied_actions = all_actions.difference(desired_actions)
    denied_prefixes = set()
    for denied_action in denied_actions:
        for denied_prefix in _get_prefixes_for_action(denied_action):
            denied_prefixes.add(denied_prefix)

    return denied_prefixes

minimize_statement_actions(desired_actions, all_actions, minchars=None)

This is a condensed version of policyuniverse's minimize_statement_actions, changed for our purposes. https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L123

Source code in policy_sentry/writing/minimize.py
def minimize_statement_actions(
    desired_actions, all_actions, minchars=None
):  # pylint: disable=missing-function-docstring
    """
    This is a condensed version of policyuniverse's minimize_statement_actions, changed for our purposes.
    https://github.com/Netflix-Skunkworks/policyuniverse/blob/master/policyuniverse/expander_minimizer.py#L123
    """
    desired_actions = [x.lower() for x in desired_actions]
    minimized_actions = set()
    denied_prefixes = get_denied_prefixes_from_desired(desired_actions, all_actions)
    for action in desired_actions:
        if action in denied_prefixes:
            # print("Action is a denied prefix. Action: {}".format(action))
            minimized_actions.add(action)
            continue

        found_prefix = False
        prefixes = _get_prefixes_for_action(action)
        for prefix in prefixes:
            permission = prefix.split(":")[1]
            if check_min_permission_length(permission, minchars=minchars):
                continue
            # If the action name is not empty
            if prefix not in denied_prefixes:
                if permission != "":
                    if prefix not in desired_actions:
                        prefix = "{}*".format(prefix)
                    minimized_actions.add(prefix)
                    found_prefix = True
                    break

        if not found_prefix:
            logger.debug(
                "Could not suitable prefix. Defaulting to %s".format(prefixes[-1])
            )
            minimized_actions.add(prefixes[-1])
    # sort the actions
    minimized_actions_list = sorted(minimized_actions)

    return minimized_actions_list