I understand that Azure Functions are potentially open endpoints on the internet if I read Microsoft’s documentation correctly and per conversations with a friend who has some experience working with web development paradigms that Azure Functions leverages. A cursory reading of security forums and stack overflow questions on the topic leads me to understand at least a couple options of securing them namely
Context/ What does my Azure Function do? It manages a blob container related to an ETL of vendor data from a SFTP source to a SQL Endpoint which this ETL utilizes an intermediary blob container for file transfer and long term cold storage of source data. The Azure Function moves the blobs from one container to an archive container after they have been loaded to the SQL endpoint. Why Azure Function to manage the blob containers?
An example of one of the functions is shown here below:
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Collections.Generic;
using System.Text;
namespace AFA_ArchiveBlob
{
public static class HttpTrigger_BlobInput
{
[FunctionName("HttpTrigger_BlobInput")]
public static async Task<HttpResponseMessage> Run(
//public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "{name}")] HttpRequest req,
string name,
ILogger log,
[Blob("{name}/blobname",FileAccess.ReadWrite,Connection = "AzureWebJobsStorage")] CloudBlobContainer myCloudBlobContainer
)
{
//Execution Logged.
log.LogInformation($"HttpTrigger_BlobInput - C# HTTP trigger function processed a request.");
//Run the query against the blob to list the contents.
BlobContinuationToken continuationToken = null;
List<IListBlobItem> results = new List<IListBlobItem>();
do
{
var response = await myCloudBlobContainer.ListBlobsSegmentedAsync(continuationToken);
continuationToken = response.ContinuationToken;
results.AddRange(response.Results);
}
while (continuationToken != null);
//Query the names of the blobs. Todo: can this be a single line linq query select instead?
List<string> listBlobNames = new List<string>();
foreach (CloudBlockBlob b in results)
{
listBlobNames.Add(b.Name);
}
//Serialize the list of blob names to json for passing to function caller via return statement
var jsonReturn = JsonConvert.SerializeObject(listBlobNames);
log.LogInformation("Returning the following JSON");
log.LogInformation(jsonReturn);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonReturn, Encoding.UTF8, "application/json")
};
}
}
}
Firstly, even though using keys might be convenient, I see that official documentation advises against using keys to secure function endpoint in production scenarios.
I suggest it would be a better choice to go with Azure Active Directory for security.. as explained here Secure an HTTP endpoint in production
How to Implement
I see two possible approaches:
1. Simple Approach: Check that calling application is your Azure logic app specifically
Enable Azure Active Directory Authentication for your Azure Function App. You can simply use Express settings (with create a new Azure AD app)
Enable Managed Service Identity for your Logic App.
Find out appid for Managed Service Identity associated with your logic app.. go to Azure Portal > Azure Active Directory > Enterprise Applications > All Applications > Relevant Service Principal (Explained in more detail with screenshots in another SO post here)
Authenticate your logic app to Azure function using Managed Service Identity as explained here.. Authenticate with managed identity in logic app.. note that resource being accessed will be your Azure function.
In your function code, now you can check that appid
claim in access token should exactly match the appid
for logic app (i.e. logic app is the one calling your function).. otherwise you can reject the call with Unauthorized exception.
2. A more declarative Approach: Have an application permission defined for Azure function app and check for this permission/role being present in auth token from client calling your function
This approach is a little more declarative, as you define an application permission that needs to be assigned to any application that can call your Azure function.
Enable Azure Active Directory Authentication for your Azure Function App. You can simply use Express settings (with create a new Azure AD app)
Now go to Azure Active Directory > App Registrations > App registration for your function app > Manifest
Add a new application role.. using json like this:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Can invoke my function",
"id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
"isEnabled": true,
"description": "Apps that have this role have the ability to invoke my Azure function",
"value": "MyFunctionValidClient"
}]
Enable Managed Service Identity for your Logic App.
Find out appid for Managed Service Identity associated with your logic app.. as already explained in approach 1 above
Assign the app permission to this managed service identity..
New-AzureADServiceAppRoleAssignment -ObjectId <logicappmsi.ObjectId> -PrincipalId <logicappmsi.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <yourfunctionaadapp.ObjectId>
Authenticate your logic app to Azure function using Managed Service Identity.. as already explained in approach 1 above
Now, in the auth token received by your function, you can check that the role
claims collection must contain a role named "MyFunctionValidClient"
otherwise you can reject the call with Unauthorized exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With