Repokid ======= []() [](https://travis-ci.com/Netflix/repokid) [](https://badge.fury.io/py/repokid) [](https://coveralls.io/github/Netflix/repokid?branch=master) [](https://discord.gg/9kwMWa6) <img align="center" alt="Repokid 标志" src="https://raw.githubusercontent.com/Netflix/repokid/master/docs/images/Repokid.png?raw=true" width="25%" display="block"> Repokid 使用 [Aardvark](https://github.com/Netflix-Skunkworks/aardvark) 提供的 Access Advisor,从 AWS 账户中 IAM 角色的内联策略中移除对未使用服务的访问权限。 ## 开始使用 ### 安装 ```bash mkvirtualenv repokid git clone [email protected]:Netflix/repokid.git cd repokid pip install -e . repokid config config.json ``` #### DynamoDB 您需要一个名为 `repokid_roles` 的 [DynamoDB](https://aws.amazon.com/dynamodb/) 表(在配置文件的 `dynamo_db` 中指定账户和端点)。 该表应具有以下属性: - `RoleId`(字符串)作为主分区键,无主排序键 - 一个名为 `Account` 的全局二级索引,主分区键为 `Account`,投影属性为 `RoleId` 和 `Account` - 一个名为 `RoleName` 的全局二级索引,主分区键为 `RoleName`,投影属性为 `RoleId` 和 `RoleName` 在开发环境中,您可以在 [本地](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) 运行 DynamoDB。 本地运行方式: ```bash docker-compose up ``` DynamoDB 的端点为 `http://localhost:8000`。DynamoDB 管理面板可在 `http://localhost:8001` 访问。 如果您运行开发版本,表和索引将自动为您创建。 #### IAM 权限 Repokid 需要在每个将要查询的账户中有一个 IAM 角色。此外,Repokid 需要使用一个可以 `sts:AssumeRole` 到不同账户角色的角色或用户来启动。 RepokidInstanceProfile: - 仅需创建一个。 - 需要能够调用所有 RepokidRole 的 `sts:AssumeRole`。 - 对 `repokid_roles` 表及其所有索引具有 DynamoDB 权限(在配置中 `dynamo_db` 的 `assume_role` 子部分指定),并且能够运行 `dynamodb:ListTables`。 RepokidRole: - 必须在每个由 repokid 管理的账户中存在。 - 必须具有允许 `RepokidInstanceProfile` 的信任策略。 - 名称必须在配置文件的 `connection_iam` 中指定。 - 具有以下权限: ```json { "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": "*" } ] } ``` 因此,如果您监控 `n` 个账户,则始终需要 `n+1` 个角色(`n` 个 RepokidRole 和 `1` 个 RepokidInstanceProfile)。 #### 编辑 config.json 运行 `repokid config config.json` 会创建一个需要编辑的文件。找到并更新以下字段: - `dynamodb`:如果使用本地 dynamo,将端点设置为 `http://localhost:8010`。如果使用 AWS 托管的 dynamo,请设置 `region`、`assume_role` 和 `account_number`。 - `aardvark_api_location`:您的 Aardvark REST API 的位置。类似 `https://aardvark.yourcompany.net/api/1/advisors`。 - `connection_iam`:将 `assume_role` 设置为 `RepokidRole` 或您自定义的名称。 ## 可选配置 Repokid 使用过滤器来决定哪些角色适合被 repo。过滤器可以根据您的环境进行配置,如下所述。 ### 黑名单过滤器 角色可以通过添加到黑名单过滤器来排除。一个常见的排除原因是,相应的工作负载可能会执行一些偶尔的操作,这些操作可能尚未被观察到,但已知是必需的。排除角色有两种方式: - 对所有账户排除角色名称:将其添加到配置 `filter_config.BlocklistFilter.all` 的列表中。 - 对特定账户排除角色名称:将其添加到配置 `filter_config.BlocklistFilter.<ACCOUNT_NUMBER>` 的列表中。 黑名单也可以维护在 S3 黑名单文件中。它们应采用以下格式: ```json { "arns": ["arn1", "arn2"], "names": {"role_name_1": ["all", "account_number_1"], "role_name_2": ["account_number_2", "account_number_3"]} } ``` ### 独占过滤器 如果您只希望对某些角色进行 repo,可以使用独占过滤器。也许您只想考虑用于生产环境或特定团队的角色。要选择要 repo 的角色,您可以在配置文件中列出它们的名称。支持 Shell 风格的 glob 模式。角色选择可以针对单个账户或全局进行指定。 要激活此过滤器,请在配置文件的 `active_filters` 部分添加 `"repokid.filters.exclusive:ExclusiveFilter"`。要配置它,可以从自动生成的配置文件开始,该文件在 `"filter_config"` 部分有一个示例配置: ``` "ExclusiveFilter": { "all": [ "<GLOB_PATTERN>" ], "<ACCOUNT_NUMBER>": [ "<GLOB_PATTERN>" ] } ``` ### 年龄过滤器 默认情况下,年龄过滤器排除创建时间少于 90 天的角色。要更改此设置,请编辑配置: `filter_config.AgeFilter.minimum_age`。 ### 活跃过滤器 可以创建新的过滤器来支持内部逻辑。在 Netflix,我们有多个针对我们用例特定的过滤器。要使它们生效,请确保它们在 Python 路径中,并将它们添加到配置文件的 `active_filters` 列表中。 ## 扩展 Repokid ### 钩子 Repokid 通过钩子进行扩展,这些钩子在下表列出的各种操作之前、期间和之后被调用。 | 钩子名称 | 上下文 | |-----------|---------| | `AFTER_REPO` | role, errors | | `AFTER_REPO_ROLES` | roles, errors | | `BEFORE_REPO_ROLES` | account_number, roles | | `AFTER_SCHEDULE_REPO` | roles | | `DURING_REPOABLE_CALCULATION` | role_id, arn, account_number, role_name, potentially_repoable_permissions, minimum_age | | `DURING_REPOABLE_CALCULATION_BATCH` | role_batch, potentially_repoable_permissions, minimum_age | 钩子必须遵循以下接口: ```python from repokid.hooks import implements_hook from repokid.types import RepokidHookInput, RepokidHookOutput @implements_hook("TARGET_HOOK_NAME", 1) def custom_hook(input_dict: RepokidHookInput) -> RepokidHookOutput: """钩子函数会接收一个包含上述基于目标钩子的键的字典。 对输入所做的任何更改并返回输出,将会传递给后续的钩子函数。 """ ... ``` 钩子实现的示例可以在 [`repokid.hooks.loggers`](https://github.com/netflix/repokid/blob/master/repokid/hooks/loggers/__init__.py) 中找到。 ### 过滤器 可以编写自定义过滤器来排除角色,使其不被 repo。过滤器必须遵循以下接口: ```python from repokid.filters import Filter from repokid.types import RepokidFilterConfig from repokid.role import RoleList class CustomFilterName(Filter): def __init__(self, config: RepokidFilterConfig = None) -> None: """过滤器在初始化时会接收一个字典,其中包含配置文件 `filter_config.FilterName` 的内容。 此示例将使用 `filter_config.CustomFilterName` 进行初始化。 可以通过 `self.config` 访问配置。 如果您不需要任何自定义初始化逻辑,可以在过滤器类中省略此函数。 """ super().__init__(config=config) # 自定义初始化逻辑在此 ... def apply(self, input_list: RoleList) -> RoleList: """确定要排除的角色,并以 RoleList 形式返回""" ... ``` 一个简单的过滤器实现可以在 [`repokid.filters.age`](https://github.com/netflix/repokid/blob/master/repokid/filters/age/__init__.py) 中找到。更复杂的示例在 [`repokid.blocklist.age`](https://github.com/netflix/repokid/blob/master/repokid/filters/blocklist/__init__.py) 中。 ## 如何使用 配置 Repokid 后,按如下方式使用: ### 标准流程 - 更新角色缓存:`repokid update_role_cache <ACCOUNT_NUMBER>` - 显示角色缓存:`repokid display_role_cache <ACCOUNT_NUMBER>` - 显示特定角色的信息:`repokid display_role <ACCOUNT_NUMBER> <ROLE_NAME>` - 对特定角色进行 repo:`repokid repo_role <ACCOUNT_NUMBER> <ROLE_NAME>` - 对账户中所有角色进行 repo:`repokid repo_all_roles <ACCOUNT_NUMBER> -c` ### 调度 您可以调度一个 repo 操作(`schedule_repo` 命令),而不是立即执行。调度与实际可执行之间的持续时间是可配置的,但默认情况下,角色在调度后 7 天才能进行 repo。然后您可以运行 `repo_scheduled_roles` 命令,仅对已调度的角色进行 repo。 ### 针对特定权限 假设您发现某个特定权限在您的环境中特别危险。这里以 `s3:PutObjectACL` 为例。您可以使用 Repokid 查找所有具有此权限的角色(甚至包括隐藏在通配符中的角色),然后仅移除该单个权限。 查找与移除: - 开始前确保角色缓存已更新。 - 查找具有给定权限的角色:`repokid find_roles_with_permissions <permission>... [--output=ROLE_FILE]` - 从角色中移除权限:`repokid remove_permissions_from_roles --role-file=ROLE_FILE <permission>... [-c]` 示例: ``` $ 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" -c ``` ### 回滚 Repokid 会存储其知道的每个内联策略版本的副本。这些副本会在 `update_role_cache` 期间发现策略的不同版本时以及每次 repo 操作发生时添加。要恢复以前版本,运行: 查看角色的所有版本:`repokid rollback_role <ACCOUNT_NUMBER> <ROLE_NAME>` 恢复特定版本:`repokid rollback_role <ACCOUNT_NUMBER> <ROLE_NAME> --selection=<NUMBER> -c` ### 统计 Repokid 会统计每个角色的总权限数量。任何 `update_role_cache` 或 `repo_role` 操作发生时都会添加统计信息。要将所有统计信息输出到 CSV 文件,运行:`repokid repo_stats <OUTPUT_FILENAME>`。可以指定可选的账户编号,以仅输出特定账户的统计信息。 ### 库使用 > 自 `v0.14.2` 起新增 可以使用 `repokid.lib` 模块将 Repokid 作为库调用: ```python from repokid.lib import display_role, repo_role, update_role_cache account_number = "123456789012" display_role(account_number, "superCoolRoleName") update_role_cache(account_number) repo_role(account_number, "superCoolRoleName", commit=True) ``` ## Dispatcher ## Repokid Dispatcher 旨在监听队列中的消息并执行操作。目前支持的操作有: - 列出角色中可 repo 的服务 - 设置或移除退出选择 - 列出并执行角色的回滚 Repokid 将通过一个可配置的 SNS 主题,回复有关任何成功或失败的信息。Dispatcher 组件旨在帮助您在整个组织中操作化 repo 生命周期。您可以选择直接向开发者公开队列,但更可能的是应该加以保护,因为如果操作不当,回滚可能是一种破坏性操作。 ## 开发 ### 发布 版本管理由 [setupmeta](https://github.com/zsimic/setupmeta) 处理。要创建新版本: ```bash python setup.py version --bump patch --push # 检查输出,确保符合预期 # 如果一切正常,提交并推送新标签: python setup.py version --bump patch --push --commit ```