Friday, July 28, 2023

Application Gateway and Location Header rewrites:


One of the previous articles described a problem about redirects from an application using the provided Host header which changes when an application gateway comes in between the client and the application. This article describes a fix.

When an application is reached directly, it gets the host as "context.Request.Host" and the remote IP address as "context.Connection.RemoteIpAddress" which the application uses to create redirects with Location header. With the introduction of the application gateways, these values are skewed but we get the original values in the X-Original-XX and X-Forwarded-XX headers. The app just needs to update its middleware to make use of these applications.

For example:         

public void Configure(IApplicationBuilder app)

{

   app.UseForwardedHeaders();

   app.Use(...);

}

The above works the same as setting a few options directly in various languages:

- options.ForwardedHeaders = ForwardedHeaders.All;

- options.ForwardedHostHeaderName = "X-Original-Host";

- options.ForwardLimit = 2;

The number 2 here signifies the hops between the client and the gateway and the gateway and the app service.

Another alternative is for the Application to specify the Location Uri to directly include the gateway endpoint. For example:

using System.Net;

using System.Net.Http;

using System.Threading;

using Microsoft.AspNetCore.Mvc;

using Microsoft.Extensions.Primitives;

using Newtonsoft.Json;

 

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)

{

    log.LogInformation("C# HTTP trigger function processed a request.");

    string gateway = “gwy-demo-3.centralus.cloudapp.net”;

    string name = req.Query["name"];

 

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

    dynamic data = JsonConvert.DeserializeObject(requestBody);

    name = name ?? data?.name;

 

    string responseMessage = string.IsNullOrEmpty(name)

        ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."

                : $"Hello, {name}. This HTTP triggered function executed successfully for a request that came from: {req.Headers.Host}.";

  var result = new AcceptedResult($"https://{gateway}/", responseMessage);

  return result;

}

 

Finally, there is a conditional rewrite rule that we can author on the application gateway as an alternative to fixing all the responses from all the applications that are backend pool members of the application gateway.

 

Sample deployment: gwy3proof.zip

Reference: Article on how to author rewrite rules.

 

 

 

Thursday, July 27, 2023

 Azure application gateway and overcoming app services with private links. 

The previous post described a problem. This post describes the solution to that problem in terms of self-explanatory azure command-line statements. 

rg="rg-demo-1" 

planName="asp-demo-1" 

appName="w-a-6" 

vnetName="vnet-demo-1" 

gwyName="gwy-demo-1" 

az group create -n $rg -l "CentralUS" 

az appservice plan create -g $rg -n $planName --sku P1V2 --is-linux 

az webapp list-runtimes --os-type linux 

az webapp create -g $rg -p $planName -n $appName --runtime "DOTNETCORE|6.0" 

az network vnet create -n $vnetName -g $rg --address-prefix 10.0.0.0/16 --subnet-name Default --subnet-prefix 10.0.0.0/24 

az network vnet subnet create -g $rg  --vnet-name $vnetName -n Apps --address-prefixes 10.0.1.0/24 

az network application-gateway create -g $rg -n $gwName --capacity 1 --sku Standard_v2 --vnet-name $vnetName --subnet Default --public-ip-address pip-demo-1 

echo $gwName 

az network application-gateway create -g $rg -n $gwyName --capacity 1 --sku Standard_v2 --vnet-name $vnetName --subnet Default --public-ip-address pip-demo-1 --waf-policy pol-waf-4 --priority 1001 

az network application-gateway create -g $rg -n $gwyName --capacity 1 --sku WAF_v2 --vnet-name $vnetName --subnet Default --public-ip-address pip-demo-1 --waf-policy pol-waf-4 --priority 1001 

az network application-gateway create -g $rg -n $gwyName --capacity 1 --sku WAF_v2 --vnet-name $vnetName --subnet Default --public-ip-address pip-demo-1 --waf-policy "/subscriptions/c9fe3899-495c-475e-9349-dfb252897b9e/resourceGroups/rg-temp/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/pol-waf-4" --priority 1001 

az network application-gateway address-pool update -g $rg -n appGatewayBackendPool --gateway-name $gwyName --servers "${appName}.azurewebsites.net" 

 az network application-gateway http-settings update -g $rg -n appGatewayBackendHttpSettings --gateway-name $gwyName --host-name-from-backend-pool true 

az network vnet subnet update -g $rg --vnet-name $vnetName -n Apps --disable-private-endpoint-network-policies true 

webAppId=$(az webapp show -g $rg -n $appName --query "id" --out tsv) 

echo $webAppId 

az network private-endpoint create -g $rg -n "${appName}-endpoint" --vnet-name $vnetName --subnet Apps --private-connection-resource-id $webAppId --connection-name "${appName}-connection" --group-id sites 

az network private-dns zone create -g $rg -n privatelink.azurewebsites.net 

az network private-dns link vnet create -g $rg -n "${appName}-dnslink" --registration-enabled false --virtual-network $vnetName --zone-name privatelink.azurewebsites.net 

az network private-endpoint dns-zone-group create -g $rg -n $appName --endpoint-name "${appName}-endpoint" --private-dns-zone privatelink.azurewebsites.net --zone-name privatelink.azurewebsites.net 

az network application-gateway address-pool update -g $rg -n appGatewayBackendPool --gateway-name $gwyName --servers "${appName}.azurewebsites.net" 

az network application-gateway update --name $gwyName --resource-group $rg 

az network application-gateway update --name $gwyName --resource-group $rg 

az webapp config access-restriction add -g $rg -n $appName --rule-name 'WebAppAccess' --priority 200 --action Allow --vnet-name $vnetName --subnet Default 

Caveat: Check for the successful 200 http status code when requesting the gateway url after the address pool update and subsequently when the private dns zone is created. The address pool update is important to get the gateway to refresh again so it recognizes the changes made. 

Sample deployment: gwy3proof.zip