Package: repokid
RepokidRepokid uses Access Advisor provided byAardvarkto remove permissions granting access to unused services from the inline policies of IAM roles in
an AWS account.Getting StartedInstallmkvirtualenvrepokid
gitclonegit@github.com:Netflix/repokid.gitcdrepokid
pipinstall-e.
repokidconfigconfig.jsonDynamoDBYou will need aDynamoDBtable calledrepokid_roles(specify account and endpoint indynamo_dbin config file).The table should have the following properties:RoleId(string) as a primary partition key, no primary sort keyA global secondary index namedAccountwith a primary partition key ofAccountandRoleIdandAccountas projected attributesA global secondary index namedRoleNamewith a primary partition key ofRoleNameandRoleIdandRoleNameas projected attributesFor development, you can run dynamolocally.To run locally:docker-composeupThe endpoint for DynamoDB will behttp://localhost:8000. A DynamoDB admin panel can be found athttp://localhost:8001.If you run the development version the table and index will be created for you automatically.IAM PermissionsRepokid needs an IAM Role in each account that will be queried.  Additionally, Repokid needs to be launched with a role or user which cansts:AssumeRoleinto the different account roles.RepokidInstanceProfile:Only create one.Needs the ability to callsts:AssumeRoleinto all of the RepokidRoles.DynamoDB permissions for therepokid_rolestable and all indexes (specified inassume_rolesubsection ofdynamo_dbin config) and the ability to rundynamodb:ListTablesRepokidRole:Must exist in every account to be managed by repokid.Must have a trust policy allowingRepokidInstanceProfile.Name must be specified inconnection_iamin config file.Has these permissions:{"Version":"2012-10-17","Statement":[{"Action":["iam:DeleteInstanceProfile","iam:DeleteRole","iam:DeleteRolePolicy","iam:GetAccountAuthorizationDetails","iam:GetInstanceProfile","iam:GetRole","iam:GetRolePolicy","iam:ListInstanceProfiles","iam:ListInstanceProfilesForRole","iam:ListRolePolicies","iam:PutRolePolicy","iam:UpdateRoleDescription"],"Effect":"Allow","Resource":"*"}]}So if you are monitoringnaccounts, you will always needn+1roles. (nRepokidRoles and1RepokidInstanceProfile).Editing config.jsonRunningrepokid config config.jsoncreates a file that you will need to edit.  Find and update these fields:dynamodb: If using dynamo locally, set the endpoint tohttp://localhost:8010.  If using AWS hosted dynamo, set theregion,assume_role, andaccount_number.aardvark_api_location: The location to your Aardvark REST API.  Something likehttps://aardvark.yourcompany.net/api/1/advisorsconnection_iam: Setassume_roletoRepokidRole, or whatever you have called it.Optional ConfigRepokid uses filters to decide which roles are candidates to be repoed.  Filters may be configured to suit your
environment as described below.Blocklist FilterRoles may be excluded by adding them to the Blocklist filter.  One common reason to exclude a role is if
the corresponding workload performs occasional actions that may not have been observed but are known to be
required.  There are two ways to exclude a role:Exclude role name for all accounts: add it to a list in the configfilter_config.BlocklistFilter.allExclude role name for specific account: add it to a list in the configfilter_config.BlocklistFilter.<ACCOUNT_NUMBER>Blocklists can also be maintained in an S3 blocklist file.  They should be in the following form:{"arns":["arn1","arn2"],"names":{"role_name_1":["all","account_number_1"],"role_name_2":["account_number_2","account_number_3"]}}Exclusive FilterIf you prefer to repo only certain roles you can use the Exclusive Filter. Maybe you want to consider only roles used in production or by certain teams.
To select roles for repo-ing you may list their names in the configuration files. Shell style glob patterns are also supported.
Role selection can be specified per individual account or globally.
To activate this filter put"repokid.filters.exclusive:ExclusiveFilter"in the sectionactive_filtersof the config file.
To configure it you can start with the autogenerated config file, which has an example config in the"filter_config"section:"ExclusiveFilter": {
                   "all": [
                     "<GLOB_PATTERN>"
                     ],
                   "<ACCOUNT_NUMBER>": [
                     "<GLOB_PATTERN>"
                    ]
                   }Age FilterBy default the age filter excludes roles that are younger than 90 days.  To change this edit the config setting:filter_config.AgeFilter.minimum_age.Active FiltersNew filters can be created to support internal logic.  At Netflix we have several that are specific to our
use cases.  To make them active make sure they are in the Python path and add them in the config to the list in
the sectionactive_filters.Extending RepokidHooksRepokid is extensible via hooks that are called before, during, and after various operations as listed below.Hook nameContextAFTER_REPOrole, errorsAFTER_REPO_ROLESroles, errorsBEFORE_REPO_ROLESaccount_number, rolesAFTER_SCHEDULE_REPOrolesDURING_REPOABLE_CALCULATIONrole_id, arn, account_number, role_name, potentially_repoable_permissions, minimum_ageDURING_REPOABLE_CALCULATION_BATCHrole_batch, potentially_repoable_permissions, minimum_ageHooks must adhere to the following interface:fromrepokid.hooksimportimplements_hookfromrepokid.typesimportRepokidHookInput,RepokidHookOutput@implements_hook("TARGET_HOOK_NAME",1)defcustom_hook(input_dict:RepokidHookInput)->RepokidHookOutput:"""Hook functions are called with a dict containing the keys listed above based on the target hook.Any mutations made to the input and returned in the output will be passed on to subsequent hook funtions."""...Examples of hook implementations can be found inrepokid.hooks.loggers.FiltersCustom filters can be written to exclude roles from being repoed. Filters must adhere to the following interface:fromrepokid.filtersimportFilterfromrepokid.typesimportRepokidFilterConfigfromrepokid.roleimportRoleListclassCustomFilterName(Filter):def__init__(self,config:RepokidFilterConfig=None)->None:"""Filters are initialized with a dict containing the contents of `filter_config.FilterName`from the config file. This example would be initialized with `filter_config.CustomFilterName`.The configuration can be accessed via `self.config`If you don't need any custom initialization logic, you can leave this function out of yourfilter class."""super().__init__(config=config)# custom initialization logic goes here...defapply(self,input_list:RoleList)->RoleList:"""Determine roles to be excluded and return them as a RoleList"""...A simple filter implementation can be found inrepokid.filters.age. A more complex example is inrepokid.blocklist.age.How to UseOnce Repokid is configured, use it as follows:Standard flowUpdate role cache:repokid update_role_cache <ACCOUNT_NUMBER>Display role cache:repokid display_role_cache <ACCOUNT_NUMBER>Display information about a specific role:repokid display_role <ACCOUNT_NUMBER> <ROLE_NAME>Repo a specific role:repokid repo_role <ACCOUNT_NUMBER> <ROLE_NAME>Repo all roles in an account:repokid repo_all_roles <ACCOUNT_NUMBER> -cSchedulingRather than running a repo right now you can schedule one (schedule_repocommand). The duration between scheduling and eligibility is configurable, but by default roles can be repoed 7 days after scheduling.  You can then run a commandrepo_scheduled_rolesto only repo roles which have already been scheduled.Targeting a specific permissionSay that you find a given permission especially dangerous in your environment.  Here I'll uses3:PutObjectACLas an example. You can use Repokid to find all roles that have this permission (even those hidden in a wildcard), and then remove just that single permission.Find & Remove:Ensure the role cache is updated before beginning.Find roles with a given permission:repokid find_roles_with_permissions <permission>... [--output=ROLE_FILE]Remove permission from roles:repokid remove_permissions_from_roles --role-file=ROLE_FILE <permission>... [-c]Example:$ repokid find_roles_with_permissions "s3:putobjectacl" "sts:assumerole" --output=myroles.json
...
$ repokid remove_permissions_from_roles --role-file=myroles.json "s3:putobjectacl" "sts:assumerole" -cRolling backRepokid stores a copy of each version of inline policies it knows about.  These are added when
a different version of a policy is found duringupdate_role_cacheand any time a repo action
occurs.  To restore a previous version run:See all versions of roles:repokid rollback_role <ACCOUNT_NUMBER> <ROLE_NAME>Restore a specific version:repokid rollback_role <ACCOUNT_NUMBER> <ROLE_NAME> --selection=<NUMBER> -cStatsRepokid keeps counts of the total permissions for each role.  Stats are added any time anupdate_role_cacheorrepo_roleaction occur.  To output all stats to a CSV file run:repokid repo_stats <OUTPUT_FILENAME>.  An optional account number can be specified to output stats for a specific account only.LibraryNew inv0.14.2Repokid can be called as a library using therepokid.libmodule:fromrepokid.libimportdisplay_role,repo_role,update_role_cacheaccount_number="123456789012"display_role(account_number,"superCoolRoleName")update_role_cache(account_number)repo_role(account_number,"superCoolRoleName",commit=True)DispatcherRepokid Dispatcher is designed to listen for messages on a queue and perform actions.  So far the actions are:List repoable services from a roleSet or remove an opt-outList and perform rollbacks for a roleRepokid will respond on a configurable SNS topic with information about any success or failures. The Dispatcher
component exists to help with operationalization of the repo lifecycle across your organization. You may choose
to expose the queue directly to developers, but more likely this should be guarded because rolling back can be
a destructive action if not done carefully.DevelopmentReleasingVersioning is handled bysetupmeta. To create a new release:pythonsetup.pyversion--bumppatch--push# Inspect output and make sure it's what you expect# If all is well, commit and push the new tag:pythonsetup.pyversion--bumppatch--push--commit