Skip to content

本文为 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 的格式为:

xml
spiffe://<trust_domain>/spire/agent/azure_imds/<tenant_id>/<subscription_id>/<vm_id>

server 本身无需运行在 Azure 中即可执行节点认证或解析 selector。

Configuration(配置)

ConfigurationRequiredDescriptionDefault
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.comsub.eastus.metadata.azure.com)。["metadata.azure.com"]

主配置中的每个租户支持以下配置:

ConfigurationRequiredDescriptionDefault
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)

FieldRequiredDescription
app_id必填应用(客户端)ID(application/client ID)
app_secret必填应用密钥(application secret)

基于令牌的认证(token_auth)

FieldRequiredDescription
app_id必填应用(客户端)ID(application/client ID)
token_path必填磁盘上 JWT 令牌的路径

注意:secret_authtoken_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_idapp_secret 进行认证。此方法适用于你能够安全地提供和管理密钥的场景。
  • 基于令牌的认证(token_auth):使用应用的 app_id 以及一个存储在磁盘上(token_path)的、预先生成的 JWT 令牌进行认证。这在密钥管理被委托给他方、或令牌以带外(out-of-band)方式预置的环境中很有用(例如使用 Kubernetes 投射服务账号令牌 projected service account token)。

每个租户只能选择一种认证方法;这些选项互斥。

Sample Configurations(示例配置)

使用默认认证的基础配置

此配置使用默认的 Azure 凭据链(环境变量、Workload Identity 或 Managed Identity):

hcl
NodeAttestor "azure_imds" {
    plugin_data {
        tenants = {
            "onmicrosoft.com" = {
                restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
            }
        }
    }
}

使用基于密钥认证的配置

此配置使用 Azure 应用的 client ID 和 secret 进行认证:

hcl
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 令牌:

hcl
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 标签限制的多个租户:

hcl
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:

hcl
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)进行配置:

hcl
NodeAttestor "azure_imds" {
    plugin_data {
        tenants = {
            "example.onmicrosoft.com" = {
                restrict_to_subscriptions = ["d5b40d61-272e-48da-beb9-05f295c42bd6"]
            }
        }
        allowed_metadata_domains = ["metadata.azure.us"]
    }
}

使用多个云环境的配置

此配置演示了如何允许来自多个 Azure 环境(例如商业云与政府云)的认证:

hcl
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。

SelectorExampleDescription
Subscription IDsubscription-id:d5b40d61-272e-48da-beb9-05f295c42bd6节点所属的订阅(subscription)
Virtual Machine Namevm-name:blog虚拟机名称(例如 blog)
Virtual Machine Scale Set Namevmss-name:myvmss虚拟机规模集(virtual machine scale set)名称(例如 myvmss)
Network Security Groupnetwork-security-group:frontend:webservers网络安全组名称(例如 webservers),以其资源组(例如 frontend)限定
Resource Groupresource-group:frontend资源组(resource group)名称(例如 frontend)
Virtual Machine Locationvm-location:eastus虚拟机的位置(例如 eastus)
Virtual Networkvirtual-network:vnet虚拟网络名称(例如 vnet)
Virtual Network Subnetvirtual-network-subnet:vnet:default虚拟网络子网名称(例如 default),以其虚拟网络和资源组限定
Virtual Machine Tagvm-tag:environment:productionVM 上的标签键与值,格式为 vm-tag:<key>:<value>

所有 selector 的类型均为 azure_imds

Agent 路径模板(Agent Path Template)

Agent 路径模板(agent path template)是一种自定义 agent SPIFFE ID 生成格式的方式。模板格式化器采用 Golang text/template 约定,可以引用插件提供的值,或 IMDS 已认证文档中的值。关于模板引擎的详情,见模板引擎文档

一些有用的值包括:

ValueDescription
.PluginName插件名称
.TenantIDAzure 租户标识符
.SubscriptionIDAzure 订阅标识符
.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 都会记录该事件。此类情况应作为可能的安全事件加以调查。