Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
本文介绍如何使用 Azure 密钥保管库 中的机密作为使用 Azure 应用服务、Azure Functions 或 Azure 逻辑应用(标准)创建的应用的应用 设置 或 连接字符串 中的值。
密钥保管库 是一项服务,提供集中式机密管理,可完全控制访问策略和审核历史记录。 当应用设置或连接字符串是 密钥保管库 引用时,应用程序代码可以使用它,就像任何其他应用设置或连接字符串一样。 这样一来,除了应用的配置之外,还可以维护机密。 应用程序设置在静态时已安全加密,但如果您需要进行机密信息管理,应将它们存入密钥保管库。
请授予应用对密钥保管库的访问权限
若要从密钥保管库读取机密,首先需要创建保管库,并授予应用访问它的权限:
按照 密钥保管库 快速入门中的说明创建一个密钥保管库。
为应用程序创建一个托管标识。
默认情况下,密钥保管库引用会使用应用的系统分配的标识,但你可以指定用户分配的标识。
为已创建的托管标识授予对密钥保管库中的机密的读取访问权限。 操作方式取决于你的密钥保管库的权限模型:
- Azure 基于角色的访问控制:将密钥保管库机密用户角色分配给托管标识。 请参阅 使用 Azure 基于角色的访问控制提供对 密钥保管库 密钥、证书和机密的访问权限。
- 保管库访问策略:为托管标识分配获取机密权限。 请参阅分配密钥保管库访问策略。
访问网络受限保管库
如果你的保管库配置了网络限制,则请确保该应用程序具有网络访问权限。 保管库不应依赖于应用的公共出站 IP 地址,因为机密请求的源 IP 地址可能有所不同。 应改将保管库配置为接受来自应用所使用的虚拟网络的流量。
确保应用程序配置了出站网络功能,如 应用服务网络功能和Azure Functions 网络选项中所述。
除了在 Flex 消耗计划中运行的函数应用之外,必须显式配置连接到专用终结点的 Linux 应用程序,以便将所有流量通过虚拟网络路由。 在 Flex Consumption 计划中运行时,此路由会自动完成,不需要其他配置。 运行以下命令,通过将 vnetRouteAllEnabled 设置为
true来配置虚拟网络路由:az webapp config set --resource-group <group-name> --subscription <subscription> --name <app-name> --generic-configurations '{"vnetRouteAllEnabled": true}'
- 请确保保管库的配置允许您的应用所使用的网络或子网访问它。
即使您已正确配置保管库以接受来自虚拟网络的流量,保管库的审核日志仍可能显示应用程序公共出站 IP 发送的 SecretGet 请求失败(403 - 禁止)事件。 应用程序的私有 IP 上成功的 SecretGet 事件是有意安排的结果。
使用用户分配的标识访问保管库
某些应用需要在创建时引用机密,而系统分配的标识尚不可用。 在这些情况下,请创建用户分配的标识,并提前授予它对保管库的访问权限。
向用户分配的标识授予权限后,请执行以下步骤:
为应用程序分配标识。
通过将
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}
此设置适用于应用的所有 密钥保管库 引用。
提示
如果要将应用还原为使用系统分配的标识,请将该值设置为 SystemAssigned 而不是资源 ID。
了解轮换
如果引用中未指定机密版本,则应用会使用密钥保管库中存在的最新版本。 当较新版本可用(例如轮换)时,应用会自动更新,并在 24 小时内开始使用最新版本。
延迟是因为应用服务缓存 密钥保管库 引用的值,并每隔 24 小时重新提取它们。 对应用所做的任何配置更改都会导致应用重启和立即重新提取所有引用的机密。
若要强制解析应用的 密钥保管库 引用,请向 API 终结点 https://management.chinacloudapi.cn/[Resource ID]/config/configreferences/appsettings/refresh?api-version=2022-03-01发出经过身份验证的 POST 请求。
了解 密钥保管库 中的源应用设置
若要使用 密钥保管库 引用,请将引用设置为设置的值。 应用可以通过密钥正常引用机密。 不需更改代码。
提示
由于每个环境应具有单独的保管库,因此使用 密钥保管库 引用的大多数应用设置应标记为插槽设置。
密钥保管库 引用以@Microsoft.KeyVault({referenceString})的形式,其中{referenceString}为以下格式之一:
| 引用字符串 | 说明 |
|---|---|
SecretUri=<secretUri> |
SecretUri应是保管库中机密的完整数据平面 URI。 例如,https://myvault.vault.azure.cn/secrets/mysecret。 (可选)加入一个版本,例如 https://myvault.vault.azure.cn/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931。 |
VaultName=<vaultName>;SecretName=<secretName>;SecretVersion=<secretVersion> |
VaultName 值是必需的,是保管库名称。 该值 SecretName 是必需的,并且是机密名称。 该值 SecretVersion 是可选的,但如果存在,则表示要使用的机密版本。 |
例如,没有特定版本的完整引用类似于以下字符串:
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.cn/secrets/mysecret)
也可使用以下命令:
@Microsoft.KeyVault(VaultName=myvault;SecretName=mysecret)
Azure 文件存储装载注意事项
应用可使用 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING 应用程序设置将 Azure 文件存储装载为文件系统。 此设置有验证检查,旨在确保该应用可以正常启动。
平台依赖于在 Azure 文件存储中共享内容。 平台会使用默认名称,除非通过 WEBSITE_CONTENTSHARE 设置指定了一个名称。 对于修改这些设置的任何请求,平台将验证此内容共享是否存在。 如果内容共享不存在,平台会尝试创建它。 如果平台无法定位或创建内容共享,它会阻止该请求。
在此设置中使用 密钥保管库 引用时,验证检查默认失败,因为在处理传入请求期间无法解析机密。 若要避免此问题,可以通过设置为 WEBSITE_SKIP_CONTENTSHARE_VALIDATION1 跳过验证。 此设置告知应用服务绕过所有检查,并且不会为你创建内容共享。 应确保提前创建内容共享。
注意
如果跳过验证并且连接字符串或内容共享无效,则应用无法正确启动并创建 HTTP 500 错误。
在创建应用过程中,尝试装载内容共享可能会失败,因为未传播托管标识权限或未设置虚拟网络集成。 可以在部署模板中延迟设置 Azure 文件存储,以适应此行为。 有关详细信息,请参阅本文后面的 Azure 资源管理器部署 。
在这种情况下,应用服务会使用默认的文件系统,直到设置了 Azure 文件存储,并且不会将文件复制过去。 必须确保在装载 Azure 文件存储之前的过渡期间不会发生部署尝试。
Application Insights 检测的注意事项
应用可以使用 APPINSIGHTS_INSTRUMENTATIONKEY 或 APPLICATIONINSIGHTS_CONNECTION_STRING 应用程序设置来与 Application Insights 集成。
对于应用服务和 Azure Functions,Azure 门户还使用这些设置来显示来自资源的遥测数据。 如果从 密钥保管库 引用了这些值,则此方法不可用。 相反,需要直接使用 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(...)。 此情况可能会导致应用程序引发错误,因为它需要具有不同值的机密。
未能解决的常见原因是密钥保管库访问策略配置不当。 但是,原因也可能是机密不再存在,或者引用包含语法错误。
如果语法正确,可以通过在 Azure 门户中检查当前解析状态来查看错误的其他原因。 转到 “应用程序设置” ,然后选择“ 编辑 ”以获取相关参考。 编辑对话框显示状态信息,包括任何错误。 如果未看到状态消息,则表示语法无效,无法识别为 密钥保管库 引用。
还可以使用其中一个内置检测器来获取详细信息。
若要在应用服务中使用检测器,请执行以下步骤:
- 在 Azure 管理门户中,转到你的应用。
- 选择“诊断和解决问题”。
- 选择 “可用性和性能>Web 应用”关闭。
- 在搜索框中,搜索并选择“密钥保管库应用程序设置诊断”。
若要在 Azure Functions 中使用检测器,请按照以下步骤操作:
- 在 Azure 管理门户中,转到你的应用。
- 选择“诊断和解决问题”。
- 选择 可用性和性能>函数应用关闭或报告错误。
- 选择“密钥保管库应用程序设置诊断”。