Exemple #1
0
def get_repoable_permissions(
    account_number: str,
    role_name: str,
    permissions: Set[str],
    aa_data: List[Dict[str, Any]],
    no_repo_permissions: Dict[str, Any],
    role_id: str,
    minimum_age: int,
    hooks: RepokidHooks,
) -> Set[str]:
    """
    Generate a list of repoable permissions for a role based on the list of all permissions the role's policies
    currently allow and Access Advisor data for the services included in the role's policies.

    The first step is to come up with a list of services that were used within the time threshold (the same defined)
    in the age filter config. Permissions are repoable if they aren't in the used list, aren't in the constant list
    of unsupported services/actions (IAM_ACCESS_ADVISOR_UNSUPPORTED_SERVICES, IAM_ACCESS_ADVISOR_UNSUPPORTED_ACTIONS),
    and aren't being temporarily ignored because they're on the no_repo_permissions list (newly added).

    Args:
        account_number
        role_name
        permissions (set): The full set of permissions that the role's permissions allow
        aa_data (list): A list of Access Advisor data for a role. Each element is a dictionary with a couple required
                        attributes: lastAuthenticated (epoch time in milliseconds when the service was last used and
                        serviceNamespace (the service used)
        no_repo_permissions (dict): Keys are the name of permissions and values are the time the entry expires
        minimum_age: Minimum age of a role (in days) for it to be repoable
        hooks: Dict containing hook names and functions to run

    Returns:
        set: Permissions that are 'repoable' (not used within the time threshold)
    """
    potentially_repoable_permissions = _get_potentially_repoable_permissions(
        role_name,
        account_number,
        aa_data,
        permissions,
        no_repo_permissions,
        minimum_age,
    )

    hooks_output = call_hooks(
        hooks,
        "DURING_REPOABLE_CALCULATION",
        {
            "account_number": account_number,
            "role_name": role_name,
            "potentially_repoable_permissions":
            potentially_repoable_permissions,
            "minimum_age": minimum_age,
            "role_id": role_id,
        },
    )

    logger.debug(
        "Repoable permissions for role {role_name} in {account_number}:\n{repoable}"
        .format(
            role_name=role_name,
            account_number=account_number,
            repoable="".join(
                "{}: {}\n".format(perm, decision.decider)
                for perm, decision in list(
                    hooks_output["potentially_repoable_permissions"].items())),
        ))

    return {
        permission_name
        for permission_name, permission_value in list(
            hooks_output["potentially_repoable_permissions"].items())
        if permission_value.repoable
    }
Exemple #2
0
    def repo(self,
             hooks: RepokidHooks,
             commit: bool = False,
             scheduled: bool = False) -> List[str]:
        errors: List[str] = []

        eligible, reason = self.is_eligible_for_repo()
        if not eligible:
            errors.append(
                f"Role {self.role_name} not eligible for repo: {reason}")
            return errors

        self.calculate_repo_scores(
            self.config["filter_config"]["AgeFilter"]["minimum_age"],
            hooks  # type: ignore
        )
        try:
            repoed_policies, deleted_policy_names = self.get_repoed_policy(
                scheduled=scheduled)
        except MissingRepoableServices as e:
            errors.append(f"Role {self.role_name} cannot be repoed: {e}")
            return errors

        if inline_policies_size_exceeds_maximum(repoed_policies):
            error = (
                "Policies would exceed the AWS size limit after repo for role: {} in account {}.  "
                "Please manually minify.".format(self.role_name, self.account))
            logger.error(error)
            errors.append(error)
            self.repo_scheduled = 0
            self.scheduled_perms = []
            self.store(["repo_scheduled", "scheduled_perms"])
            return errors

        if not commit:
            log_deleted_and_repoed_policies(deleted_policy_names,
                                            repoed_policies, self.role_name,
                                            self.account)
            return errors

        conn = self.config["connection_iam"]  # type: ignore
        conn["account_number"] = self.account

        for name in deleted_policy_names:
            try:
                delete_policy(name, self.role_name, self.account, conn)
            except IAMError as e:
                logger.error(e)
                errors.append(str(e))

        if repoed_policies:
            try:
                replace_policies(repoed_policies, self.role_name, self.account,
                                 conn)
            except IAMError as e:
                logger.error(e)
                errors.append(str(e))

        current_policies = (get_role_inline_policies(self.dict(by_alias=True),
                                                     **conn) or {})
        self.add_policy_version(current_policies, source="Repo")

        # regardless of whether we're successful we want to unschedule the repo
        self.repo_scheduled = 0
        self.scheduled_perms = []

        call_hooks(hooks, "AFTER_REPO", {"role": self, "errors": errors})

        if not errors:
            # repos will stay scheduled until they are successful
            self.repoed = datetime.datetime.now(
                tz=datetime.timezone.utc).isoformat()
            update_repoed_description(self.role_name, conn)
            logger.info("Successfully repoed role: {} in account {}".format(
                self.role_name, self.account))
        try:
            self.store()
        except RoleStoreError:
            logger.exception("failed to store role after repo", exc_info=True)
        return errors