Azure-docs: Making backend API (Azure Functions) accessible ONLY via API Management Gateway

Created on 6 Mar 2019  Â·  28Comments  Â·  Source: MicrosoftDocs/azure-docs

APIMG provides very nice security layer with policies for OAuth2; for offloading auth; e.g.: https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies

However the backend Azure Functions would still stay exposed; if somebody calls them directly.

How could make azure functions accessible only via API Management Gateway?

A similar question was asked on https://stackoverflow.com/questions/36764654/make-back-end-apis-only-accessible-via-azure-api-managment

So far the only solution seems restricting ip's calling azure funtions.
But IP's change; here is a recent example from APIMG: https://blogs.msdn.microsoft.com/apimanagement/2018/05/31/potential-change-to-api-management-ip-addresses/

using subscription key (as was suggested in related issue #6928 )
seems a lot less secure.


Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Pri1 api-managemensvc cxp product-question triaged

Most helpful comment

@krispenner thank you for your work on this. Where did you end up on the protection on the App Service side? (other than IP restrictions).

@PramodValavala-MSFT if security is a core value of APIM, a full documented solution of APIM->App Service is a gap in the documentation.

All 28 comments

@neil-rubens Thank you for your interest in Azure products and services. We are investigating and will get back to you soon.

@neil-rubens Both, IP restrictions and Function keys, are indeed ways of tackling this problem.

Another option would be to setup Authentication/Authorization for Functions and Managed Identity for APIM which would be more secure.

@PramodValavala-MSFT thank you for the answer; and mentioning other options that I wasn't aware of.

I think it might be useful to add it to the docs; since it may be the next natural question that many users might face.

p.s. in my case, I am planning to go with the ip restrictions; since I want APIM to be the single place where auth is done.

@neil-rubens Glad we could help!

Do note that with IP Restrictions, some functionality in the portal is not accessible anymore.
The workaround for that would be to temporarily add your public IP for management. You could also remove the restriction temporarily but the former is more secure.

As for a doc explaining how functions (or even any app service) can be secured behind APIM, I believe its best placed in the Azure Functions / App Service Auth docs itself.
Could you open an issue for this there for it to be addressed properly?

We will now proceed to close this thread. If there are further questions regarding this matter, please tag me in your reply. We will gladly continue the discussion and we will reopen the issue.

@PramodValavala-MSFT are there docs that show managed identity for APIM connecting to an App Service (in my case, Linux containers)? Much like @neil-rubens I want APIM to be our security layer and lock down the Azure App Service. Also, if I go with IP Restrictions, how do we know the IP to allow from APIM? Many thanks.

@JonKragh Sorry for not getting back sooner. While APIM has Managed Identity Support, it is currently limited to fetching certificates from Azure Key Vault.

Other scenarios will be supported in the future but no timeline exists as such.

@PramodValavala-MSFT thank you for the reply. What is the preferred method to lock down an App Service API (a container in my case) so that access must go through APIM?

@JonKragh You could just setup IP restrictions on the app service with the APIM instance's IP since its constant.

I have used IP Restrcition in app service and given APIM ip address in that. Although App service are no longer accessible but when I import api app in APIM using Open api swagger url , it gives error that url is not accessible as it is not available publically.

@NehaOberoi92 That is expected since the APIM Portal UI makes a request to fetch the OpenAPI Spec from the browser. For that to work, you would have to add your IP (the public IP of the computer you are using) to the IP restrictions of the App Service for it to work.

Actually I was using a powershell script from octopus server, I think in
that case I need to add public ip of that server in which powershell script
is running

On Wed, 24 Jul 2019, 10:54 am Pramod Valavala, notifications@github.com
wrote:

@NehaOberoi92 https://github.com/NehaOberoi92 That is expected since
the APIM Portal UI makes a request to fetch the OpenAPI Spec from the
browser. For that to work, you would have to add your IP (the public IP of
the computer you are using) to the IP restrictions of the App Service for
it to work.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/MicrosoftDocs/azure-docs/issues/26312?email_source=notifications&email_token=ADSXFCNATUEORYPNQUG3PMTQA7RPXA5CNFSM4G37YVJKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2VGQSA#issuecomment-514484296,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADSXFCJK5NYQ2MFRSZDKOO3QA7RPXANCNFSM4G37YVJA
.

@PramodValavala-MSFT : I tried the same, gave public ip of my computer, I am able to access swagger url on chrome, but when I try on azure portal it is still showing that url is not publically accessible.

@NehaOberoi92 If you are still experiencing this issue you can try removing all IP restrictions on the app service, enable 'Web server logging' on the app service and open the 'Log stream' page to the 'Web Server logs' tab to view requests as they come in. Next, make your request how you want and it should succeed and find it in the logs, note the IP address. Finally add back your IP restrictions on the app service including the noted IP address from the request log.

I'm guessing this is some APIM SCM server that was tied to my APIM instance. It was not my APIM IP address nor my computer's public IP address. Once added it was the same in every other request to all my other backend API app services I imported swagger files from for the one APIM using powershell.

@PramodValavala-MSFT Can you confirm if the IP address assigned to the APIM instance in dedicated to only that APIM instance and is in no way shared with any other Azure APIM instance or other Azure resources? If it is in anyway virtual or shared then it's not a very secure choice.

Are there any options yet to use APIM managed identity directly against an App Service if I enable Azure AD authentication on the App Service and include the bearer token in the header somehow using policies?

Can this be applied to backend API requests?
https://docs.microsoft.com/en-us/azure/api-management/api-management-authentication-policies#ManagedIdentity

In case anyone comes here looking, I was able to get this working using the below policy on the APIM where the resource is the Application (Client) ID of the Azure AD Registered Application assigned to the App Service's Azure AD Sign-in.

<policies>
    <inbound>
        <authentication-managed-identity resource="12345678-abcd-0011-9081-d417e00cc82f" />
    </inbound>
    <backend>
        <forward-request timeout="600" />
    </backend>
    <outbound>
        <set-header name="Server" exists-action="delete" />
        <set-header name="X-Powered-By" exists-action="delete" />
        <redirect-content-urls />
    </outbound>
    <on-error />
</policies>

Inbound trace:

authentication-managed-identity (0.263 ms)
{
    "message": "Obtaining managed identity token using clientId:a0a0a0ab-9900-5500-1122-454b2bbda1f3 AAD Authority:https://login.windows.net/111111-1010-2020-3030-a8ada8a7bef8 for 12345678-abcd-0011-9081-d417e00cc82f audience succeeded.",
    "errorResponse": null
}
authentication-managed-identity (0.011 ms)
{
    "message": "Managed identity token is added to Authorization header."
}

Backend trace:

{
    "name": "Authorization",
    "value": "Bearer abcd1234...."
},

@krispenner : Thanks it is working now, do you have any idea how securing backend api will work in consumption apim plan as it does not have public ip address nor support AD.

@nehaob26 sorry, I have no idea, I have never used the consumption APIM plan.

Apologies for not getting back sooner.

@krispenner Thanks for helping out! You are right about the request being made via a proxy and not by the browser directly. My bad. I seem to have confused the behavior with another service.

Except consumption, all tiers have a static IP with a few exceptions as documented here.

Also, your managed identity policy is spot on.

@JonKragh I believe you were looking for the same as well. And @NehaOberoi92, you should still be able to use Managed Identity on the Consumption Tier.

What consumption tier doesn't support is Azure AD (and Azure AD B2C) as an identity provider for user sign in on the developer portal (which itself is not available on this tier).

@PramodValavala-MSFT : Hi , Thanks for your input, I was going through the documentation and it seems to secure my backend api in consumption plan I need to use client certificate authentication https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-mutual-certificates-for-clients
Trying to implement that.

@nehaob26 Yes. Its actually this doc that you would need to refer to for authentication between APIM and your backend.

FYI, the static IP address of the APIM is exclusive to only the one APIM instance. I had this confirmed with the APIM team yesterday and they updated their documentation to clearly state that, shown below. The reason I had asked is that a static IP address in the cloud doesn't explicitly state it's not shared with other resources, only that it won't change. So for backend access whitelisting purposes, it is good to know that no other Azure resource (eg. other APIM instances) could share the IP address and gain access.

_Every API Management service instance in Developer, Basic, Standard, or Premium tier has public IP addresses, which are exclusive only to that service instance (they are not shared with other resources)._

https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-ip-addresses

@krispenner thank you for your work on this. Where did you end up on the protection on the App Service side? (other than IP restrictions).

@PramodValavala-MSFT if security is a core value of APIM, a full documented solution of APIM->App Service is a gap in the documentation.

@JonKragh I was using IP restrictions, which if you are able to use is probably one of the easiest ways to secure a backend.

However, I prefer to use Azure AD authentication so I am moving to that. Then I can manage additional users via Azure AD group memberships that need access to the backend APIs directly. I also don't need to bother with any scripts to update IP restrictions across our 30+ backend API app services when a new IP needs access.

The APIM inbound policy <authentication-managed-identity resource="AzureAppServiceAdAppId" /> is working great, it only takes a millisecond or so of overhead to get the token and the backend app services are configured now to only allow Azure AD authorized users to make requests, of which the managed identity of the APIM instance is one.

I was importing my swagger definition from the backend URL via an Azure PowerShell script (Import-AzApiManagementApi) after each deployment. However, this will no longer work due to authentication requirements, as the Swagger/OpenAPI spec would need to be accessed anonymously from the Azure REST API proxy from my understanding.

As I'm using ASP.NET Core and Swashbuckle, I'm currently investigating using the Swagger CLI tool to generate my Swagger/OpenAPI spec during my CI process and then pass it in on the -SpecificationPath PowerShell parameter rather than use the -SpecificationUrl parameter. Regardless of authentication, I think generating the spec during the build and uploading it during deployment is a much cleaner approach anyways. So I'm hoping to get this working next.

I may still keep the IP restrictions on as well, you can never too be secure. But I may remove them if they become problematic.

@PramodValavala-MSFT : Can you guide me if I can use Oauth in apim consumption plan ? I am trying this document - https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad but not able to get it working in consumption plan.

@JonKragh Thanks for the feedback! Feel free to open another issue for tracking this.

@NehaOberoi92 Please feel free to open a thread on MSDN / Stack Overflow sharing details on what you have tried and errors if any for better support. This repo is essentially to tackle documentation related issues and feedback.

@neil-rubens
For me, I have created a good security solution to this
I have added a secret to Azure key vault and named it {your secret name}
and then added a policy to the APIM to insert a header with the secret into the request

 <inbound>
                <send-request mode="new" response-variable-name="varResponse" timeout="20" ignore-error="false">
                    <set-url>https://{your vault name}.vault.azure.net/secrets/{your secret name}?api-version=7.0</set-url>
                    <set-method>GET</set-method>
                    <authentication-managed-identity resource="https://vault.azure.net" />
                </send-request>
                <!-- Store response body in context variable as a string -->
                <set-variable name="varKey" value="@((string)((IResponse)context.Variables["varResponse"]).Body.As<JObject>()["value"])" />
        <!-- set the key in the header -->
        <set-header name="{your header name}" exists-action="override">
            <value>@((string)context.Variables["varKey"])</value>
        </set-header>
</inbound>

so any request coming to Azure APIM, it will add this header to it and in your Azure function you must allow only requests coming with that header, and you should also read the same secret from your Azure function to compare with the header value.
Here is the policy version with caching


 <inbound>
        <!--Look for secret in the cache -->
        <cache-lookup-value key="cachedVarKey" variable-name="varKey" />
        <!-- If API Management doesn’t find it in the cache, fetch it from Key Vault -->
        <choose>
            <when condition="@(!context.Variables.ContainsKey("varKey"))">
                <!-- Retrieve secret from Key Vault -->
                <send-request mode="new" response-variable-name="varKeyResponse" timeout="20" ignore-error="false">
                    <set-url>https://{your vault name}.vault.azure.net/secrets/{your secret name}?api-version=7.0</set-url>
                    <set-method>GET</set-method>
                    <authentication-managed-identity resource="https://vault.azure.net" />
                </send-request>
                <!-- Store response body in context variable as a string -->
                <set-variable name="varKey" value="@((string)((IResponse)context.Variables["varKeyResponse"]).Body.As<JObject>()["value"])" />
                <!-- Store result in cache -->
                <cache-store-value key="cachedVarKey" value="@((string)context.Variables["varKey"])" duration="3600" />
            </when>
        </choose>
        <!-- set the key in the header -->
        <set-header name="{your header name}" exists-action="override">
            <value>@((string)context.Variables["varKey"])</value>
        </set-header>
</inbound>

@malballah that's a creative solution! How does it work with regards to overhead and latency for each API call; do you experience slower responses?

@haugan
It works very fine remember I have added a version with caching, so both APIM and the Azure function will be having the secret from the vault once they check the first time

Was this page helpful?
0 / 5 - 0 ratings