你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
使用 Azure 密钥保管库引用作为 Azure 应用服务和 Azure Functions 中的应用设置
本文介绍如何将 Azure 密钥保管库中的密钥用作 Azure 应用服务或 Azure Functions 应用中的应用设置或连接字符串的值。
Azure Key Vault 是一项服务,可以提供集中式机密管理,并且可以完全控制访问策略和审核历史记录。 当应用设置或连接字符串是密钥保管库引用时,应用代码可以像使用任何其他应用设置或连接字符串那样来使用它。 这样就可以将密钥与应用配置分开进行维护。 可以对应用程序设置进行安全的静态加密,但如果需要机密管理功能,则应将它们置于密钥保管库中。
授予应用对密钥保管库的访问权限
若要从密钥保管库读取机密,需创建一个保管库并授予应用访问该保管库的权限。
按照 Key Vault 快速入门中的说明创建一个密钥保管库。
为应用程序创建一个托管标识。
默认情况下,密钥保管库引用将使用应用的系统分配的标识,但你可以指定用户分配的标识。
为先前创建的托管标识授予对密钥保管库密钥的读取访问权限。 操作方式取决于密钥保管库采用哪种权限模型:
- Azure 基于角色的访问控制:为托管标识分配“密钥保管库密钥用户”角色。 请参阅使用 Azure 基于角色的访问控制提供对密钥保管库密钥、证书和机密的访问权限,了解操作说明。
- 保管库访问策略:为托管标识分配“Get”密钥权限。 请参阅分配密钥保管库访问策略,了解操作说明。
访问网络受限保管库
如果你的保管库配置有网络限制,确保该应用程序具有网络访问权。 保管库不应依赖于应用的公共出站 IP,因为机密请求的源 IP 可能不同。 相反,应将保管库配置为接受来自应用所使用的虚拟网络的流量。
请确保该应用程序已配置有出站网络功能,正如应用服务网络功能和 Azure Functions 网络选项所述。
必须将连接专用终结点的 Linux 应用明确配置为通过虚拟网络路由全部流量。 即将到来的更新中将删除此要求。 若要配置上述设置,请运行以下命令:
az webapp config set --subscription <sub> -g <group-name> -n <app-name> --generic-configurations '{"vnetRouteAllEnabled": true}'
请确保保管库的配置允许应用使用的网络或子网访问该保管库。
使用用户分配的标识访问保管库
系统分配的标识尚不可用时,某些应用需要在创建时引用机密。 在这些情况下,可以提前创建用户分配的标识并为其提供对保管库的访问权限。
被授予对用户分配的标识的权限后,请执行以下步骤:
将标识分配到应用程序(如果尚未分配)。
通过将
keyVaultReferenceIdentity
属性设置为用户分配的标识的资源 ID,配置应用以将此标识用于密钥保管库引用操作。identityResourceId=$(az identity show --resource-group <group-name> --name <identity-name> --query id -o tsv) az webapp update --resource-group <group-name> --name <app-name> --set keyVaultReferenceIdentity=${identityResourceId}
应用的所有密钥保管库引用都应用此设置。
旋转
如果引用中未指定密钥版本,则应用将使用密钥保管库中存在的最新版本。 在有较新的版本可用时(例如通过轮换事件),应用将会在 24 小时内自动更新并开始使用最新版本。 延迟是因为应用服务会缓存 Key Vault 引用的值,并且每 24 小时会重新提取一次。 对应用所做的任何配置更改都会导致应用重启并立即重新提取所有引用的机密。
密钥保管库中的源应用设置
若要使用密钥保管库引用,请将引用设为设置的值。 应用可以通过密钥正常引用机密。 不需更改代码。
提示
应该将大多数使用密钥保管库引用的应用程序设置标记为槽设置,因为你应该为每个环境设置单独的保管库。
密钥保管库引用采用 @Microsoft.KeyVault({referenceString})
格式,其中 {referenceString}
将采用下述格式之一:
引用字符串 | 说明 |
---|---|
SecretUri=secretUri | SecretUri 应该是密钥保管库中机密的完整数据平面 URI,可以选择包括版本,例如 https://myvault.vault.azure.net/secrets/mysecret/ 或 https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931 |
VaultName=vaultName;SecretName=secretName;SecretVersion=secretVersion | VaultName是必需的,是保管库的名称。 SecretName是必需的,是机密的名称。 SecretVersion 是可选的,但如果存在,则指示要使用的机密的版本。 |
例如,完整的引用将如以下字符串所示:
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/)
也可使用以下命令:
@Microsoft.KeyVault(VaultName=myvault;SecretName=mysecret)
Azure 文件存储装载注意事项
应用可使用 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
应用程序设置将 Azure 文件存储装载为文件系统。 此设置有验证检查,旨在确保该应用可以正常启动。 平台依赖于在 Azure 文件存储中拥有内容共享,而且除非通过 WEBSITE_CONTENTSHARE
设置指定名称,否则其会采用默认名称。 对于修改这些设置的任何请求,平台会验证此内容共享是否存在,如果不存在,尝试创建共享。 如果找不到或无法创建内容共享,系统会阻止请求。
在此设置中使用密钥保管库引用时,验证检查默认会失败,因为在处理传入请求时无法解析机密本身。 若要避免此问题,可以通过将 WEBSITE_SKIP_CONTENTSHARE_VALIDATION
设置为“1”来跳过验证。 此设置会告诉 Azure 应用服务绕过所有检查,并且不会为你创建内容共享。 应确保提前创建内容共享。
注意
如果跳过验证,并且连接字符串或内容共享无效,该应用将无法正常启动,并且只会显示 HTTP 500 错误。
在创建应用的过程中,因为未传播托管标识权限,或未设置虚拟网络集成,导致内容共享装载尝试失败。 你可将 Azure 文件存储的设置工作推迟到稍后在部署模板中进行,以适应这种情况。 若要了解更多信息,请参阅 Azure 资源管理器部署。 这种情况下,Azure 应用服务默认使用文件系统,直到设置 Azure 文件存储,并且不会复制文件。 装载 Azure 文件存储之前的过渡期间,切记不要尝试进行部署。
Application Insights 检测的注意事项
应用可以使用 APPINSIGHTS_INSTRUMENTATIONKEY
或 APPLICATIONINSIGHTS_CONNECTION_STRING
应用程序设置来与 Application Insights 集成。 应用服务和 Azure Functions 的门户体验还使用这些设置来显示资源的遥测数据。 如果这些值引用自密钥保管库,则这些体验不可用,需改为直接使用 Application Insights 资源来查看遥测数据。 但是,这些值不被视为机密,因此也可以考虑直接配置它们,而不是使用密钥保管库引用。
Azure 资源管理器部署
通过 Azure 资源管理器模板自动进行资源部署时,可能需要将依赖项按特定的顺序排列,这样才能使该功能发挥作用。 确保将应用程序设置定义为其自己的资源,而不能使用应用定义中的 siteConfig
属性。 这是因为,应用需先进行定义,这样才能使用它来创建系统分配标识并将该标识用在访问策略中。
下面并非真实的模型,而是展示函数应用外观的示例:
{
//...
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
//...
},
{
"type": "Microsoft.Insights/components",
"name": "[variables('appInsightsName')]",
//...
},
{
"type": "Microsoft.Web/sites",
"name": "[variables('functionAppName')]",
"identity": {
"type": "SystemAssigned"
},
//...
"resources": [
{
"type": "config",
"name": "appsettings",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('storageConnectionStringName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('appInsightsKeyName'))]"
],
"properties": {
"AzureWebJobsStorage": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringName')).secretUriWithVersion, ')')]",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringName')).secretUriWithVersion, ')')]",
"APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyName')).secretUriWithVersion, ')')]",
"WEBSITE_ENABLE_SYNC_UPDATE_SITE": "true"
//...
}
},
{
"type": "sourcecontrols",
"name": "web",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.Web/sites/config', variables('functionAppName'), 'appsettings')]"
],
}
]
},
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('keyVaultName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
],
"properties": {
//...
"accessPolicies": [
{
"tenantId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.tenantId]",
"objectId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.principalId]",
"permissions": {
"secrets": [ "get" ]
}
}
]
},
"resources": [
{
"type": "secrets",
"name": "[variables('storageConnectionStringName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),'2019-09-01').key1)]"
}
},
{
"type": "secrets",
"name": "[variables('appInsightsKeyName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
],
"properties": {
"value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2019-09-01').InstrumentationKey]"
}
}
]
}
]
}
注意
在此示例中,源代码管理部署取决于应用程序设置。 这通常是不安全的行为,因为应用设置更新是以异步方式表现的。 不过,由于我们已包括 WEBSITE_ENABLE_SYNC_UPDATE_SITE
应用程序设置,因此更新是同步的。 这意味着源代码管理部署只有在应用程序设置已完全更新后才会开始。 有关更多应用设置,请参阅 Azure 应用服务中的环境变量和应用设置。
排查密钥保管库引用问题
如果未正确解析引用,则会改用引用字符串(例如,@Microsoft.KeyVault(...)
)。 这可能会导致应用程序引发错误,因为它需要具有不同值的机密。
无法解析通常是由于密钥保管库访问策略配置不当。 但是,原因也可能是机密不再存在,或者引用本身存在语法错误。
如果语法正确,可以通过在门户中检查当前解析状态来查看其他错误原因。 导航到“应用程序设置”,然后选择有问题的引用对应的“编辑”。 编辑对话框显示状态信息,包括任何错误。 如果未看到状态消息,则表示语法无效,无法识别为密钥保管库引用。
也可以使用某个内置检测程序来获取更多信息。
使用应用服务的检测程序
- 在门户中导航到你的应用。
- 选择“诊断和解决问题”。
- 依次选择“可用性和性能”、“Web 应用故障”。
- 在搜索框中,搜索并选择“密钥保管库应用程序设置诊断”。
使用 Azure Functions 的检测程序
- 在门户中导航到你的应用。
- 导航到“平台功能”。
- 选择“诊断并解决问题”。
- 依次选择“可用性和性能”、“函数应用故障或报错”。
- 选择“密钥保管库应用程序设置诊断”。