在部署时从Azure Function获取Azure Function密钥?

9
我正在使用SendGrid绑定在Azure Functions中发送电子邮件。作为该电子邮件内容的一部分,我想包含一个指向Azure Functions实例中HTTP方法之一以获取更多信息的链接。我已经将所有的HTTP函数都设置了AuthorizationLevel.Function安全级别。
我看到了在PowerShell中从ARM和Kudu中抓取密钥(还有这个)的解决方案以及仅使用ARM输出密钥的解决方案,但这两种方法都依赖于我所缺少的东西:访问ARM(Azure资源管理)API的权限。
我也发现了 Azure Functions主机的密钥管理API,在本地工作得非常好,但是一旦部署Azure函数后,我不知道如何跨过 401未经授权。我可以使用 _master 函数密钥手动跨过此问题,但随后我又不知道如何在运行时获取该密钥。
问题是:是否可能从Azure函数主机在运行时获取Azure函数的密钥?我非常希望不需要ARM权限来实现这一点。

你是如何部署你的函数应用和函数代码的?使用DevOps管道可以让你轻松实现这一点。据我所知,函数无法知道它们自己的密钥。使用KUDU编写任何内容到你的函数应用中会感觉像是一个糟糕的设计,因为存在循环引用。检索密钥并将其放入密钥保险库中的管道,然后使用C#从密钥保险库中检索它们是最安全的方式。即使你不喜欢CI,如果你手动将密钥放入保险库中,也可以实现这一点。如果你想让我围绕所有这些问题提供答案,请告诉我。 - Pete Philters
@PhilPeters 我正在使用 ARM 模板通过 VSTS 进行 CI/CD 部署,因此在这种情况下肯定可以工作,因为我已经将 SendGrid API 密钥存储在 KeyVault 中。请详细说明我应该向 ARM 模板添加哪些部分 :) - Tom
5个回答

7

请尝试以下两个步骤:

  1. get the host master key:

    GET https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourcegroupName}/providers/Microsoft.Web/sites/{functionApp}/functions/admin/masterkey?api-version=2016-08-01
    
  2. Get the function keys:

    GET https://{functionApp}.azurewebsites.net/admin/functions/{functionName}/keys?code={masterKeyFromStep1}
    

第二步的响应:

    {
      "keys": [
        {
          "name": "default",
          "value": "xxxxxxxxxxxxxxxxxxxxxx"
        }
      ],
      "links": [
        {
          "rel": "self",
          "href": "https://myFncApp.azurewebsites.net/admin/functions/myFunction/keys"
        }
      ]
 }

更新:

请注意,第一步需要使用以下格式的授权标头:

Authorization: Bearer bearerToken

可以从Azure Active Directory (AAD)获取一个bearerToken字符串,示例中的代码片段如下:

    private string AccessToken(string clientID)
    {
        string redirectUri = "https://login.live.com/oauth20_desktop.srf";
        authContext = new AuthenticationContext("https://login.windows.net/common/oauth2/authorize", TokenCache.DefaultShared);
        var ar = authContext.AcquireTokenAsync("https://management.azure.com/", clientID, new Uri(redirectUri), new PlatformParameters(PromptBehavior.SelectAccount)).Result;
        return ar.AccessToken;
    }

请注意,clientID是您在AAD中注册的应用程序的quid,该应用程序具有访问Windows Azure Service Management API的API权限。

1
这里有一些隐含的步骤,比如需要使用具有适当权限的服务主体或标识来获取承载令牌,并将该承载令牌包含在您的Get请求的身份验证标头中。 - Josh
我正在尝试避免请求任何ARM权限,因为我无法控制那些资源(企业IT),所以虽然这是一种解决方案,但对我来说不起作用。我的Azure函数正在运行,作为AAD服务主体。 - Tom
PSA:您可以使用 Azure 搜索管理文档中的“尝试”按钮生成预览承载令牌。https://learn.microsoft.com/en-us/rest/api/appservice/webapps/getfunction - Samuel Jenks
第一步现在不再可行了。我收到了错误消息:运行时密钥存储在 Blob 存储中。是否有另一种方法可以检索它们(而无需切换到文件存储)? - Sunib

2
要在CI管道中使用ARM模板实现这一点,您需要确保您的密钥保管库和函数应用程序在同一个资源组中。
  1. Deploy your function app using ARM
  2. Deploy the function to the function app - update this code to look for the key from keyvault as you've mentioned you do for your SendGrid API Key
  3. Run the below as an ARM template ensuring it is run as incremental. This will get the key from the named function and put it into the desired key vault.

    {
        "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "functionAppName":: {
                "type": "string",
                "metadata": {
                    "description": "The name of the function app that you wish to get the key from."
                }
            },
            "functionName": {
                "type": "string",
                "metadata": {
                     "description": "The name of the function that you wish to get the key from."
              }
            },
            "keyVaultName": {
                "type": "string",
                "metadata": {
                    "description": "The name of the key vault you wish to put the key in."
                }
            }
        },
        "variables": {
            "functionAppName": "[parameters('functionAppName')]",
            "keyVaultName": "[parameters('keyVaultName')]",
            "functionName": "[parameters('functionName')]"
        },
        "resources": [
            {
                "type": "Microsoft.KeyVault/vaults/secrets",
                "name": "[concat(variables('keyVaultName'),'/', variables('functionAppName'))]",
                "apiVersion": "2015-06-01",
                "properties": {
                    "contentType": "text/plain",
                    "value": "[listsecrets(resourceId('Microsoft.Web/sites/functions', variables('functionAppName'),  variables('functionName'),'2015-08-01').key]"
                },
                "dependsOn": []
            }
        ]
    }
    

函数应用程序 V2 已经出现故障。https://github.com/Azure/azure-functions-host/wiki/Changes-to-Key-Management-in-Functions-V2 - Sujit Singh

1

Powershell方法:

$funcKey = (Invoke-AzResourceAction `
    -Action listKeys `
    -ResourceType 'Microsoft.Web/sites/functions/' `
    -ResourceGroupName $resourceGroup `
    -ResourceName "$funcAppName/$funcName" `
    -Force).default

0

您可以通过 Kudu 使用 HTTP 获取主密钥和功能密钥:

示例(使用 Powershell):

$RSGROUP="mygroup"
$WEBAPPNAME="myfunctionsapp"
$function="myfunction"

$DeploymentUrl = Get-AzWebAppContainerContinuousDeploymentUrl -ResourceGroupName $RSGROUP -Name $WEBAPPNAME

$userpass = $DeploymentUrl.split("@")[0].Replace("https://","")
$kuduCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($userpass))

$jwt = Invoke-RestMethod -Uri "https://$WEBAPPNAME.scm.azurewebsites.net/api/functions/admin/token" -Headers @{Authorization=("Basic {0}" -f $kuduCreds)} -Method GET

$masterkey=(Invoke-RestMethod "https://$WEBAPPNAME.azurewebsites.net/admin/host/systemkeys/_master" -Headers @{Authorization="Bearer $jwt"}).value
$functionkey=(Invoke-RestMethod "https://$WEBAPPNAME.azurewebsites.net/admin/functions/$function/keys" -Headers @{Authorization="Bearer $jwt"}).keys[0].value

echo $masterkey
echo $functionkey

0
Nuget: Microsoft.Azure.Management.Fluent API现在可以管理密钥:
using Microsoft.Azure.Management.AppService.Fluent;
    
var functionApp = AzureInstance.AppServices.FunctionApps.GetByResourceGroup(resourceGroupName, functionAppName);
foreach (var function in functionApp.ListFunctions())
{
    var functionName = function.Name.Split('/')[1];
    var functionKeys = functionApp.ListFunctionKeys(functionName);

    functionKeys.TryGetValue(keyName, out string functionKey);
    dict.Add(functionName, functionKey);

}

您也可以设置新的密钥: (将秘钥设为null以自动生成密钥)

foreach (var function in functionApp.ListFunctions())
{
    var functionName = function.Name.Split('/')[1];
    var nameValue = functionApp.AddFunctionKey(functionName, keyName,null);
}

注意事项:为了使其在生产环境中正常工作,FunctionApp Identity 必须具有 Function App 的 RBAC Owner 角色。这可以在“访问控制(IAM)”选项卡上进行设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接