身份:实体与组
Vault 支持多种身份验证方法,还允许在不同的挂载路径上启用相同类型的身份验证方法。每个 Vault 客户端可能有多个帐户,这些帐户具有在 Vault 服务器上启用的各种身份提供者。
Vault 客户端可以映射为实体,并且它们与身份验证提供程序的对应帐户可以映射为别名。本质上,每个实体都由零个或多个别名组成。Vault 内部使用身份机密引擎维护 Vault 识别的客户端。
举例来说,Bob 在 Github 和 LDAP 中都有帐户。 Github 和 LDAP 身份验证方法都在 Vault 服务器上启用,他可以使用他的任一帐户进行身份验证。虽然两个账户都属于 Bob,但是两个账户之间并没有关联来设置一些共同的属性。
解决方案
创建一个代表 Bob 的实体,并将代表他的每个帐户的别名关联为实体成员。我们可以在实体级别设置其他策略和元数据,以便两个帐户都可以继承。
当 Bob 使用他的任一帐户进行身份验证时,实体标识符将与经过身份验证的令牌相关联。当使用此类令牌时,它们的实体标识符会被审计记录,标记特定用户执行的操作的踪迹。
准备工作
要执行本教程的实验,我们需要:
- 一个已经初始化并解封的 Vault 服务器
- 安装 jq
启动一个 dev 服务
- 在命令行终端中,使用
root
作为根令牌启动一个 Vault dev 服务:
$ vault server -dev -dev-root-token-id root
Vault dev 服务默认运行于 127.0.0.1:8200
。该服务启动后已被初始化并解封。
- 开启一个新的终端,使用环境变量设置 Vault 命令行使用的 Vaul 服务地址:
$ export VAULT_ADDR=http://127.0.0.1:8200
- 使用环境变量设置 Vault 命令行使用的 Vaul 根令牌:
$ export VAULT_TOKEN=root
步骤一:创建一个带别名的实体
我们将创建一个分配了基本策略的新实体。该实体定义了两个实体别名,每个别名都分配了不同的策略。
场景:一个 ACME Inc. 的用户 Bob Smith 碰巧有两组凭据:bob
和 bsmith
。 Bob 可以使用他的任一帐户向 Vault 进行身份验证。要管理用户的帐户并将其链接到 QA 团队中的身份 Bob Smith
,您将为 Bob 创建一个实体。
请注意,为简化本教程,我们将使用 userpass
身份验证方法。在实际场景中,用户 bob
可能是一个存在于活动目录中的用户名,而 bsmith
可能是 Bob 在 GitHub 的用户名。为了模拟该行为,本教程中将在两个独立路径上启用 userpass
身份验证方法:userpass-test
以及 userpass-qa
,来模拟两种不同的身份验证方法。
场景策略
base.hcl
:
path "secret/data/training_*" {
capabilities = ["create", "read"]
}
test.hcl
:
path "secret/data/test" {
capabilities = [ "create", "read", "update", "delete" ]
}
team-qa.hcl
:
path "secret/data/team-qa" {
capabilities = [ "create", "read", "update", "delete" ]
}
请注意该场景假设已在 secret
路径上启用了 K/V v2 机密引擎。如果不了解 K/V 机密引擎,请先阅读相关文档。
现在,让我们使用合适的权限来创建 bob
和 bsmith
用户:
- 创建
base
策略:
$ vault policy write base -<<EOF
path "secret/data/training_*" {
capabilities = ["create", "read"]
}
EOF
- 创建
test
策略:
$ vault policy write test -<<EOF
path "secret/data/test" {
capabilities = [ "create", "read", "update", "delete" ]
}
EOF
- 创建
team-qa
策略:
$ vault policy write team-qa -<<EOF
path "secret/data/team-qa" {
capabilities = [ "create", "read", "update", "delete" ]
}
EOF
- 列出所有策略,确认
base
、test
以及team-qa
策略已就绪:
$ vault policy list
base
default
team-qa
test
root
- 在
userpass-test
路径上启用userpass
身份验证方法:
$ vault auth enable -path="userpass-test" userpass
- 在
userpass-test
上创建一个名为bob
的新用户,密码为training
,附加test
策略:
$ vault write auth/userpass-test/users/bob password="training" policies="test"
- 在另一个路径
userpass-qa
上启用userpass
身份验证方法:
$ vault auth enable -path="userpass-qa" userpass
- 在
userpass-qa
上创建一个名为bsmith
的新用户,密码为training
,附加team-qa
策略:
$ vault write auth/userpass-qa/users/bsmith password="training" policies="team-qa"
- 执行以下命令获取
userpass
身份验证方法挂载点的访问器:
$ vault auth list -detailed
Path Type Accessor ...
---- ---- -------- ...
token/ token auth_token_c5943123 ...
userpass-qa/ userpass auth_userpass_8c7b8e0f ...
userpass-test/ userpass auth_userpass_264d4705 ...
...
每一个 userpass
身份验证方法都有一个唯一的 Accessor 值作为标志符。
- 运行以下命令将
userpass-test
身份验证方法的访问器保存在名为access_test.txt
的文件里:
$ vault auth list -format=json | jq -r '.["userpass-test/"].accessor' > accessor_test.txt
- 类似的,运行以下命令将
userpass-qa
身份验证方法的访问器保存在名为access_qa.txt
的文件里:
$ vault auth list -format=json | jq -r '.["userpass-qa/"].accessor' > accessor_qa.txt
- 创建名为
bob-smith
的实体,将返回的实体 ID 保存在名为entity_id.txt
的文件内:
$ vault write -format=json identity/entity name="bob-smith" policies="base" \
metadata=organization="ACME Inc." \
metadata=team="QA" \
| jq -r ".data.id" > entity_id.txt
- 现在,通过创建一个实体别名来将用户
bob
添加到实体bob-smith
中。在名为bob
的实体的别名上设置自定义元数据“account”,并将其值设置为“Tester Account”:
$ vault write identity/entity-alias name="bob" \
canonical_id=$(cat entity_id.txt) \
mount_accessor=$(cat accessor_test.txt) \
custom_metadata=account="Tester Account"
请注意:要在实体别名上设置自定义元数据,需要运行 Vault 1.9 或更高版本。如果我们的 Vault 版本低于 v1.9,那么执行上述命令时请不要添加 custom_metadata=account="Tester Account"
。
输出样例:
Key Value
--- -----
canonical_id 24204b50-22a6-61f5-bd4b-803f1a4e4726
id ae2cdd0f-9807-7336-2265-5575c71837e7
- 重复以上步骤,通过创建一个实体别名来将用户
bsmith
添加到实体bob-smith
中。在名为bob
的实体的别名上设置自定义元数据“account”,并将其值设置为“QA Eng Account”:
$ vault write identity/entity-alias name="bsmith" \
canonical_id=$(cat entity_id.txt) \
mount_accessor=$(cat accessor_qa.txt) \
custom_metadata=account="QA Eng Account"
输出样例:
Key Value
--- -----
canonical_id 24204b50-22a6-61f5-bd4b-803f1a4e4726
id 6066f6af-bb1c-5310-58a1-fd9c8f151573
- 审查实体明细:
$ vault read -format=json identity/entity/id/$(cat entity_id.txt) | jq -r ".data"
样例输出:输出应包括实体别名、元数据(组织和团队)和基本策略:
{
"aliases": [
{
"canonical_id": "73503625-abcd-db22-08c3-c121d682d550",
"creation_time": "2021-11-17T05:33:48.040506Z",
"custom_metadata": {
"account": "Tester Account"
},
"id": "cf073e2e-41af-852f-848d-f67533c8a610",
"last_update_time": "2021-11-17T05:33:48.040506Z",
"local": false,
"merged_from_canonical_ids": null,
"metadata": null,
"mount_accessor": "auth_userpass_0a6936a7",
"mount_path": "auth/userpass-test/",
"mount_type": "userpass",
"name": "bob"
},
{
"canonical_id": "73503625-abcd-db22-08c3-c121d682d550",
"creation_time": "2021-11-17T05:33:48.107834Z",
"custom_metadata": {
"account": "QA Eng Account"
},
"id": "7add6763-ce53-d92a-c795-c8ae529ce6e7",
"last_update_time": "2021-11-17T05:33:48.107834Z",
"local": false,
"merged_from_canonical_ids": null,
"metadata": null,
"mount_accessor": "auth_userpass_ef4f8068",
"mount_path": "auth/userpass-qa/",
"mount_type": "userpass",
"name": "bsmith"
}
],
"creation_time": "2021-11-17T05:33:47.966585Z",
"direct_group_ids": [
"49e8b9e8-8933-1e14-f05c-e1c9674b142b"
],
"disabled": false,
"group_ids": [
"49e8b9e8-8933-1e14-f05c-e1c9674b142b"
],
"id": "73503625-abcd-db22-08c3-c121d682d550",
"inherited_group_ids": [],
"last_update_time": "2021-11-17T05:33:47.966585Z",
"merged_entity_ids": null,
"metadata": {
"organization": "ACME Inc.",
"team": "QA"
},
"mfa_secrets": {},
"name": "bob-smith",
"namespace_id": "root",
"policies": [
"base"
]
}
安全注意事项:避免在实体元数据中存储任何敏感的个人身份信息 (PII)。如果配置了性能复制,实体元数据将复制到其他集群。这可能严重违反 GDPR 规定。 Vault 1.9 引入了在不与 Vault 设置的元数据重叠的每个实体别名上设置自定义元数据的功能。如果身份验证方法是集群本地的,则元数据不会被复制到同一性能复制组中的其他集群。因此,建议改用实体别名上的自定义元数据。
步骤二:测试实体
为了更好地理解令牌如何从实体的策略继承功能,您将通过以 bob
身份登录来测试它。
- 首先以
bob
的身份登录,并将生成的客户端令牌保存在bob_token.txt
文件中:
$ vault login -format=json -method=userpass -path=userpass-test \
username=bob password=training \
| jq -r ".auth.client_token" > bob_token.txt
test
策略授予了对secret/test
路径的 CRUD 操作权限。测试确认我们可以向该路径写入机密:
$ VAULT_TOKEN=$(cat bob_token.txt) vault kv put secret/test owner="bob"
Key Value
--- -----
created_time 2021-11-06T02:12:02.104146Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
- 虽然用户名
bob
没有附加base
策略,但它的令牌集成了base
策略,因为bob
是bob-smith
实体的成员之一,而该实体附加了base
策略。检查 bob 的令牌是否集成了这些权限:
$ VAULT_TOKEN=$(cat bob_token.txt) vault token capabilities secret/data/training_test
create, read
base
策略授予了对secret/training_*
路径的创建与读取权限;所以bob
也可以对任意以secret/training_*
为前缀的路径执行创建以及读取操作。
那么 secret/team-qa
路径呢?
$ VAULT_TOKEN=$(cat bob_token.txt) vault token capabilities secret/data/team-qa
deny
用户 bob
只集成了关联实体的策略。用户只可以通过以 bsmith
的凭据登录才能访问 secret/team-qa
路径。
步骤三:创建一个内部组
请注意,请在执行步骤三的操作前,先使用先前配置实体所使用的令牌重新登录。
现在,我们要创建一个名为 engineers
的内部组,它的成员是我们在步骤一中创建的实体 bob-smith
:
名为 team-eng.hcl
的文件中定义了名为 team-eng
的组策略:
team-eng.hcl
:
path "secret/data/team/eng" {
capabilities = [ "create", "read", "update", "delete"]
}
注意:在继续下面的操作前,请确认环境变量 VAULT_TOKEN
被设置为了 root
。
- 创建名为
team-eng
的新策略:
$ vault policy write team-eng -<<EOF
path "secret/data/team/eng" {
capabilities = [ "create", "read", "update", "delete"]
}
EOF
- 创建名为
engineers
的内部组,将bob-smith
实体添加为组成员,并附加team-eng
策略:
$ vault write identity/group name="engineers" \
policies="team-eng" \
member_entity_ids=$(cat entity_id.txt) \
metadata=team="Engineering" \
metadata=region="North America"
输出样例:
Key Value
--- -----
id 0cd76d41-fe36-8c7b-4758-d36bbc212650
name engineers
现在,当我们用 bob
或是 bsmith
登录时,生成的令牌都继承了组级别的策略 team-eng
。
总结
默认情况下,Vault 创建内部组。当我们创建一个内部组时,我们可以设置组成员而不是组别名。组别名时 Vault 与外部身份提供者(例如 LDAP、GitHub 等)之间的映射。因此,只有在创建外部组时才定义组别名。对于内部组,我们指定 member_entity_ids
和/或 member_group_ids
。
步骤四:创建一个外部组
组织通常使用 LDAP、Okta 和 GitHub 等身份验证方法来处理 Vault 用户身份验证,并且在这些身份提供程序中定义个人用户的组成员身份。
为了管理组级别的授权,我们可以创建一个外部组以将 Vault 与外部身份提供者(身份验证提供者)链接起来,并将适当的策略附加到该组。
样例场景
所有属于 Github 组织 example-inc
中 training
团队的用户都被允许在 secret/education
路径上执行任意操作。
请注意,该场景假设 GitHub 上已经存在了名为 example-inc
的组织以及名为 training
的团队。
education.hcl
:
path "secret/data/education" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
- 创建名为
education
的新策略:
$ vault policy write education -<<EOF
path "secret/data/education" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
EOF
- 启用 GitHub 身份验证方法:
$ vault auth enable github
- 获取 GitHub 身份验证方法的挂载点访问器,并保存在名为
accessor_github.txt
的文件内:
$ vault auth list -format=json | jq -r '.["github/"].accessor' > accessor_github.txt
- 配置指向我们的 GitHub 组织:
$ vault write auth/github/config organization=example-inc
- 创建一个外部组,将组 ID 保存至文件
group_id.txt
中:
$ vault write -format=json identity/group name="education" \
policies="education" \
type="external" \
metadata=organization="Product Education" | jq -r ".data.id" > group_id.txt
- 创建一个组别名,其中
canonical_id
是组 ID,名称必须是存在的实际 GitHub 团队名称。:
$ vault write identity/group-alias name="training" \
mount_accessor=$(cat accessor_github.txt) \
canonical_id="$(cat group_id.txt)"
样例输出:
Key Value
--- -----
canonical_id 66818a45-ef85-0ff3-6c1e-37faf12ea55e
id 578944f0-dcfd-29fd-a763-d3f9431512d7