Friday 27 March 2020

Secure PowerShell Azure Function APIs through API Management and custom roles - Part 2

The first part of this post can be found here : https://vipulkelkar.blogspot.com/2020/03/secure-powershell-azure-function-apis.html

To secure the API we built in the previous post, we will create and configure an API management instance. Create a new one through Azure portal and import the Azure Function App in API management, some guidance provided here : https://docs.microsoft.com/en-us/azure/api-management/add-api-manually


API Management allows us to configure access restriction policies. Since we want to restrict access to the APIs based on specific roles, we will configure a Validate-JWT policy that validates the 'roles' claim in the access token: https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#ValidateJWT

Let's configure a policy for '/Applications' endpoint. The client SPN 'MyOrg-API-Consumer' from has access to the '/Applications' endpoint. So we will validate that the access policy works when this endpoint is hit. Then we will also verify the case that  'MyOrg-API-Consumer-Devices' SPN cannot hit the '/Applications' endpoint since it only has access to '/Devices'

In the API management instance, we navigate to our API and select the /Applications endpoint. From the Inbound processing section, a new policy can be added through the 'Add Policy' button.


















This opens a form to configure the policy. We can also choose to add the policies using the code editor. On the base policy which is added by default, select the code editor.




















We start off by putting the validate-jwt policy. Just below the set back-end service policy, we configure the validation for the 'roles' claim in the access token received in the request

 <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. You do not have the assigned role to access this endpoint.">
            <required-claims>
                <claim name="roles" match="any">
                    <value>Users</value>
                </claim>
            </required-claims>
 </validate-jwt>

Now, from the 'TEST' tab, try to call the '/Applications' API endpoint. Make sure you have acquired an access token using the 'MyOrg-API-Consumer' SPN and add it to the header. API management allows us to setup OAuth and use the developer portal which has the ability to get access tokens : https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-oauth2

However, in this case i have manually supplied the access token in the header by fetching it through Postman

























This call fails with 401 Unauthorized however we expected the call to succeed. To dig further, API Management has error handling feature which can be added to the policy code to get the details of the error and troubleshoot : https://docs.microsoft.com/en-us/azure/api-management/api-management-error-handling-policies
After error handling didn't help and weirdly the test threw a  500 Internal Server Error.

Turned out that the OpenID specification url 'openid-config url' had to be supplied to the policy for it to work.  This value specifies the path to the OpenID v2.0 config file. You can view the config by opening the file in browser.

https://login.microsoftonline.com/[Tenant-ID]/v2.0/.well-known/openid-configuration

Access token and token validation concepts are detailed in reference documentation : https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens

Trying out another test failed however, this time the error details were visible since we have added the error tags in the policy.


Issuer validation failed. Issuer: 'https://sts.windows.net/[Tenant-ID]/'. Did not match: validationParameters.ValidIssuer: '' or validationParameters.ValidIssuers: 'https://login.microsoftonline.com/[Tenant-ID]/v2.0'

This is because with the OpenID config file set, the policy expects a v2.0 token coming through. The token that I had acquired through Postman was for v1.0 token endpoint.

This can be traced back to the "accessTokenAcceptedVersion" property in the app registration that registers our API. By default it is set to 'null' which means that when a token is fetched with the url of our API in audience, a v1.0 token will be issued: More details here : https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens

So we let our policy know, that we will be using v1.0 tokens. The final policy setup looks like below with the issuer check in place.
(replace the [tenant-id] with your tenant ID in the code)



The api test call works and we get a OK response.

Similarly a TEST with MyOrg-API-Consumer-Devices access token which does not have access to '/Applications' API, results is 401 Unauthorized. To make sure our policy is working, we can validate that the message we get in 401 response is the one we configured in the policy.

The direct calls to the back end Azure function API can be secured to that it accepts calls only through API management instance. Here is some documentation on how to achieve that : https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-mutual-certificates

Monday 23 March 2020

Secure PowerShell Azure Function APIs through API Management and custom roles - Part 1

Azure Functions enable us to quickly build and publish APIs and also secure it using Azure Active Directory. While it is fairly straight forward to implement role-based access control within the custom API code using ASP.net and middle ware, it could be tricky in PowerShell Azure Functions. This involves extracting the roles claims from the access tokens and validating it in the code when the PowerShell API endpoint starts execution - not that this is too complex to implement.

Another way could be to use API Management to configure access policies and secure the endpoints based on roles. This approach can be easier if you are already using API management's other features for your custom API.

Part 2 of this Post can be found here : https://vipulkelkar.blogspot.com/2020/03/secure-powershell-azure-function-apis_27.html

Background

Here is our scenario :  Our custom API exposes a set of endpoints which provide information related to our organization. Every API endpoint must be accessible through a unique permission level. The client SPNs/Apps must have specific application permission assigned to be able to acquire application tokens and access the endpoints

- /Applications
- /Devices
- /Users
- /Groups


When an Azure Function  is secured using Azure AD, the identities that can authenticate to Azure AD can access it by default. Which means that any SPN/App, would be able to access the API endpoints in Function App by default when Azure AD login is configured.

This is what we want to stop and create ROLES so that the SPNs/Apps need to be specifically provided application permission to be able to access the endpoints.

Secure PowerShell Core Azure function API :

We will work with a very simple Azure Function called "MyOrg-API" which exposes a set of GET endpoints.

Our sample API URL is : https://myorg-api.azurewebsites.net






















We have created an Azure AD App registration which represents our API.



















The APP ID URI must be the base URL of our Azure Function : https://myorg-api.azurewebsites.net



Next step is to secure the API Azure Function. Navigate to Function App -> Platform features -> Authentication/Authorization

Configure the auth setup. The Client ID is the id of the app that we registered.

























Now if you access any endpoint from the Azure function in a browser, you will be prompted for authentication and on supplying your credentials, you will get a response from the GET endpoint.

Lets see how that works with an SPN. Create a new SPN in your AAD. If you have Az PowerShell module the below command will craete an SPN

Connect-AzAccount -Tenant <Your-Tenant-ID>

New-AzADServicePrincipal -DisplayName "RandomSPN" -Scope $null -Role $null

Create a Secret for the SPN and try to call the API through postman/logic apps http action - the call goes through since SPN is able to authenticate with AAD.


Configure Roles:

A way to stop this is by defining ROLES and validating within the API whether the caller has the specified role to carry out a specific API operation.

Lets start by creating roles in the App registration which secures our API.

Navigate to the App registration -> Manifest. In the 'appRoles' property, define custom roles. Create an object for each endpoint we defined at the start of this post. Below sample explains the role confg for /Applications endpoint
Replace the 'id' value by a GUID




Now that we have configured the custom roles, we will create two consumer SPNs to demonstrate our setup. Access your SPN from the App registrations and navigate to 'API Permissions'.  Search for our custom API.


















1) MyOrg-Consumer-SPN :

This SPN will be able to access Applications, Users and Groups endpoints. Provide the permissions and Grant consent.
























2) MyOrg-Consumer-SPN-Devices :

We have another consumer SPN which will have access to only /Devices endpoints


























In the next post we will look at configuring API management to make sure that only the SPNs that have been given a role can access the respective endpoint.
https://vipulkelkar.blogspot.com/2020/03/secure-powershell-azure-function-apis_27.html