SPIFFE(适用于每个人的安全生产标识框架)是一组开源标准,为软件工作负载提供与平台无关的标识,称为 SPIFFE ID。 工作负载通过出示一种称为 SVID 的短期凭据来证明其身份。 SPIRE 是 SPIFFE 标准的参考实现。
在 Azure 外部运行的工作负载通常通过存储的机密信息或证书进行身份验证,这些凭据必须受到保护并定期轮换;如果在你完成替换之前过期,可能会导致服务中断。 工作负载身份联合无需再使用该已存储的凭据:工作负载无需持有密钥,而是提供其现有的 SPIFFE 身份,并用其换取 Microsoft Entra 令牌。
在本教程中,将在 Kubernetes 群集中设置 SPIRE,为示例工作负荷提供 SPIFFE ID,并将该标识与Microsoft Entra ID联合。 建立信任关系后,工作负载会交换其 SPIFFE JWT-SVID 以获取Microsoft Entra访问令牌,并调用Azure资源(例如Azure Blob 存储),而无需存储任何机密或证书。
本教程包含四个依次进行的部分,共同逐步构建成一个完整的场景。 每个部分都取决于前一部分的输出。
在本教程中,你将了解:
- 在 Kubernetes 群集中使用 JWT-SVID 和 OIDC 发现支持部署 SPIRE。
- 部署示例工作负荷并为其分配 SPIFFE ID。
- 将Microsoft Entra应用程序配置为信任 SPIFFE ID。
- 将 SPIFFE JWT-SVID 换取为 Microsoft Entra 访问令牌,并访问 Azure 资源。
下图显示了工作负载身份联合流程:工作负载从外部身份提供程序获取令牌,将其与 Microsoft 标识平台交换以获取访问令牌,并使用该访问令牌来访问 Azure 资源。 在本教程中,外部标识提供者是 SPIRE,令牌是 SPIFFE JWT-SVID。
先决条件
- 一个 Microsoft Entra 租户和一个 Azure 订阅。 如果没有 Azure 订阅,可在开始前创建一个试用帐户。
- 可以托管 SPIRE 服务器、SPIRE 代理和 OIDC 发现提供程序的 Kubernetes 群集。 群集必须通过外部 IP 地址公开服务。
- 一个由你控制、用于 OIDC 发现端点的已注册域名,以及管理其 DNS 记录的能力。 本教程使用 OIDC 发现域的占位符
oidc.contoso.com。 - 用于保存示例工作负荷映像的容器注册表。 本教程使用注册表名称的占位符
<your-registry>。 - 已安装并配置为可访问你的群集、注册表和租户的
kubectl、docker和 Azure CLI(az)命令行工具。 - 将联合标识凭据添加到应用注册或托管标识的权限。 若要将联合凭据添加到应用注册,你的帐户必须是应用的所有者,或者拥有 应用程序管理员、 应用程序开发人员或 云应用程序管理员 角色之一,或者具有
microsoft.directory/applications/credentials/update权限。
本教程使用 SPIFFE 信任域 example.org。
example.org 是整个官方 SPIRE 快速入门中使用的信任域示例;在实际部署中将其替换为自己的信任域。 以下步骤server-configmap-oidc.yamlagent-configmap.yaml(等等)中引用的 SPIRE 配置文件来自 SPIRE 部署清单。 请基于当前官方 SPIRE 文档 和 Kubernetes 快速入门来配置 SPIRE 服务器、代理和 OIDC 发现提供程序,以确保节点证明器和镜像版本保持最新。
第 1 部分:使用 JWT 和 OIDC 发现支持部署 SPIRE
在本部分中,你将部署 SPIRE 服务器、SPIRE 代理和 OIDC 发现提供程序。 OIDC 发现提供程序发布一个标准的 OpenID Connect 发现文档以及一个 JWKS 终结点,以便 Microsoft Entra ID 能够验证 SPIFFE JWT-SVID。
根据您的环境自定义 SPIRE 配置文件:
-
server-configmap-oidc.yaml:设置 OIDC 发现域 FQDN(例如oidc.contoso.com)和群集名称。 -
agent-configmap.yaml:设置群集名称。 -
oidc-ingress.yaml:设置 OIDC 发现端点的 FQDN。 -
oidc-dp-configmap.yaml:设置 OIDC 发现 FQDN、联系邮箱和set_key_use = true。
Important
当你在
"use": "sig"中设置oidc-dp-configmap.yaml时,SPIRE OIDC 发现提供程序可以将set_key_use = true参数添加到已发布的签名密钥中。 启用它,因为Microsoft Entra ID需要在 OIDC 发现文档的 JWKS 中的签名密钥上使用此参数。Important
Microsoft Entra ID支持使用 RS256 算法对令牌进行签名的外部颁发者。 SPIRE 默认签发使用 EC(ES256)签名的 JWT-SVID,因此请将 SPIRE 服务器证书颁发机构配置为签发使用 RS256 签名的 JWT-SVID(例如,将服务器 CA
jwt_key_type设置为 RSA 密钥类型),以便 Microsoft 标识平台接受这些令牌。 有关详细信息,请参阅联合 标识凭据的重要注意事项和限制中支持的签名算法和颁发者指南。-
部署 SPIRE 服务器:
kubectl apply -f spire-namespace.yaml kubectl apply -f server-account.yaml -f spire-bundle-configmap.yaml -f server-cluster-role.yaml kubectl apply -f server-configmap-oidc.yaml -f server-statefulset.yaml -f server-service.yaml部署 SPIRE 代理:
kubectl apply -f agent-account.yaml -f agent-cluster-role.yaml kubectl apply -f agent-configmap.yaml -f agent-daemonset.yaml验证 SPIRE Pod 是否正在运行。 每个节点应有一个
spire-server-0Pod 和一个spire-agent-*Pod,且都处于Running状态。kubectl get pods -n spire注册代理节点标识,以便 SPIRE 服务器信任代理。 将
<your-cluster-name>替换为您的集群名称。kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s_sat:cluster:<your-cluster-name> \ -selector k8s_sat:agent_ns:spire \ -selector k8s_sat:agent_sa:spire-agent -node部署 SPIRE OIDC 发现服务提供程序。 根据官方
"use": "sig",请使用支持 选项的当前提供程序镜像,以便发布的密钥包含set_key_use。kubectl apply -f oidc-account.yaml -f oidc-dp-configmap.yaml kubectl apply -f oidc-ingress.yaml -f oidc-service.yaml kubectl apply -f oidc-deployment.yaml注册 OIDC 提供方身份:
kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/oidc-discovery \ -parentID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s:ns:spire -selector k8s:sa:spire-oidcOIDC 发现服务作为一个具有外部 IP 地址的
LoadBalancer运行。 将 OIDC 发现域的 DNSA记录指向该外部 IP 地址。验证发现终结点是否已解析。 发现文档列出了
issuer、jwks_uri和id_token_signing_alg_values_supported的值,而 JWKS 列出了签名密钥。https://oidc.contoso.com/.well-known/openid-configurationhttps://oidc.contoso.com/keys
确认位于
/keys的 JWKS 中的每个密钥都包含"use": "sig"。 Microsoft Entra ID在签名密钥上需要此参数。
第 2 部分:部署示例工作负荷并为其分配 SPIFFE ID
在本部分中,你将生成和部署示例工作负荷,然后基于其 Kubernetes 命名空间和服务帐户为其分配 SPIFFE ID。
生成示例工作负荷容器映像并将其推送到注册表。 将
<your-registry>替换为你的注册表名称。docker build -f deployment/docker/dockerfile -t spiffe-demo . docker tag spiffe-demo <your-registry>.azurecr.io/spiffe-demo:v1 az acr login -n <your-registry>.azurecr.io docker push <your-registry>.azurecr.io/spiffe-demo:v1编辑
deployment.yaml,使其引用你的映像(例如<your-registry>.azurecr.io/spiffe-demo:v1),然后部署工作负载:kubectl apply -f demo-namespace.yaml kubectl apply -f serviceaccount.yaml kubectl apply -f deployment.yaml kubectl apply -f service.yaml根据其命名空间和服务账户为工作负载分配 SPIFFE ID:
kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/demo-spiffe/sa/demo-sa \ -parentID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s:ns:demo-spiffe -selector k8s:sa:demo-sa
工作负荷现已具有 SPIFFE ID spiffe://example.org/ns/demo-spiffe/sa/demo-sa。 在第 3 部分配置 Microsoft Entra 应用程序时,请将此值用作联合身份凭据的主题。
第 3 部分:配置Microsoft Entra应用程序以信任 SPIFFE ID
在本部分中,你需要向 Microsoft Entra 应用注册添加联合标识凭据,以便 Microsoft Entra ID 信任为你的工作负载的 SPIFFE ID 颁发的令牌。 联合标识凭据需要三个输入:
-
颁发者:OIDC 发现 URL,例如:
https://oidc.contoso.com。 -
主题:工作负荷的 SPIFFE ID。
spiffe://example.org/ns/demo-spiffe/sa/demo-sa -
访问群体:
["api://AzureADTokenExchange"].
创建包含以下内容的名为
credential.json的文件。 将签发者和值主题值替换为您自己的值。{ "name": "AccessUsingSpiffe", "issuer": "https://oidc.contoso.com", "subject": "spiffe://example.org/ns/demo-spiffe/sa/demo-sa", "audiences": ["api://AzureADTokenExchange"], "description": "Federated credential for SPIFFE workload" }将联合标识凭据添加到应用注册。 将
<your-app-id>替换为你的应用的对象 ID。az ad app federated-credential create --id <your-app-id> --parameters credential.json
还可以通过在Microsoft Entra 管理中心中选择“其他颁发者”方案并提供 OIDC 发现 URL 作为颁发者和 SPIFFE ID 作为主题来添加联合凭据。 有关详细步骤,请参阅 配置应用以信任外部标识提供者。 若要在用户分配的托管标识而不是应用注册上配置凭据,请参阅 配置用户分配的托管标识以信任外部标识提供者。
向你的应用或托管标识授予访问你的工作负载所调用的 Azure 资源的权限,例如对你的存储帐户进行角色分配。
第 4 部分:将 JWT-SVID 换取为 Microsoft Entra 访问令牌
在本部分中,您的工作负载会获取 SPIFFE JWT-SVID,并用其换取 Microsoft Entra 访问令牌,然后使用该令牌调用 Azure 资源。
SPIFFE 定义工作负荷 API,工作负荷使用该 API 从本地 SPIRE 代理中提取其 SVID。 为了向 Microsoft Entra ID 表明身份,工作负载会获取受众为 api://AzureADTokenExchange 的 JWT-SVID。 此受众必须与您在第 3 部分中为 Microsoft Entra 应用注册配置的联合标识凭据上的受众一致。
从 SPIFFE Workload API 获取 JWT-SVID。 以下示例代码片段为
api://AzureADTokenExchange受众请求 JWT-SVID,并返回该 SVID 字符串。 该概念在具有 SPIFFE 工作负荷 API 客户端的任何语言中都是相同的。async function getSpiffeJwt() { const svid = await workloadApiClient.fetchJwtSvid({ audience: ["api://AzureADTokenExchange"], }); return svid.token; }使用 Azure Identity SDK 中的
ClientAssertionCredential将 JWT-SVID 交换为 Microsoft Entra 令牌。ClientAssertionCredential接受一个返回联合断言的回调函数——在本例中为 JWT-SVID。import { ClientAssertionCredential } from "@azure/identity"; const credential = new ClientAssertionCredential(tenantId, clientId, getSpiffeJwt);将此凭据用于任何 Azure SDK 客户端。 例如,若要调用Azure Blob 存储:
const { BlobServiceClient } = require("@azure/storage-blob"); const blobClient = new BlobServiceClient(blobUrl, credential);
当客户端需要令牌时,它会调用该回调以获取新的 JWT-SVID,使用该 JWT-SVID 向 Microsoft 标识平台 换取访问令牌,并缓存生成的访问令牌。 由于 ClientAssertionCredential 通过回调提供联合断言,因此工作负荷永远不会存储机密。
ClientAssertionCredential可在Azure标识 SDK 中使用,包括.NET、Java、JavaScript、Python和 Go。 如果需要对令牌交换进行较低级别的控制,MSAL 库还支持客户端断言。
您的 SPIFFE/SPIRE 工作负载现在可以无需存储任何机密信息即可访问受 Microsoft Entra 保护的资源。
清理资源
如果不再需要在本教程中创建的资源,请将其删除以避免持续收费:
从群集中删除 SPIRE 和演示工作负荷:
kubectl delete namespace demo-spiffe kubectl delete namespace spire删除为 OIDC 发现域创建的 DNS
A记录。从注册表中删除容器映像,如果仅为本教程创建了容器映像,请删除群集。
从应用注册中删除联合标识凭据:
az ad app federated-credential delete --id <your-app-id> --federated-credential-id AccessUsingSpiffe