Azure 应用服务可以在不使用连接字符串的情况下通过托管标识连接到后端服务,这样就无需使用连接机密在生产环境中管理后端连接并保持其安全性。 对于不支持托管标识但仍需要连接机密的后端服务,可以使用密钥保管库来管理连接机密。 本教程使用 Foundry 工具作为示例来演示如何在实践中完成。 完成后,已有一个应用对 Foundry Tools 进行编程调用,而无需在应用服务中存储任何连接机密。
提示
Foundry 工具支持通过托管身份进行身份验证,但本教程使用订阅密钥身份验证演示如何连接到不支持应用服务托管身份的 Azure 服务。
基于这套架构:
- 与密钥保管库的连接由托管标识进行保护
- 应用服务使用密钥保管库引用作为应用设置来访问机密。
- 应用程序的访问权限仅限于密钥存储库。 应用参与者(例如管理员)可能完全控制应用服务资源,但同时无权访问密钥保管库机密。
- 如果你的应用程序代码可以通过应用设置访问连接机密,则无需进行任何更改。
要学习的知识:
- 启用托管标识
- 使用托管标识连接到密钥保管库
- 使用 Key Vault 引用
- Access Foundry 工具
先决条件
为 Azure CLI 准备环境。
如需在本地运行 CLI 参考命令,请安装 Azure CLI。 如果在 Windows 或 macOS 上运行,请考虑在 Docker 容器中运行 Azure CLI。 有关详细信息,请参阅如何在 Docker 容器中运行 Azure CLI。
如果使用的是本地安装,请使用 az login 命令登录到 Azure CLI。 若要完成身份验证过程,请遵循终端中显示的步骤。 有关其他登录选项,请参阅使用 Azure CLI 登录。
出现提示时,请在首次使用时安装 Azure CLI 扩展。 有关扩展详细信息,请参阅使用 Azure CLI 的扩展。
运行 az version 以查找安装的版本和依赖库。 若要升级到最新版本,请运行 az upgrade。
使用与 Foundry 工具的连接创建应用
创建一个资源组以包含你的所有资源:
# Save resource group name as variable for convenience groupName=myKVResourceGroup region=chinanorth3 az group create --name $groupName --location $region创建 Azure AI 服务资源。 请将 <cs-resource-name> 替换为您选择的唯一名称。
# Save resource name as variable for convenience. csResourceName=<cs-resource-name> az cognitiveservices account create --resource-group $groupName --name $csResourceName --location $region --kind TextAnalytics --sku F0 --custom-domain $csResourceName注意
--sku F0创建 Azure AI 服务资源(免费层)。 每个订阅限制于一个免费层级TextAnalytics资源的配额。 如果已超出配额,请改用--sku S。
配置 .NET 应用
在本地克隆示例存储库,并将示例应用程序部署到应用服务。 将 <app-name> 替换为唯一的名称。
# Save app name as variable for convenience
appName=<app-name>
# Clone sample application
git clone https://github.com/Azure-Samples/app-service-language-detector.git
cd app-service-language-detector/dotnet
az webapp up --sku F1 --resource-group $groupName --name $appName --plan $appName --location $region
将密钥配置为应用程序设置
将 Foundry Tools 机密配置为应用设置
CS_ACCOUNT_NAME和CS_ACCOUNT_KEY。# Get subscription key for Cognitive Services resource csKey1=$(az cognitiveservices account keys list --resource-group $groupName --name $csResourceName --query key1 --output tsv) az webapp config appsettings set --resource-group $groupName --name $appName --settings CS_ACCOUNT_NAME="$csResourceName" CS_ACCOUNT_KEY="$csKey1"在浏览器中,导航到
<app-name>.chinacloudsites.cn以部署应用,并使用各种语言的字符串尝试运行语言检测器。
查看应用程序代码时,你可能会注意到,检测结果的调试输出与背景的字体颜色相同。 尝试突出显示结果正下方的空白区域即可看到。
保护后端连接
连接机密此时作为应用设置存储在应用服务应用中。 这种方法已经能够保护应用程序代码库中的连接机密。 但是,任何可以管理你的应用的参与者也能看到应用设置。 在此步骤中,请将连接机密移到密钥保管库并锁定访问权限,以便只有你可以管理该密钥保管库,并且只有应用服务应用可以使用其托管标识来读取该密钥保管库。
创建密钥保管库。 将 <vault-name> 替换为唯一名称。
# Save app name as variable for convenience vaultName=<vault-name> az keyvault create --resource-group $groupName --name $vaultName --location $region --sku standard --enable-rbac-authorization--enable-rbac-authorization参数将 Azure 角色基于访问控制(RBAC)设置为权限模型。 默认情况下,此设置会使所有访问策略权限失效。为你自己分配对保管库的“密钥保管库机密主管”RBAC 角色。
vaultResourceId=$(az keyvault show --name $vaultName --query id --output tsv) myId=$(az ad signed-in-user show --query id --output tsv) az role assignment create --role "Key Vault Secrets Officer" --assignee-object-id $myId --assignee-principal-type User --scope $vaultResourceId为应用启用系统分配的托管身份,并为其分配“密钥保管库机密用户”RBAC角色,以访问保管库。
az webapp identity assign --resource-group $groupName --name $appName --scope $vaultResourceId --role "Key Vault Secrets User"将 Azure AI 服务资源名称和订阅密钥作为机密添加到保管库,并将其 ID 保存为环境变量,以便在下一步骤中使用。
csResourceKVUri=$(az keyvault secret set --vault-name $vaultName --name csresource --value $csResourceName --query id --output tsv) csKeyKVUri=$(az keyvault secret set --vault-name $vaultName --name cskey --value $csKey1 --query id --output tsv)前面你已在应用中将机密设置为
CS_ACCOUNT_NAME和CS_ACCOUNT_KEY应用设置。 现在,请改为将它们设置为密钥保管库引用。az webapp config appsettings set --resource-group $groupName --name $appName --settings CS_ACCOUNT_NAME="@Microsoft.KeyVault(SecretUri=$csResourceKVUri)" CS_ACCOUNT_KEY="@Microsoft.KeyVault(SecretUri=$csKeyKVUri)"在浏览器中,再次导航到
<app-name>.chinacloudsites.cn。 如果返回了检测结果,那么您正在使用密钥保管库引用连接到 Azure AI 服务端点。
恭喜,应用现在使用密钥保管库中保留的机密连接到 Foundry 工具,而无需对应用程序代码进行任何更改。
清理资源
在前面的步骤中,你在资源组中创建了 Azure 资源。 如果认为将来不需要这些资源,请在本地 Shell 中运行以下命令删除资源组:
az group delete --name $groupName
此命令可能需要花费一点时间运行。