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
What I cover:
- 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)
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.
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.
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
appgateway.brooksjc.com pointing at the CNAME of the Application Gateway IP
Certificate : *.brooksjc.com
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
$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
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 😀