教程:使用密钥保管库保护从 .NET 应用服务进行的认知服务连接
Azure 应用服务可以在不使用连接字符串的情况下通过托管标识连接到后端服务,这样就无需使用连接机密在生产环境中管理后端连接并保持其安全性。 对于不支持托管标识但仍需要连接机密的后端服务,可以使用密钥保管库来管理连接机密。 本教程以 Azure AI 服务为例,介绍如何在实践中采取这种做法。 完成本教程后,你将获得一个应用,它会对 Azure AI 服务发出编程调用,且不会在应用服务中存储任何连接机密。
提示
Azure AI 服务支持通过托管标识进行身份验证,但本教程使用订阅密钥身份验证来演示如何从应用服务连接到不支持托管标识的 Azure 服务。
使用此体系结构:
- 与密钥保管库的连接受托管标识的保护
- 应用服务使用密钥保管库引用作为应用设置来访问机密。
- 仅限该应用访问密钥保管库。 应用参与者(例如管理员)可能完全控制应用服务资源,但同时无权访问密钥保管库机密。
- 如果你的应用程序代码可以通过应用设置访问连接机密,则无需进行任何更改。
要学习的知识:
- 启用托管标识
- 使用托管标识连接到密钥保管库
- 使用 Key Vault 引用
- 访问 Azure AI 服务
先决条件
为 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。
创建与 Azure AI 服务连接的应用
创建一个资源组以包含你的所有资源:
# Save resource group name as variable for convenience groupName=myKVResourceGroup region=chinanorth3 az group create --name $groupName --location $region
创建 Azure AI 服务资源。 请将
替换为你选择的唯一名称。 # 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 应用
在本地克隆示例存储库,并将示例应用程序部署到应用服务。 将
# 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
将机密配置为应用设置
将 Azure AI 服务机密配置为应用设置
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
以部署应用,并使用各种语言的字符串尝试运行语言检测器。查看应用程序代码时,你可能会注意到,检测结果的调试输出与背景的字体颜色相同。 突出显示结果正下方的空格即可看到输出。
保护后端连接
连接机密此时作为应用设置存储在应用服务应用中。 这种方法已经能够保护应用程序代码库中的连接机密。 但是,任何可以管理你的应用的参与者也能看到应用设置。 在此步骤中,请将连接机密移到密钥保管库并锁定访问权限,以便只有你可以管理该密钥保管库,并且只有应用服务应用可以使用其托管标识来读取该密钥保管库。
创建密钥保管库。 将
替换为唯一名称。 # 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
参数--enable-rbac-authorization
。 默认情况下,此设置会使所有访问策略权限失效。为你自己分配对保管库的“密钥保管库机密主管”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 服务终结点。
恭喜,你的应用现在正在使用密钥保管库中保存的机密连接到 Azure AI 服务,而无需对应用程序代码进行任何更改。
清理资源
在前面的步骤中,你在资源组中创建了 Azure 资源。 如果认为将来不需要这些资源,请在本地 Shell 中运行以下命令删除资源组:
az group delete --name $groupName
此命令可能需要花费一点时间运行。