You might have read my previous intro post to the AAD Application Proxy, where I went over a quick intro to this service and a comparison with other reverse proxies available in the Azure portfolio. I finished that post with a very generic diagram describing how to combine multiple proxies to get different capabilities, for example using App Proxy to expose internal applications, and App Gateway or Front Door to provide Web Application Firewall inspection. Today I am going to dive deeper in this use case.
As in previous posts I am testing with a sample Python Flask app that offers some endpoints to analyze the HTTP request from the application perspective, you can find the code here.
Let’s start with something relatively easy: Azure Application Gateway is an Azure reverse proxy with optional WAF functionality that can be deployed in Azure Virtual Networks (also known as VNets).
If your application is already running on an Azure VNet, or if your application is running on-premises and you already have network connectivity to an Azure VNet over VPN or ExpressRoute, having the connector and the Azure Application Gateway in Azure might be a good choice. Here the setup I am testing here:
In essence, the users connect to the App Proxy portal, which “sends” the request to the connector in the VNet (I write “send” in quotes, because it is actually the connector that pulls the request with outbound traffic, as we discussed in the intro post).
The connector will then access a URL pointing to the Application Gateway, which will then create an HTTP connection to the actual application either in the same VNet, or on-premises via VPN or ExpressRoute. The following screenshot shows how I configured the connector to go to the App Gateway (for my test I put “appgw” as a hostname that resolves to the App GW’s private IP address in the Windows hosts file):
The Application Gateway would then do its magic, such as WAF inspection, and eventually forward the traffic to the application defined as a backend. If we have a look at our sample app, you can see the HTTP headers injected by the Application Gateway, as well as the headers injected by App Proxy with authentication information:
As you can see, headers like
Userprincipalname have been introduced by AAD App Proxy with information relative to the authenticated user. Other headers like
X-Forwarded-Port have been introduced by the App Gateway.
X-Forwarded-For header is interesting, because it contains the IP address of the original client (
184.108.40.206 in my example), as well as the IP address of the first proxy in the chain, namely the App Proxy connector (
172.16.2.4). So the client IP address information is not lost even if going through two reverse proxies here.
One aspect that you need to consider is that the Application Gateway will sit between the connector and the application, you need to take care that it doesn’t break any authentication that may happen between the connector and the app. In my case, the app doesn’t provide any auth, otherwise you need to double check this point.
Let’s make things a bit more complex, by inserting the Web Application Firewall in a different place. Another service in Azure that offers WAF functionality is Azure Front Door. Front Door doesn’t sit on a VNet, but instead it is a multi-tenant service deployed on Microsoft Points-of-Presence across the Internet. As a consequence, it is not ideal to access private applications (an exception to this is if the application is deployed on an Azure VNet, where Front Door support for Private Link is a fantastic tool).
Taking the generic case where the internal app can potentially sit on your local premises, the most intuitive design might be putting Azure Front Door in front of AAD App Proxy, in a constellation similar to this:
Note the design is now more complex, because Front Door is sitting between the user and the AAD Proxy Portal that provides authentication. For this authentication to work, the redirection URL that Azure Active Directory will provide to the user after successful authentication needs to send them back to Front Door and not to the App Proxy portal, otherwise authentication would break.
Without going into too much detail, the solution to this is using your own custom domain (in my case I bought
starkiller.info for a couple of bucks) and your own digital certificates (which I generated from Let’s Encrypt and uploaded to my Azure Key Vault using this method). This way you can define that your application is behind a custom domain name (
whoami.starkiller.info), but in your DNS you resolve this FQDN to Front Door via a CNAME record.
Let’s look first at the AAD App Proxy config, where I am using a custom domain that I previously associated to my tenant:
Note that at the very bottom of the App Proxy config page you will see a blue banner that lets you know the FQDN that you can use to get to the App Proxy portal:
Now, let’s have a look at the Front Door config. The tricky piece is sending the request to the origin to the right IP address (resolving the FQDN
whoami-starkiller.msappproxy.net), but with the hostname that AAD App Proxy expects (
whoami.starkiller.info). That is why it is necessary to overwrite the Origin Host Header as the origin configuration in Front Door shows:
And we are done! When we look at the HTTP headers that the application sees, as before we will see both the headers injected by Azure Front Door as well as the headers injected by App Proxy:
X-Forwarded-For header can be used to see the client’s original IP, in this case an IPv6 because my computer and ISP are fully dual stack.
Talking about the XFF header, you might have noticed that we now see there the IP address
220.127.116.11. This actually represents the first proxy in the chain, Azure Front Door, and it is a Microsoft-owned IP address for Front Door backend communication as you can see if you look deep into the Azure IP ranges JSON document, which will be important for the next section.
What I mean here is whether a malicious actor could bypass Azure Front Door and access App Proxy directly, to fly below the WAF radar? In theory possible, if the malicious actor gets the right HTTP Host header to configure on their request, manipulates their local DNS resolution to resolve
whoami.starkiller.info to AAD App Proxy’s public IP address, and has valid AAD credentials authorized to access the app.
A relatively easy way to prevent this from happening is by configuring some filters at the application level, by making sure that the application request actually went through Azure Front Door, by checking that the IP address in the
X-Forwarded-For is actually a valid Front Door backend address, and additionally verifying that the
X-Azure-Fdid header contains your Front Door instance ID.
For more information on this you can check the Azure Front Door FAQ on How do I lock down the access to my backend to only Azure Front Door?
We could now chain all three reverse proxies we have been talking about in this article. Why? Because we can. Or to show that the two approaches above are not mutually exclusive. If we wanted for any reason to chain Front Door + App Proxy + App Gateway, this is what it would look like:
This setup might actually make sense for example if the first proxy (Front Door in this case) provides global load balancing, the second one (AAD App Proxy) does the authentication, and the third one (App Gateway) some additional functions such as Web Application Firewalling. This design might be combined with other proxies such as Azure API Management.
As you can imagine, now everybody is putting headers in the request, but still you can see the original client IP address in the
I knew you would ask, and you might already know what my answer is going to be (“it depends”). Here some decision criteria:
I am sorry I am not giving you a black or white advice (those are rare and in my experience usually not universally applicable), but I hope you learnt something with this post. Thanks for reading!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.