Cloud functions are great. Who does not like automatic scalability and hands-off infrastructure management? HTTP-triggered Azure functions however are exposed to the public Internet. So if we want to use them, for example, as a part of a microservice-based application, we'd want to take steps to secure those functions from malicious attacks.
One way would be to bake authentication and authorization into the function itself. However this does not prevent public access to the HTTP endpoint. And even though the function would be secured, Azure will still charge for the execution, making it a target of "denial-of-wallet" attack.
I wish that there was an option to create a function in something like Google Cloud Virtual Private Network, or even Azure's own VNet, exposing only internal IPs by default. Well, you can use a dedicated Azure App Service Environment (ASE), but the pricing makes it prohibitive, and it greatly reduces the flexibility.
One of the solutions, as it often is in software engineering, is to insert another level of indirection. Azure API Management Service can help to secure your APIs.
In a high-level view, as seen in a cover image, APIM will stand between the client and the function endpoint, managing authentication and access, while the function will be protected by the Azure Active Directory. In addition, API Management comes with a vast number of features, like limiting quotas, API documentation, integration with payment service and many others, which is impossible to fit into one article. And that is, anyway, not what the title says.
Let's get to the subject and do step-by-step configuration of the API Management and Azure Function, as there are several tricky steps that better not to be skipped.
Enable APIM Managed Identity
The first thing that we need to do is to enable APIM Managed Identity. Well, the first thing is to create an instance of the API Management Service, but it could be easily provisioned in Azure Portal Beware though that it takes up to an hour to get it. This and consequent steps we will be doing in the Azure Portal. Navigate to your APIM instance, select the "Managed Identity" menu, and enable the checkbox.
Secure Azure Functions with Azure Active Directory
Having finished the first step, let go to the Azure Function you want to secure. (You've already created one, right?). We can safely disable existing levels of protection, like Function Keys, and make a function anonymous. Now we need to integrate it with Azure AD. For that, click on (1) "Authentication/Authorization" link in Platform Features tab. It will open a new page. We will return to the previous one and use a link number (2) "API Management" when we finish with Active Directory steps.
Enable App Service Authentication and select "Log in using AAD" from the dropdown.
Let's choose the "Express" setting to create a new Active Directory app. Give it a name, and remember it, as we'll need it in the next step.
Finding AAD Application ID
Once an Azure AD application is created, we need to find its Application ID.
In the top search bar, search for "Azure Active Directory", and select "Enterprise Applications" from the left menu. Now look up your newly created AD app (you can use a filter to type the name), and copy the Application ID.
Importing API to Azure Management Service
We are done with Active Directory. At this point you can check that your Function is not callable from the direct URL anymore, and you should get an Unauthorized response. Now we need to expose your function again and the easiest way to do that is from Function's Platform Features page (a link #2 "API Management" from the image above we promised to return to).
Select an existing instance of APIM, "Create New API", and click on "Link API" button. It will import your functions endpoints to the APIm, and you'll be redirected to the corresponding APIM page.
Applying Inbound Policy to the API
Finally we are approaching one of the most important steps - applying inbound policy for the API that we imported from the Azure function.
To be able to successfully call a function via API Management, an inbound policy rule should insert authorization token (APIM Managed Identity) and be able to verify it using our Active Directory App.
To modify an inbound policy select the API and click on Policies "</>" link. It will open an XML Editor. Insert the lines below after the "base" policy. Replace [Azure AD Application ID] with the values we found earlier, and save.
<authentication-managed-identity resource="[Paste Azure AD Application ID]" ignore-error="false" /> <set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
Checking Azure Active Directory Integration
Let's test our integration. We can do it right in the APIM console.
Select one API endpoint and go to the Test tab. Here we can enter parameters, if they are required for your function and click Test. After that we will get a (hopefully) successful result. But if not, click on a Trace tab and ensure that you see "Managed Identity token is added to the Authorization header" message.
We are done! Or are we? Our function direct URL is secured, but we now exposed it via API Management URL, and it is publicly accessible again. The thing is, now APIM gives you flexibility to apply an authentication method of your choice, being it Subscription Keys, JWT tokens, OAuth 2.0, OpenID Connect, or integration with third-party providers like Okta or Auth0. But that is a different story and possibly a subject for another post.
One hint though: remember that policies are evaluated in order. So, for example, if you decide to authenticate your API with JWT Tokens, a "validate-jwt" policy should come before the "authentication-managed-identity" policy that we implemented here. Otherwise an "Authorization" header will be replaced before having a chance to be validated.
In this article we described a way to secure publicly accessible HTTP Azure functions with API Management Service and Azure Active Directory. Now you can relax and manage your API with style, like a cat manages his money in the picture below.
© APTYP_KOK / GETTY IMAGES
Top comments (3)
Thanks for sharing Igor, this is something that is easily missed.
The only improvement I could suggest would be using the Azure Cli instead of the portal. The cli or or PowerShell make it much easier to script and automate.
Thank you for the knowledge you have shared with us. I am curious to know about enabling AzureFunctions with third-party identity providers like google and facebook as well as our custom JWTToken based authentication/authorization. I found the way to implement either one but didn't find the way to enable both third-party identity providers as well as our custom JWTToken based verification. If we enable third-party identity providers, we are not able to call any AzureFunctions APIs like our JWTToken based call on AzureFunctions.
It seems it is possible via APIM as you mentioned it in this paragraph "So, for example, if you decide to authenticate your API with JWT Tokens, a "validate-jwt" policy should come before the "authentication-managed-identity" policy that we implemented here. Otherwise an "Authorization" header will be replaced before having a chance to be validated."
I want to make my app login with google, facebook as well as our internal JWTToken based authentication and authorizations.
Would you please guide me if it is possible in any way?
Any examples,samples, links would be highly appreciated.
"validate-jwt" policy allows only one Identity Provider, either Oauth or OIDC. So I think only one provider allowed per API.
It makes sense from the APIM point of view, as you usually use it to expose API to one client at a time.
Two way that I can see: