本文为 SPIRE 官方文档 plugin_server_nodeattestor_gcp_iit.md 的中文译本,以英文原文为准。 🔗 穿越源码分析:Server 详解 · NodeAttestor
Server 插件:NodeAttestor "gcp_iit"
必须与 Agent 侧 gcp_iit 插件配合使用
gcp_iit 插件使用 GCP 实例身份令牌(GCP Instance Identity Token)自动对实例进行认证。它还允许运维人员在定义 SPIFFE ID 认证策略时使用 GCP 实例 ID(Instance ID)。由 gcp_iit 认证器认证的 agent 会被签发形如 spiffe://TRUST_DOMAIN/spire/agent/gcp_iit/PROJECT_ID/INSTANCE_ID 的 SPIFFE ID。本插件要求提供一个允许认证节点的 ProjectID 允许列表(allow list)。这也意味着你不应在同一个 GCP 项目中运行多个信任域(trust domain)。
Configuration(配置)
| Configuration | Description | Default |
|---|---|---|
projectid_allow_list | 允许认证节点的 ProjectID 列表。 | |
use_instance_metadata | 若为 true,则从 Google Compute Engine API 获取实例元数据(metadata),用于补充插件生成的节点 selector。 | false |
service_account_file | 用于向 Google Compute Engine API 认证的 service account 文件路径。 | |
allowed_label_keys | 参与构建 selector 的实例标签键(label key) | |
allowed_metadata_keys | 参与构建 selector 的实例元数据键(metadata key) | |
max_metadata_value_size | 设置插件在构建 selector 时所考虑的元数据值(metadata value)的最大大小 | 128 |
agent_path_template | Agent SPIFFE ID 的 URL 路径部分格式,以 text/template 格式描述。 | "/{{ .PluginName }}/{{ .ProjectID }}/{{ .InstanceID }}" |
一份示例配置:
NodeAttestor "gcp_iit" {
plugin_data {
projectid_allow_list = ["project-123"]
}
}Selectors(选择器)
本插件基于实例身份令牌(Instance Identity Token)中包含的信息生成以下 selector:
| Selector | Example | Description |
|---|---|---|
gcp_iit:project-id | gcp_iit:project-id:big-kahuna-123456 | 包含该实例的项目 ID |
gcp_iit:zone | gcp_iit:zone:us-west1-b | 包含该实例的可用区(zone) |
gcp_iit:instance-name | gcp_iit:instance-name:blog-server | 实例名称 |
gcp_iit:sa | gcp_iit:sa:123456789-compute@developer.gserviceaccount.com | Service account 邮箱 |
如果 use_instance_metadata 为 true,则会向 Google Compute Engine API 查询实例元数据,用于填充以下额外的 selector:
| Selector | Example | Description |
|---|---|---|
gcp_iit:tag | gcp_iit:tag:blog-server | 实例标签(tag)(每个一个 selector) |
gcp_iit:label | gcp_iit:label:key:value | 实例标签(label) |
gcp_iit:metadata | gcp_iit:metadata:key:value | 实例元数据(见下方注意事项) |
并非所有实例的 label 和 metadata 值都对节点选择有用。为避免生成大量无用的 selector,label 和 metadata 默认不会被使用。若要显式启用某个特定的 label 或 metadata 值,请在 allowed_label_keys 或 allowed_metadata_keys 配置项中指定其键名。
实例 metadata 可容纳最大 256KiB 的大值。为避免将大量数据写入数据存储(datastore),插件会强制执行一个元数据值大小上限。如果遇到某个被允许的(即键名已在 allowed_metadata_keys 中指定)metadata 值超过该上限,认证将失败。
metadata 和 label 的值是可选的。如果值不存在,对应的 selector 仍会带有一个尾部冒号(即 gcp_iit:label:<key>:、gcp_iit:metadata:<key>:)。
通过 Google Compute Engine API 进行认证
插件使用应用默认凭据(Application Default Credentials)向 Google Compute Engine API 进行认证,具体见 Setting Up Authentication For Server to Server 文档。当 SPIRE Server 运行在 GCP 内部时,它会使用其所在实例可用的默认 service account 凭据。当运行在 GCP 之外,或需要使用非默认凭据时,可以通过 GOOGLE_APPLICATION_CREDENTIALS 环境变量或 service_account_file 配置项(见 Configuration)指定包含凭据的 service account 文件路径。
该 service account 必须具备 IAM 权限和授权作用域(Authorization Scopes),以授予对以下 API 的访问:
Agent 路径模板(Agent Path Template)
Agent 路径模板(agent path template)是一种自定义 agent SPIFFE ID 生成格式的方式。模板格式化器采用 Golang text/template 约定,可以引用插件提供的值,或 Compute Engine 身份令牌中的值。关于模板引擎的详情,见模板引擎文档。注意,agent 的 SPIFFE ID 在一个信任域内必须唯一,因此如果两个 agent 使用了相同的 service account,则路径模板中必须包含额外的、能够完全区分它们的标识符。
一些有用的值包括:
| Value | Description |
|---|---|
| .PluginName | 插件名称 |
| .ProjectID | 创建实例所在项目的 ID |
| .InstanceID | 该令牌所属实例的唯一 ID |
| .ProjectNumber | 创建实例所在项目的唯一编号 |
| .Zone | 实例所在的可用区(zone) |
| .InstanceCreationTimestamp | 一个 Unix 时间戳,表示实例的创建时间 |
| .ServiceAccount | Service account 邮箱("@" 被替换为 "_") |
Security Considerations(安全考量)
实例身份令牌(Instance Identity Token)是本认证器用于证明节点身份的凭据,默认情况下节点上运行的任何进程都可访问它。因此,节点上的非 agent 代码有可能向 SPIRE Server 完成认证,从而获取该节点被授权运行的任意工作负载身份(workload identity)。
尽管许多运维人员会选择配置系统以阻止对实例身份令牌的访问,但 SPIRE 项目无法保证这一安全态势。为缓解相关风险,gcp_iit 节点认证器实现了首次使用即信任(Trust On First Use,简称 TOFU)语义。对于任意给定节点,认证只能发生一次,后续的认证尝试都将被拒绝。
非 agent 代码仍有可能先于 SPIRE Agent 完成节点认证;不过这种情况很容易被迅速发现,因为此时 SPIRE Agent 将无法启动,并且 SPIRE Agent 和 SPIRE Server 都会记录该事件。此类情况应作为可能的安全事件加以调查。