通过


你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在Azure 应用服务、Azure Functions和Azure 逻辑应用(标准)中使用密钥保管库引用作为应用设置

本文介绍如何在通过 Azure 应用服务、Azure Functions 或 Azure 逻辑应用(标准版)创建的应用中,将来自 Azure 密钥保管库 的机密用作应用设置连接字符串中的值。

密钥保管库是一项服务,提供集中式机密管理,可以完全控制访问策略和审核历史记录。 当应用设置或连接字符串是 密钥保管库 引用时,应用程序代码可以像使用其他任何应用设置或连接字符串一样使用它。 这样一来,除了应用的配置之外,还可以维护机密。 应用程序设置在静态时已安全加密,但如果您需要进行机密信息管理,应将它们存入密钥保管库。

请授予应用对密钥保管库的访问权限

若要从密钥保管库读取机密,首先需要创建保管库,并授予应用访问它的权限:

  1. 创建密钥保管库,按照密钥保管库 快速入门中的步骤进行。

  2. 为应用程序创建一个托管标识

    默认情况下,密钥保管库引用会使用应用的系统分配的标识,但你可以指定用户分配的标识

  3. 为已创建的托管标识授予对密钥保管库中的机密的读取访问权限。 操作方式取决于你的密钥保管库的权限模型:

访问网络受限保管库

如果你的保管库配置了网络限制,则请确保该应用程序具有网络访问权限。 保管库不应依赖于应用的公共出站 IP 地址,因为机密请求的源 IP 地址可能有所不同。 应改将保管库配置为接受来自应用所使用的虚拟网络的流量。

  1. 确保应用程序配置了出站网络功能,如 App 服务网络功能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}'
    
  2. 请确保保管库的配置允许你的应用能够通过其所用的网络或子网进行访问。

请注意,即使已正确配置保管库以接受来自虚拟网络的流量,保管库的审核日志也可能显示来自应用的公共出站 IP 的失败(403 - 禁止)SecretGet 事件。 随后,应用的专用 IP 将发生成功的 SecretGet 事件,这是设计使然。

使用用户分配的标识访问保管库

某些应用需要在创建时引用机密,而系统分配的标识尚不可用。 在这些情况下,请创建用户自定义标识,并提前授予它对保管库的访问权限。

向用户分配的标识授予权限后,请执行以下步骤:

  1. 为应用程序分配标识

  2. 通过将 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 终结点发出经过身份验证的 POST 请求https://management.azure.com/[Resource ID]/config/configreferences/appsettings/refresh?api-version=2022-03-01

了解来自密钥保管库的源应用设置

若要使用 密钥保管库 引用,请将该引用设置为设置项的值。 应用可以通过密钥正常引用机密。 不需更改代码。

提示

由于每个环境应使用单独的密钥保管库,大多数使用 密钥保管库 引用的应用设置应标记为插槽设置。

密钥保管库引用的格式为 @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_VALIDATION1 跳过验证。 此设置告知应用服务绕过所有检查,并且不会为你创建内容共享。 应确保提前创建内容共享。

注意

如果跳过验证并且连接字符串或内容共享无效,则应用程序无法正确启动,并会导致 HTTP 500 错误。

在创建应用过程中,尝试装载内容共享可能会失败,因为未传播托管标识权限或未设置虚拟网络集成。 可以在部署模板中延迟设置Azure 文件存储,以适应此行为。 有关详细信息,请参阅本文后面的 Azure 资源管理器 部署

在这种情况下,App Service 使用默认文件系统,直到 Azure 文件存储 设置完成,并且期间不会复制文件。 必须确保在装载Azure 文件存储之前,在此期间不会发生部署尝试。

应用程序洞察工具使用的注意事项

应用可以使用 APPINSIGHTS_INSTRUMENTATIONKEYAPPLICATIONINSIGHTS_CONNECTION_STRING 应用程序设置来与 Application Insights 集成。

对于 App Service 和 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门户中检查当前解决状态来查看错误的其他原因。 转到 “应用程序设置” ,然后选择“ 编辑 ”以获取相关参考。 编辑对话框显示状态信息,包括任何错误。 如果未看到状态消息,则表示语法无效,无法识别为密钥保管库引用。

还可以使用其中一个内置检测器来获取详细信息。

若要在应用服务中使用检测器,请执行以下步骤:

  1. 在 Azure 门户中,转到您的应用程序。
  2. 选择“诊断和解决问题”。
  3. 选择 “可用性和性能>Web 应用”关闭
  4. 在搜索框中,搜索并选择密钥保管库 应用设置诊断

若要将检测器用于Azure Functions,

  1. 在 Azure 门户中,转到你的应用程序。
  2. 转到 平台功能
  3. 选择“诊断和解决问题”。
  4. 选择 可用性和性能>函数应用关闭或报告错误
  5. 选择密钥保管库 应用程序设置诊断