3、访问密钥保管库的身份认证:Azure AD认证、服务主体、托管标识、访问令牌获取
聊到 Key Vault 的安全,绕不开一个问题——「谁有资格开这把锁?」
嗯,身份认证就是这个问题的答案。Azure 这边走的是 Azure AD 路线,不管是人、是应用、还是自动化脚本,想碰 Key Vault 里的秘密,都得先过 Azure AD 这一关。今天这篇,我就把这几条主流认证路子掰开讲清楚。
3.1 Azure AD 认证:基石与思路
Key Vault 本身不存用户密码,它只认 Azure AD 颁发的令牌()。说白了,你拿到令牌就相当于拿到了临时入场券。令牌背后绑定的是一个身份,这个身份可以是:
- 用户账号(User Principal)——开发人员登录 Azure Portal 或 CLI 时用。
- 服务主体(Service Principal)——应用程序或 CI/CD 管道的「机器人账号」。
- 托管标识(Managed Identity)——Azure 资源自带的免密身份。
这三种方式最终都会走到 OAuth 2.0 的客户端凭证流或授权码流,拿到 JWT 令牌,然后往 Key Vault 的 REST API 的 Authorization 头里一塞,完事。
3.2 服务主体:给应用配一个「机器人户口」
服务主体的核心作用,就是让无用户交互的场景(比如后端服务、定时任务、GitHub Actions)能安全地访问 Azure 资源。创建服务主体后,你会得到一个 Client ID 和 Client Secret(或者证书),靠它们去换取令牌。
创建步骤(极简版):
- 在 Azure AD 中注册一个应用(App Registration)。
- 生成客户端密码(或上传证书)。
- 给这个服务主体在 Key Vault 的访问策略中分配对应权限(比如
Get,List,Set)。
用 Python 获取令牌的代码示例:
from azure.identity import ClientSecretCredential
from azure.keyvault.secrets import SecretClient
tenant_id = "your-tenant-id"
client_id = "your-client-id"
client_secret = "your-client-secret"
vault_url = "https://your-vault.vault.azure.net"
credential = ClientSecretCredential(tenant_id, client_id, client_secret)
client = SecretClient(vault_url=vault_url, credential=credential)
secret = client.get_secret("mySecret")
print(secret.value)
client_secret 直接写在了配置文件的 appsettings.json 里,还上传到了公共 Git 仓库……后面的事你可想而知。至少要用环境变量或 Azure Key Vault 引用去存。
3.3 托管标识:真正的「免密」方案
托管标识(Managed Identity)是我个人最喜欢的认证方式。它让 Azure 资源(比如 VM、App Service、Functions)自动获得一个 Azure AD 身份,你根本不需要管凭据是怎么流转的。
两种类型:
| 类型 | 生命周期 | 典型场景 |
|---|---|---|
| 系统分配(System-assigned) | 与 Azure 资源绑定,资源删掉它也跟着没 | 单应用单标识,简单直接 |
| 用户分配(User-assigned) | 独立资源,可被多个资源共用 | 多应用共用一个身份,统一权限管理 |
内部原理:Azure 会给启用托管标识的资源创建一个对应的服务主体,然后通过内部元数据端点(169.254.169.254/metadata/identity/oauth2/)自动管理令牌的签发和刷新。你在代码里只需要用 DefaultAzureCredential,它会优先尝试托管标识的令牌获取方式。
代码示例(用托管标识访问 Key Vault):
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://your-vault.vault.azure.net", credential=credential)
secret = client.get_secret("mySecret")
print(secret.value)
3.4 访问令牌获取:几种常见通道
不管你用哪种身份,最终都要拿到令牌才能调 Key Vault 的 API。除了 SDK 自动帮你打理,有时候你也需要手动获得令牌做测试或调试。
3.4.1 Azure CLI
# 先登录用户身份
az login
# 获取 Key Vault 令牌
az account get-access- --resource https://vault.azure.net
3.4.2 PowerShell
Connect-AzAccount
Get-AzAccessToken -ResourceUrl "https://vault.azure.net"
3.4.3 直接调用 REST API(服务主体)
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/
Content-Type: application/x-www-form-urlencoded
client_id=xxx
&scope=https://vault.azure.net/.default
&client_secret=yyy
&grant_type=client_credentials
返回结果里的 access_ 就是你需要的。
3.5 三种认证方式对比
| 维度 | 用户账号 | 服务主体 | 托管标识 |
|---|---|---|---|
| 需要管理凭据吗 | 是,用户密码 | 是,客户端密码/证书 | 否 |
| 适用于自动化 | 不推荐(MFA 等问题) | 完美适用 | 最佳适用 |
| 权限隔离粒度 | 按用户 | 按应用 | 按资源或共用标识 |
| 令牌刷新方式 | 用户交互/设备流 | 客户端凭证流自动刷新 | Azure 基础架构自动管理 |
| 典型场景 | 开发调试、Portal 操作 | CI/CD、后端服务 | Azure 原生资源内应用 |
3.6 核心流程示意图
3.7 避坑指南 & 最佳实践
- 令牌过期处理:SDK 里的
DefaultAzureCredential会自动刷新令牌,但你如果自己写 HTTP 请求拿令牌,记得处理 401 重试和刷新逻辑。 - 权限最小化:给服务主体或托管标识只分配
Get和List就够了,不要给Set、Delete除非真有必要。我见过因为给 CI/CD 管道配了全部权限,导致一次误操作删掉整个 Secret 的事故。 - 托管标识的局限:它只有在你部署在 Azure 内部资源上才能用,本地开发环境就不要想了。这时候可以用
AzureCliCredential或EnvironmentCredential做 fallback。
一句话总结:能用托管标识就别用服务主体,能用服务主体就别用用户账号。身份认证越「免密」,后续的运维烦恼越少。