
repokid v0.19.8
AWS 最小权限:面向分布式高速部署
Repokid
Repokid 使用 Aardvark 提供的 Access Advisor,从 AWS 账户中 IAM 角色的内联策略中移除对未使用服务的访问权限。
开始使用
安装
mkvirtualenv repokid
git clone [email protected]:Netflix/repokid.git
cd repokid
pip install -e .
repokid config config.json
DynamoDB
您需要一个名为 repokid_roles 的 DynamoDB 表(在配置文件的 dynamo_db 中指定账户和端点)。
该表应具有以下属性:
RoleId(字符串)作为主分区键,无主排序键- 一个名为
Account的全局二级索引,主分区键为Account,投影属性为RoleId和Account - 一个名为
RoleName的全局二级索引,主分区键为RoleName,投影属性为RoleId和RoleName
在开发环境中,您可以在 本地 运行 DynamoDB。
本地运行方式:
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中指定。 - 具有以下权限:
{
"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 黑名单文件中。它们应采用以下格式:
{
"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 |
钩子必须遵循以下接口:
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 中找到。
过滤器
可以编写自定义过滤器来排除角色,使其不被 repo。过滤器必须遵循以下接口:
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 中找到。更复杂的示例在 repokid.blocklist.age 中。
如何使用
配置 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 作为库调用:
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 处理。要创建新版本:
python setup.py version --bump patch --push
# 检查输出,确保符合预期
# 如果一切正常,提交并推送新标签:
python setup.py version --bump patch --push --commit