Cloud can be tricky sometimes. Find out what scenarios we've ran into that are worth being mentioned and explained.
If you ever needed (or need) to configure end-to-end SSL between Azure App Gateway and Azure API Management set on internal mode, which gets the APIs from Azure WebApps which are VNET integrated in the VNET of the Azure API Management, you might have found that the official documentation from Microsoft is incomplete (at the time of writing this article). By following the steps from the official documentation you will face a lot of roadblocks around the way. This article is meant to share with you the correct (and apparently the only) way on how to achieve this. If you want to serve requests on HTTPS between all services involved, this is the way to do it. (Again, underlining - at the time of writing this article).
So, drawing the picture - the thing that we want to achieve is this:
Keep in mind that an Azure API Management and an Azure Application Gateway require dedicated subnets in a VNET. But I am presuming you already know this.
I will presume that you already have created:
- The WebApps (serving as APIs)
- The APIM (set on internal) and also configured it to point (fetch) from the APIs (the WebApps)
- the Application Gateway
If not, please make sure that you have all of these created before continuing.
Now we'll continue with the part where we will correctly create a connection between the Azure Application Gateway and the Azure APIM.
The fist step is to create a self-signed certificate. I know, it sounds a bit odd, but bear with me. Of course, you can have a "real" certificate, signed by a CA. The idea is to just have a certificate for a domain that you will use as internal, because we we'll need to add a custom domain to the APIM which will respond to a such hostname. So, no matter the way you choose to do it, get a certificate for something like "apim.yourcompany.internal".
ONE BIG IMPORTANT NOTE HERE: if you use AppGW v2, this is enough. If you use AppGW v1 for this demo, make sure to first create a Root Certificate that will sign a second certificate which will address "apim.yourcompany.internal".
This hostname will be used to map the APIM to the AppGW. But, in order to do that in internal mode, we need a DNS that will help us resolve it. At the moment, as per official documentation from Microsoft, the only way to do this is to create a VM inside the VNET and setup a DNS server on it. This DNS server has to resolve that hostname that you created a certificate for.
Adding an "A" record for "apim.yourcompany.internal" pointing to the internal IP of the Azure APIM should do the trick.
Good. Now that we have the certificate and we have a valid hostname mapped internally on the internal APIM, we can actually proceed with the next step.
Continue by setting up a Custom DNS for the APIM. This is pretty simple to do - just go on the "Custom Domains" blade of the APIM and add a custom domain there (that "apim.yourcompany.internal" address). The one which I am particularly interested at the moment is to add a custom domain for the Gateway. Make sure to also add for this custom domain the certificate that you created.
After this is done, we can proceed by creating the Azure AppGW. This step is easy and I am sure you know how to do it. The actual configuration is a bit tricky and this I am going to explain.
After creating the AppGW, make sure you configure the AppGW the following way:
- Create a backend pool which will have target the internal hostname of the APIM (apim.yourcompany.internal)
- Have an HTTP setting set on (HTTPS) and set as Trusted Root Certificate the certificate that we created earlier. Also, make sure to have a custom probe for this HTTP setting. The probe should point to the following path: "/status-0123456789abcdef" - this is the path in which the APIM service in Azure responds with a 200 status code.
- Create a listener on HTTPS and add to it a certificate that you will use for your public addresses/hostnames. Also, set the listener type to be on "multiple sites" and add as address something like "apim.yourcompany.com". The certificate that I mentioned you should be adding here, in this case, should be a wildcard certificate resolving "*.yourcompany.com".
- Associate a rule to the listener to use the HTTP settings you created earlier to point to the backend pool you created earlier. Type can be "Basic" or "Path-based", depending on your app configuration.
After these have been configured, if you access the backend health of the AppGW, you should see that the backend response with a 200 status code.
The only thing that you need to do more is to setup the DNS so that the AppGW will respond to that "apim.yourcompany.com" address that you've set up for the listener. This can be easily configured by just accessing the DNS settings of your domain (yourcompany.com) and pointing "apim.yourcompany.com" to the publicIP of the AppGW (a CNAME should do the trick).
Now, if you access "apim.yourcompany.com", you will see that everything is being delivered in a secure way, all traffic flow going through the AppGW and taking information from the "internal" out to "external", all through HTTPS.
Now, to further give you some explanations on why I did it in this manner, as you are probably confused about some stuff:
1) I am using a Custom DNS on an internal VM because this is the only way AppGW will recognize hostnames for APIM. Azure Private DNS Zones or Azure Private Links do not support these services at the moment, and the only way to do it is with a Custom VM DNS.
2) If you set up your APIM on internal and expect the AppGW to respond "by default" to the Azure hostnames of the APIM (those ".azure-api.net" addresses), your expectations are wrong. Those were also my expectations. Apparently Azure default DNS does not know to map their own services internally.
3) You might be thinking that "Ok, but why do I need a custom internal domain and a certificate for it? I can just map in the Custom VM DNS that "azure-api.net" address to be resolved internally" - and you are completely right. You can do that and from a networking point of view that will work just fine. Only problem here is that when you setup the HTTP setting in the AppGw, that service does not know to resolve HTTPS for that default APIM hostname. If you are hoping that the "Use for App Service" or "Use Well Known CA Certificate" might help you, then you will be disappointed. Azure AppGW does not recognize internal certificates that Azure APIM uses. So, these will not help you: