End to End SSL with Application Gateway and Azure Web Apps (10/2017)
Fourth Update 7/2018: You no longer need to use Application Gateway to front your application to be PCI 3.0 compliant. You can enable the respective TLS version you would like your app to respond to directly in the Azure Portal for your web application.
Third Update: After some discussions it is probably not recommended to not use the *.azurewebsites.net certificate as it can be updated without prior knowledge due to security and compliance reasons. The recommend approach then would be to then use two custom domains to get this setup. So instead of using appname.azurewebsites.net address you would add a custom domain on the web app and use a valid SSL certificate for that domain on the backend pool.
Second REALLY Important UPDATE: If you are using the Azurewebsites.net certificate you will need to update it whenever it changes ie within the past couple days (12/19ish/2017) the *.azurewebsites.net certificate was updated so the any Gateway previously configured needs to be updated to the newer certificate. If you are getting a 502 error on the application gateway try adding the new *.azurewebsites.net certificate by re-downloading it.
Update: A colleague found how to integrate with Azure AD
https://blogs.msdn.microsoft.com/waws/2017/11/21/setting-up-application-gateway-with-an-app-service-that-uses-azure-active-directory-authentication/
What I cover:
- Background
- Important information that you will want to make note of where I briefly discuss details regarding how the certificates are used during the decryption and re-encryption and provide more detailed references.
- Configuring the endpoints via PowerShell
- Disabling TLS 1.0 and 1.1 via PowerShell on the Application Gateway (not the web app)
- Using Traffic Manager in this configuration (brief observations)
Background
Currently (as of 10/17) Azure web apps are not PCI 3.1 complaint as the date was pushed back until June of 2018. Azure maintains vigorous testing to maintain PCI compliance according to industry standards. The main option is to use an App Service Environment (premium version of App Services that allows disabling TLS 1.0). The other option is to use a workaround, to front your Azure Web App with an Azure Application Gateway where you can disable TLS versions and ciphers suites. To prevent access directly to the web app you can use IP Restrictions to prevent other IPs from reaching the Azure Web App directly through the web.config or via the IP Restrictions within the Azure web apps architecture.
Important information that you will want to make note of :
- When using SSL on the backend, the certificate must match the backend endpoint being hit. Since the default certificate is for the *.azurewebsites.net domain you can download this via any browser. Click on the certificate and Save As -> Base 64 cer***.
- When configuring the BackendHttpSettings and Probe, you must use the -PickHostNameFromBackendAddress so that the request is re-written with the hostname configured from step 1.
- The final point is you’ll want to point your custom domain that is accessing the application gateway at the CNAME of the public IP assigned instead of the actually IP because that IP address is dynamic. This is noted in official documentation.
- When configuring your DNS entries, use Dig Web Interface to verify what DNS entries are configured for your hostnames. You will have to make sure both custom domains you use are configured via the custom domains blade on the web app. Then you can point the domain back to the CNAME of the Application Gateway.
***
Note
The default probe gets the public key from the default SSL binding on the back-end’s IP address and compares the public key value it receives to the public key value you provide here.
If you are using host headers and Server Name Indication (SNI) on the back end, the retrieved public key might not be the intended site to which traffic flows. If you’re in doubt, visit https://127.0.0.1/ on the back-end servers to confirm which certificate is used for the default SSL binding. Use the public key from that request in this section. If you are using host-headers and SNI on HTTPS bindings and you do not receive a response and certificate from a manual browser request to https://127.0.0.1/ on the back-end servers, you must set up a default SSL binding on the them. If you do not do so, probes fail and the back end is not whitelisted.
Note
The certificate provided in this step should be the public key of the .pfx certificate present on the back end. Export the certificate (not the root certificate) installed on the back-end server in Claim, Evidence, and Reasoning (CER) format and use it in this step. This step whitelists the back end with the application gateway.
Another great blog post by one the MS Devs regarding how the certificates are used on the Application Gateway: https://blogs.msdn.microsoft.com/cloud_solution_architect/2017/05/05/end-to-end-ssl-with-azure-application-gateway/
***
Configuring the Endpoints via PowerShell
DNS Configuration
appgateway.brooksjc.com pointing at the CNAME of the Application Gateway IP
Certificate : *.brooksjc.com
PowerShell Commands
#Vnet Config
New-AzureRmResourceGroup -Name appgw-rg -Location “West US”
$gwSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name ‘appgwsubnet’ -AddressPrefix 10.0.0.0/24
$nicSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name ‘appsubnet’ -AddressPrefix 10.0.2.0/24
$vnet = New-AzureRmvirtualNetwork -Name ‘appgwvnet’ -ResourceGroupName appgw-rg -Location “West US” -AddressPrefix 10.0.0.0/16 -Subnet $gwSubnet, $nicSubnet
#App Gateway Network Configuration
$publicip = New-AzureRmPublicIpAddress -ResourceGroupName appgw-rg -Name ‘publicIP01’ -Location “West US” -AllocationMethod Dynamic
$vnet = Get-AzureRmvirtualNetwork -Name ‘appgwvnet’ -ResourceGroupName appgw-rg
$gwSubnet = Get-AzureRmVirtualNetworkSubnetConfig -Name ‘appgwsubnet’ -VirtualNetwork $vnet
$nicSubnet = Get-AzureRmVirtualNetworkSubnetConfig -Name ‘appsubnet’ -VirtualNetwork $vnet
# Define the status codes to match for the probe
$match = New-AzureRmApplicationGatewayProbeHealthResponseMatch -StatusCode 200-399
# Create a probe with the PickHostNameFromBackendHttpSettings switch for web apps
$probeconfig = New-AzureRmApplicationGatewayProbeConfig -name webappprobe -Protocol Https -Path / -Interval 30 -Timeout 120 -UnhealthyThreshold 3 -PickHostNameFromBackendHttpSettings -Match $match
#App Gateway Configuration
$gipconfig = New-AzureRmApplicationGatewayIPConfiguration -Name ‘gwconfig’ -Subnet $gwSubnet
$fipconfig = New-AzureRmApplicationGatewayFrontendIPConfig -Name ‘fip01’ -PublicIPAddress $publicip
$pool = New-AzureRmApplicationGatewayBackendAddressPool -Name ‘pool01’ -BackendIPAddresses appgateway.brooksjc.com #backendpool hostname
$fp = New-AzureRmApplicationGatewayFrontendPort -Name ‘port01’ -Port 443
$cert = New-AzureRmApplicationGatewaySSLCertificate -Name cert01 -CertificateFile C:\Users\xxx\OneDrive\Documents\DigiCert_certs\2016cert\brooksjccert\brooksjc.pfx -Password “xxxxxx”
$listener = New-AzureRmApplicationGatewayHttpListener -Name listener01 -Protocol Https -FrontendIPConfiguration $fipconfig -FrontendPort $fp -SSLCertificate $cert -HostName appgateway.brooksjc.com #hostname pointed at the application gateway
$authcert = New-AzureRmApplicationGatewayAuthenticationCertificate -Name ‘whitelistcert1’ -CertificateFile C:\Users\xxx\OneDrive\Documents\DigiCert_certs\2016cert\brooksjccert\azurewebsitesnet.cer
$poolSetting = New-AzureRmApplicationGatewayBackendHttpSettings -Name ‘setting01’ -Port 443 -Protocol Https -CookieBasedAffinity Disabled -AuthenticationCertificates $authcert -PickHostNameFromBackendAddress -Probe $probeconfig
$rule = New-AzureRmApplicationGatewayRequestRoutingRule -Name ‘rule01’ -RuleType basic -BackendHttpSettings $poolSetting -HttpListener $listener -BackendAddressPool $pool
$sku = New-AzureRmApplicationGatewaySku -Name Standard_Small -Tier Standard -Capacity 1
#Configure SSL Policy
$SSLPolicy = New-AzureRmApplicationGatewaySSLPolicy -MinProtocolVersion TLSv1_2 -CipherSuite “TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256”, “TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384”, “TLS_RSA_WITH_AES_128_GCM_SHA256” -PolicyType Custom
#Gateway Deployment
$appgw = New-AzureRmApplicationGateway -Name appgateway -SSLCertificates $cert -ResourceGroupName “appgw-rg” -Location “West US” -BackendAddressPools $pool -BackendHttpSettingsCollection $poolSetting -FrontendIpConfigurations $fipconfig -GatewayIpConfigurations $gipconfig -FrontendPorts $fp -HttpListeners $listener -RequestRoutingRules $rule -Sku $sku -SSLPolicy $SSLPolicy -AuthenticationCertificates $authcert -Verbose -Probes $probeconfig
References: https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-end-to-end-ssl-powershell
https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-web-app-powershell
Disabling TLS 1.0 and 1.1 to be PCI compliant prior to Azure Web Apps becoming PCI 3.1 Compliant
A lot of customers are hoping to disable TLS 1.0 and 1.1 to meet their PCI Compliance requirements. You can do so using the last line seen above ($SSLPolicy). See the images below from SSlLabs showing the configuration prior to disabling TLS 1.0 and 1.1.
Initial Configuration of the Application Gateway endpoint without any TLS versions disabled
Configuration after TLS 1.0 and 1.1 were disabled on the Application Gateway
Using Traffic Manager with this configuration
When trying to use traffic manager with application gateway you must use a Basic Listener, not a Multi-Site Listener, as the Traffic Manager probe has to be able to reach the web app to be healthy. In the configuration above I used a Multi-Site Listener to get configuration working so the Application Gateway could listen for multiple hostnames. In the documentation below they use path based routing for the rule as I understand that the basic rule will take everything coming in on one port and forward it to the Backend Pool.
I’m writing this at 5AM in the morning so please excuse any grammatical errors 😀
Thanks for the write up.
I am trying to deploy a multi region webapp with 2 x appgw behind a TM with end-end SSL. using custom domains. running into a few issues where the traffic is overwritten with the web apps url instead of the custom domain. do you have more to share about your Tm config?
Shanib, unfortunately I haven’t had much time to do much more regarding this configuration. It may be worth creating a support case with support to investigate this configuration further if you have not already done this.
Hi Jeremy,
I followed your steps above to achieve PCI compliance on my web apps. I am getting the following error:
502 – Web server received an invalid response while acting as a gateway or proxy server.
I haven’t enforced the SSL policy yet. I have followed multiple tutorials and guides for last week but was not able to get end-to-end encryption work. I was hopeful when I landed on your blog but obviously I am doing something wrong.
Any suggestions on how to debug the issue?
What’s the backend health status? If its unhealthy that will cause a 502.
The backend health status is unhealthy. My web app has the custom domain and SNI based SSL binding. Do I have to use custom SSL public key in the following step instead of azurewebsitesnet.cer?
$authcert = New-AzureRmApplicationGatewayAuthenticationCertificate -Name ‘whitelistcert1’ -CertificateFile C:\Users\xxx\OneDrive\Documents\DigiCert_certs\2016cert\brooksjccert\azurewebsitesnet.cer
That all depends on how you are setting it up. If you are trying to configure anything through the portal it will not work. Everything needs to be done via PowerShell, even any modifications should just be done via PS so that could possibly be your issue.
Regarding your questions about using the SSL public key. The certificate you configure on the authcert section will be the *.azurewebsites.net certificate. If you are stuck I’d recommend filing a support case to investigate further.
The backend health status is unhealthy. My web app has the custom domain and SNI based SSL binding. Do I have to use custom SSL public key in the following step instead of azurewebsitesnet.cer?
$authcert = New-AzureRmApplicationGatewayAuthenticationCertificate -Name ‘whitelistcert1’ -CertificateFile C:\Users\xxx\OneDrive\Documents\DigiCert_certs\2016cert\brooksjccert\azurewebsitesnet.cer
Great article, and tip about not using the stock certificate for Azure. I have a setup working with end to end ssl. What I don’t have working is ARRAffinity. A 3rd party component requires it. When going thru the gateway, it changes the ARRAffinity cookie to a response cookie with a set-cookie(fiddler). Not sure really what that means. What I do know my app works when going directly to webapp and does not when going thru gateway(this component only) and the only diff is what happens on the cookie. Thoughts?