本文为 SPIRE 官方文档 plugin_server_nodeattestor_azure_imds.md 的中文译本,以英文原文为准。 🔗 穿越源码分析:Server 详解 · NodeAttestor
Server 插件:NodeAttestor "azure_imds"
必须与 Agent 侧 azure_imds 插件配合使用
azure_imds 插件是 Azure 节点认证器的较新版本,旨在使用 Azure 实例元数据服务(Instance Metadata Service,IMDS)的已认证文档(attested document)对运行在 Microsoft Azure 中的节点进行认证。该文档由 Azure 签名,包含诸如 subscription ID 和 VM ID 等信息。与较旧的 azure_msi 插件不同,azure_imds 不要求虚拟机(VM)具备托管标识(managed identity),因而适用于更广泛的 Azure 虚拟机。
ID 的格式为:
spiffe://<trust_domain>/spire/agent/azure_imds/<tenant_id>/<subscription_id>/<vm_id>server 本身无需运行在 Azure 中即可执行节点认证或解析 selector。
Configuration(配置)
| Configuration | Required | Description | Default |
|---|---|---|---|
tenants | 必填 | 一个租户(tenant)映射,以租户域(tenant domain)为键,列出被授权进行认证的租户。未指定租户对应的令牌将被拒绝。 | |
agent_path_template | 可选 | Agent SPIFFE ID 的 URL 路径部分格式,以 text/template 格式描述。 | "/{{ .PluginName }}/{{ .TenantID }}/{{ .SubscriptionID }}/{{ .VMID }}" |
allowed_metadata_domains | 可选 | 用于证书校验的允许 Azure 元数据域(metadata domain)列表。每个域接受基础域(例如 metadata.azure.com),并自动校验该域或其任意子域的证书(例如 eastus.metadata.azure.com、sub.eastus.metadata.azure.com)。 | ["metadata.azure.com"] |
主配置中的每个租户支持以下配置:
| Configuration | Required | Description | Default |
|---|---|---|---|
secret_auth | 可选 | 使用 AppReg 的 AppID 和 AppSecret 进行认证 | |
token_auth | 可选 | 使用 AppReg 的 AppID 和存储在磁盘上的 JWT 令牌进行认证 | |
restrict_to_subscriptions | 可选 | 将认证限制在所列出的 subscription ID 内。留空或不设置则允许任意订阅。 | |
allowed_vm_tags | 可选 | 用于构建 selector 的、该租户允许的 VM 标签(tag)列表 |
如果提供了 restrict_to_subscriptions,则任何来自不在该列表中的 subscription ID 的认证尝试,都会在解析 selector 之前被拒绝。
基于密钥的认证(secret_auth)
| Field | Required | Description |
|---|---|---|
app_id | 必填 | 应用(客户端)ID(application/client ID) |
app_secret | 必填 | 应用密钥(application secret) |
基于令牌的认证(token_auth)
| Field | Required | Description |
|---|---|---|
app_id | 必填 | 应用(客户端)ID(application/client ID) |
token_path | 必填 | 磁盘上 JWT 令牌的路径 |
注意:secret_auth 和 token_auth 互斥。
向 Azure 认证(Authenticating to Azure)
本插件需要凭据(credentials)来向 Azure 进行认证,以便查询被认证节点的属性并生成 selector。
默认情况下,插件会尝试通过 DefaultAzureCredential API 使用应用默认凭据(application default credential)。DefaultAzureCredential API 会按以下顺序依次尝试各种机制进行认证——环境变量、工作负载标识(Workload Identity)、托管标识(Managed Identity),一旦某种方式成功即停止。当使用 Workload Identity 或 Managed Identity 时,插件必须能够获取所配置 tenant ID 的凭据,否则使用该认证器的节点认证将会失败。
或者,也可以将插件配置为使用以下两种方法之一进行认证:
- 基于密钥的认证(
secret_auth):使用在租户中注册的应用的app_id和app_secret进行认证。此方法适用于你能够安全地提供和管理密钥的场景。 - 基于令牌的认证(
token_auth):使用应用的app_id以及一个存储在磁盘上(token_path)的、预先生成的 JWT 令牌进行认证。这在密钥管理被委托给他方、或令牌以带外(out-of-band)方式预置的环境中很有用(例如使用 Kubernetes 投射服务账号令牌 projected service account token)。
每个租户只能选择一种认证方法;这些选项互斥。
Sample Configurations(示例配置)
使用默认认证的基础配置
此配置使用默认的 Azure 凭据链(环境变量、Workload Identity 或 Managed Identity):
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"onmicrosoft.com" = {
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
}
}
}使用基于密钥认证的配置
此配置使用 Azure 应用的 client ID 和 secret 进行认证:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"onmicrosoft.com" = {
secret_auth = {
app_id = "12345678-1234-1234-1234-123456789012"
app_secret = "your-application-secret"
}
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
}
}
}使用基于令牌认证的配置
此配置使用 Azure 应用的 client ID 和存储在磁盘上的 JWT 令牌:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"example.onmicrosoft.com" = {
token_auth = {
app_id = "12345678-1234-1234-1234-123456789012"
token_path = "/var/lib/spire/azure-token"
}
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
}
}
}多租户高级配置
此配置演示了使用不同认证方法和 VM 标签限制的多个租户:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"production.onmicrosoft.com" = {
secret_auth = {
app_id = "12345678-1234-1234-1234-123456789012"
app_secret = "production-secret"
}
restrict_to_subscriptions = [
"d5b40d61-272e-48da-beb9-05f295c42bd6",
"a1b2c3d4-5678-9012-3456-789012345678"
]
allowed_vm_tags = [
"environment",
"team"
]
}
"staging.onmicrosoft.com" = {
token_auth = {
app_id = "87654321-4321-4321-4321-210987654321"
token_path = "/var/lib/spire/staging-token"
}
restrict_to_subscriptions = ["e2f3g4h5-6789-0123-4567-890123456789"]
allowed_vm_tags = [
"environment"
]
}
}
}
}使用自定义 Agent 路径模板的配置
此配置使用自定义模板来生成 agent SPIFFE ID:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"example.onmicrosoft.com" = {
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
}
agent_path_template = "/{{ .PluginName }}/{{ .TenantID }}/{{ .SubscriptionID }}/{{ .VMID }}/custom"
}
}使用自定义元数据域的配置
此配置展示了如何为非商业(non-commercial)Azure 环境(例如 Azure Government)进行配置:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"example.onmicrosoft.com" = {
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
}
allowed_metadata_domains = ["metadata.azure.us"]
}
}使用多个云环境的配置
此配置演示了如何允许来自多个 Azure 环境(例如商业云与政府云)的认证:
NodeAttestor "azure_imds" {
plugin_data {
tenants = {
"commercial.onmicrosoft.com" = {
restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
}
"government.onmicrosoft.us" = {
restrict_to_subscriptions = ["a1b2c3d4-5678-9012-3456-789012345678"]
}
}
allowed_metadata_domains = ["metadata.azure.com", "metadata.azure.us"]
}
}Selectors(选择器)
插件生成以下 selector。
| Selector | Example | Description |
|---|---|---|
| Subscription ID | subscription-id:d5b40d61-272e-48da-beb9-05f295c42bd6 | 节点所属的订阅(subscription) |
| Virtual Machine Name | vm-name:blog | 虚拟机名称(例如 blog) |
| Virtual Machine Scale Set Name | vmss-name:myvmss | 虚拟机规模集(virtual machine scale set)名称(例如 myvmss) |
| Network Security Group | network-security-group:frontend:webservers | 网络安全组名称(例如 webservers),以其资源组(例如 frontend)限定 |
| Resource Group | resource-group:frontend | 资源组(resource group)名称(例如 frontend) |
| Virtual Machine Location | vm-location:eastus | 虚拟机的位置(例如 eastus) |
| Virtual Network | virtual-network:vnet | 虚拟网络名称(例如 vnet) |
| Virtual Network Subnet | virtual-network-subnet:vnet:default | 虚拟网络子网名称(例如 default),以其虚拟网络和资源组限定 |
| Virtual Machine Tag | vm-tag:environment:production | VM 上的标签键与值,格式为 vm-tag:<key>:<value> |
所有 selector 的类型均为 azure_imds。
Agent 路径模板(Agent Path Template)
Agent 路径模板(agent path template)是一种自定义 agent SPIFFE ID 生成格式的方式。模板格式化器采用 Golang text/template 约定,可以引用插件提供的值,或 IMDS 已认证文档中的值。关于模板引擎的详情,见模板引擎文档。
一些有用的值包括:
| Value | Description |
|---|---|
| .PluginName | 插件名称 |
| .TenantID | Azure 租户标识符 |
| .SubscriptionID | Azure 订阅标识符 |
| .VMID | 对某个特定虚拟机 ID 而言唯一的标识符 |
Security Considerations(安全考量)
Azure IMDS 已认证文档(attested document)是本认证器用于证明节点元数据的凭据,默认情况下节点上运行的任何进程都可访问它。因此,节点上的非 agent 代码有可能向 SPIRE Server 完成认证,从而获取该节点被授权运行的任意工作负载身份(workload identity)。
尽管许多运维人员会选择配置系统以阻止对 IMDS 已认证文档的访问,但 SPIRE 项目无法保证这一安全态势。为缓解相关风险,azure_imds 节点认证器实现了首次使用即信任(Trust On First Use,简称 TOFU)语义。对于任意给定节点,认证只能发生一次,后续的认证尝试都将被拒绝。
非 agent 代码仍有可能先于 SPIRE Agent 完成节点认证;不过这种情况很容易被迅速发现,因为此时 SPIRE Agent 将无法启动,并且 SPIRE Agent 和 SPIRE Server 都会记录该事件。此类情况应作为可能的安全事件加以调查。