Pages

Monday, September 12, 2022

Securing Azure Function App to require authentication and granting access to a Logic Apps’ Managed Identity

One of the topics I’ve been meaning to write about is how to secure Function Apps with authentication to prevent unauthorized calls because a newly created Function App and its functions are by default accessible via the internet so anyone who knows the URL would be able to call it without authentication. Take one of my previous posts:

Using Azure Function App and Logic Apps to create an automated report that downloads a CSV, creates a HTML table, sends an email with the HTML table and attaches the CSV
http://terenceluk.blogspot.com/2022/08/using-azure-function-app-and-logic-apps.html

… where I created an Azure Function that expects a POST method containing the Cylance Token it can use to download an export of all the devices, generate an HTML report, then return the content. While one can argue that even if the URL was discovered, a malicious attacker would need to know the Cylance Token to obtain any meaningful data back. This is true but a malicious attacker could also cause financial impact by calling the Azure Function repeatedly to cause unnecessary charges.

Below is the Function App:

image

… and the Function within the Function App that contains the PowerShell script to process the request after receiving the token:

image

Confirming Function App does not require authentication with Postman

To demonstrate that this function does not require authentication, we can test by using the Get Function Url:

image

Copy the URL:

image

Then open Postman or any API testing tool, create a post request with the URL:

image

Select No Auth for the Type under Authorization:

image

We’ll be passing a JSON to the Function App so navigate to Headers and uncheck Content-Type:text/plain, then add the following header:

Key: Content-Type
Value: application/json

image

Navigate to the Body section and paste the Cylance token for the report:

image

Proceed to send the request and you’ll see a Status: 200 OK with the returned HTML of the report:

image

Creating a System Managed Identity for the Logic App that calls the Function App

In this example, I’ll be configuring a System Managed Identity for the Logic App that is used to call the Function App to generate the report so it (the Logic App) can take the report and email it out. The System Managed Identity will be given permissions to call the Function App and any calls to it without authentication will fail. The use of System Managed Identities allows for the Logic App to authenticate with an Azure AD identity without having to provide a secret. Azure manages the identity so there are no secrets to rotate / maintain.

**Note that it is also possible to User-assigned Identity as well and the official Microsoft documentation can be found here: https://docs.microsoft.com/en-us/azure/logic-apps/create-managed-service-identity?tabs=consumption

Begin by opening the Logic App that calls the Function App, then navigate to its Identity blade:

image

Under the System assigned heading, switch the Status from Off to On and click on Save:

image

Note the message being displayed indicates that the Logic App will be registered in Azure AD so it can be granted permissions to access other resources protected by Azure AD (in this case it will be the Function App):

image

The process should complete in a few seconds and a Object (principal) ID will be displayed. This attribute represents the Logic App’s Managed Identity:

image

We’ll need the Application ID of the Logic App’s Managed Identity, which isn’t displayed in the Identity blade of the Logic App (don’t mistaken the Object Principal ID for the App ID) and it can be retrieved in the Azure AD Enterprise Application list when Application Type is configured as All Applications or Managed Identities:

image

Copy the Application ID value as we’ll need it to grant this Logic App permissions to the Function App.

Require Authentication for Function Calls

The first requirement to allow the Logic App’s Managed Identity to authenticate with the Function App is to set the Function App’s authentication level to anonymous. Failure to do so will result in the Logic App workflow throwing a BadRequest error.

Begin by opening the Function App, navigate to the Advanced Tools blade, then click on Go to open Kudu Services:

image

image

Click on the Debug Console menu and select CMD:

image

Navigate to site > wwwroot > <function name>

image

Edit the function.json file and look for authLevel in the bindings. If the property exists, set the property value to anonymous. Otherwise, add that property and set the value as anonymous:

image

The Function App in this example already has authLevel so we just need to update function to anonymous:

image

Clicking the Save button will bring us back to the command prompt:

image

Create an App Registration for the Function App in Azure AD

With the Logic App having a managed identity to authenticate against the Function App, we now need to create a App Registration for the Function App in Azure AD so it can be used to grant permissions to the managed identity of the Logic App.

We’ve already copied the Object (principal) ID for the Logic App earlier but will also need the tenant ID of the Azure AD, which can be retrieved by navigating to Azure Active Directory:

image

Then in the Overview blade, copy the Tenant ID:

image

Navigate back into the Function App and click on the Authentication blade, then the Add an identity provider button:

image

Select Microsoft for the Identity provider so we can use Azure AD identities:

image

In the App registration type section, select Provide the details of an existing app registration:

image

Fill in the following fields:

Application (client) ID: <The Logic App’s Application ID>
Client secret (recommended): <blank>
Issuer URL: https://sts.windows.net/<Tenant-ID>
Allowed token audiences: <The Logic App’s Application ID>
Restrict access: Require authentication
Unauthenticated requests: HTTP 401 Unauthorized: recommended for APIs

   image

Proceed to click Add to save the settings and you should see an identity provider representing the Logic App listed in the Authentication blade of the Function App:

  image

Confirming Function App requires authentication with Postman and Portal

Repeating the steps we performed previously with Postman should now return a Status: 401 Unauthorized and the message:

You do not have permission to view this directory or page.

image

Attempting to navigate to the URL of the Function App should also display the same message:

image

Instead of the default Your Functions 4.0 app is up and running:

image

Updating Logic App to authenticate with Managed Identity

Attempting to immediately trigger the Logic App after the changes will fail and return an Unauthorized response when calling the Azure Function App:

image

To correct the issue, enter the designer mode for the Logic App, edit the Function App step, click on Add new parameter, check the Authentication box:

image

Select Managed identity for the Authentication type:

image

Select System-assigned managed identity for Managed identity and <Logic App’s Managed Identity Application ID> as the Audience:

  image

Proceed to run the trigger and confirm that steps succeed:

image

**Note that one of the common issues I’ve come across is if the Logic App’s Object (principal) ID used as the Application ID when configuring the Azure Function App’s Authentication. Doing so may cause the following error to be thrown when executing the Logic App:

BadRequest. Http request failed as there is an error getting AD OAuth token: 'AADSTS500011: The resource principal named a6486475-xxx-xxx-xxxx-6exxxxxx882f was not found in the tenant named Contoso. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant. Trace ID: a31a44e7-7135-450f-a420-aa801a8ec003 Correlation ID: 58f958c1-6a75-4fef-bc75-bb22dae4c8c9 Timestamp: 2022-09-11 20:48:23Z'.

image

Ensure that you had used the Application ID for the appropriate fields and this error should be resolved.

I hope this helps anyone who might be looking for a walkthrough for securing an Azure Function App and using Managed Identity for a Logic App to call the function. The Microsoft documentation today isn’t very clear to me so I hope this will provide some clarity.

Thursday, September 8, 2022

Using an Azure Automation Account Runbook to create and email a Duo report with SendGrid

I received a lot of great feedback for my previous Azure Function App with Logic App posts, which demonstrated how to create HTML reports that were scheduled to be mailed out at the end of the one. A common question that I received after the posts was whether it was possible to create these reports with Azure Automation Accounts and Runbooks. The answer is certainly, yes, and I would like to demonstrate this with another report I had created for providing a count of the amount of users provisioned in Cisco Duo, which is commonly used for providing 2-factor authentication.

Along with using an Azure Automation Account for reports, I would also like to use this post to demonstrate the following:

  1. How to import custom PowerShell Module into an Automation Account (we’ll be using a the Duo PowerShell module that Matt Egan has created and shared via his GitHub: https://github.com/mbegan/Duo-PSModule)
  2. How to use a Automation Account Runbook to generate a report
  3. Storing and retrieving a secret in Azure Key Vault (we’ll be storing the API key for SendGrid in an Azure Key Vault)
  4. Using SendGrid to the report via email

Step #1 – Create an Automation Account

The first step is to create an Automation Account that we’ll be using to host the Runbook to generate the report and use SendGrid to send it out. We’ll be storing the SendGrid API key in an Azure Key Vault and therefore need to grant access to those keys for the Automation Account and that’s why this needs to be created first.

image

image

We’ll need the Automation Account’s System assigned Object (principal) ID to grant it permissions to access the Key Vault secret so navigate to the Identity blade and copy the Object (principal) ID that will be used in the following steps:

image

Step #2 – Create an Azure Key Vault, add SendGrid API Key as Secret, grant Automation Account permissions to read secret

With the Automation Account created, proceed to create an Azure Key Vault if you do not already have one:

image

The preferred way for the Access configuration today is Azure role-based access control so our Automation Account will only be able to access a specific secret. The older Vault access policy requires permissions to be granted to the whole vault, which makes it difficult to secure other secrets in the vault. Proceed to select Azure role-based access control as the Permission model under the Access Policy menu:

image

Customize the settings as required or leave them as default and create the Azure Key Vault.

image

The account used to create the new vault will not have any permissions to the vault so attempting to perform any operations areas such as Secrets will display the message: You are unauthorized to view these contents.

image

Proceed to grant the account you’re logged in as through the Access control (IAM) blade > Add role assignment:

image

Search for Key Vault Administrator and grant the permission:

image

image

With the appropriate permissions to the vault assigned, proceed to create a new Secret to store the SendGrid API Key:

image

image

image

With the secret created, we can now grant the Automation Account the ability to retrieve the secret. Click on the SendGrid secret:

image

In the SendGrid secret, navigate to Access Control (IAM) blade, Add, then Add role assignment:

image

image

Select Key Vault Secrets User as the role:

image

Select Managed identity, then locate the Automation Account and select it:

image

Proceed to assign the permissions:

image

The Automation Account should now have permissions to retrieve the SendGrid secret value;

Step #3 – Import the custom Duo PowerShell Module

Rather than attempt to write the PowerShell code required to authenticate with the Duo Admin API (https://duo.com/docs/adminapi) with a HMAC signature, then call the API methods, we’ll be using Matt Egan’s PowerShell module he has shared with the community years ago that still works today https://github.com/mbegan/Duo-PSModule

The Duo PowerShell module Matt Egan provided does not simply upload into Azure Automation’s Modules blade as the psd1 file references to the Duo_org.ps1 file that is mean to store the information required to connect to the Duo API.

Neil Sabol has a great write up that explains this and how to workaround the issue so I’ll be using his method to demonstrate the configuration: https://blog.neilsabol.site/post/importing-duo-psmodule-mfa-powershell-module-azure-automation/

The method I’ll be using is not to upload a blank Duo_org.ps1 file but rather comment all references to it in the Duo.psd1 file. You can find the updated file here in my GitHub: https://github.com/terenceluk/Azure/blob/main/Automation%20Runbook/Duo/Duo.psd1

Proceed to download the Duo.psd1 and Duo.psm1 from my GitHub https://github.com/terenceluk/Azure/tree/main/Automation%20Runbook/Duo, zip them up into a package named Duo.zip (make sure Duo.zip is the file name) then import them into the Automation Account Modules:

imageimage

I haven’t had much luck with 7.1 as the Runtime version so proceed to select 5.1 as the Runtime version:

image

Initiate the import:

image

Confirm the module has successfully imported:

image

One of the ways to check and see if the module imported properly is by clicking into the module and verify that the available cmdlets are displayed:

image

Step #4 – Create a Protected Application in Duo and add authentication information as Automation Account encrypted variables

Using the Duo Admin API requires authentication so we’ll need to create a protected application in the Duo Admin portal as described in the document here: https://duo.com/docs/adminapi

imageimage

Copy the Integration key, Secret key, and API hostname as we’ll need them to create the encrypted variables in the following steps, and grant the application the required permissions:

image

Proceed to the Automation Account, navigate to the following variables:

  1. MyDuoDirectoryID
  2. MyDuoIntegrationKey
  3. MyDuoSecretKey
  4. MyDuoAPIHostname
image

Step #5 – Create the runbook to generate a report of the users and email via SendGrid

With all the components and permissions created and configured, the last step is to create the runbook and put the code in that will build the report and use the SendGrid API to send the email report. From within the Automation Account, navigate to the Runbooks blade:

image

Click on Create a runbook:

image

Fill in the required fields:

image

The following PowerShell Runbook will be displayed where we can paste the PowerShell script to be executed:

image

The script I will be using to generate and email the report can be found here: https://github.com/terenceluk/Azure/blob/main/Automation%20Runbook/Email-Duo-User-Count-and-List.ps1

Customize the following variables:

$VaultName = "kv-Production" # Azure Key Vault Name

$destEmailAddress = "tluk@contoso.com" # From address

$fromEmailAddress = "duoreport@contoso.com" # To address

$subject = "Duo User Report" # Email Subject

<h2>Client: Contoso Limited</h2>

Proceed to paste the code into the runbook:

image

Proceed to click Save:

image

Before publishing the Runbook, click on the Test pane button to bring up the test window, then click on the Start button to test the runbook:

image

image

Confirm that the test successfully completes:

image

Verify that the email sent contains the expected report as such:

image

Proceed to publish the report once we’ve confirmed that the report is delivered:

image

The last step is to schedule this runbook according to when the report should be ran so proceed to click on the Schedules blade and then Add a schedule:

image

Click on Link a schedule to your runbook:

image

Click on Add a Schedule:

image

Configure the desired schedule (the settings in the screenshot is configured to run on the last day of the month):

image

The schedules pane should now display the configured schedule and its next run time:

image

That’s it. Hope this helps anyone who might be looking for information on how to configure the various components demonstrated in this post.