Janne Mattila

From programmer to programmer -- Programming just for the fun of it

Azure Web Application Firewall on Application Gateway for Containers

Posted on: August 9, 2025

Public Preview for Web Application Firewall on Application Gateway for Containers has been a long waited feature. I had to immediately take it for a spin when public preview started.


If you’re a new to Application Gateway for Containers, then I recommend that you read about its components and official documentation about Azure Web Application Firewall on Application Gateway for Containers, before reading this post.


I decided to update my AKS Workshop materials to include Web Application Firewall. It’s the most comprehensive demo material that I’ve built for AKS, so it’s quite natural place for me to add this. You can find the full workshop material from GitHub:

Application Gateway for Containers related snippets are in 08-agc.sh file:

First part of the script is about setting up Application Gateway for Containers:

The above is fairly straight forward process.

If you happen to have Azure Policy enabled, you might get the following error:
Error creating: admission webhook “validation.gatekeeper.sh” denied the request: [azurepolicy-k8sazurev3noprivilegeescalatio-621db1c4d893abfa0dcb]
Privilege escalation container is not allowed: cleanup
You need to either disable Azure Policy or modify the policy configurations to allow this installation to proceed.

Second part of the script is about configuring Application Gateway for Containers.

1) Create Application Gateway for Containers:

apiVersion: alb.networking.azure.io/v1
kind: ApplicationLoadBalancer
metadata:
  name: alb-demo
  namespace: alb-ns
spec:
  associations:
    - $vnet_spoke2_agc_subnet_id

The above takes a few minutes while it creates this resource:

2) Create Gateway API resource with azure-alb-external gateway class that was created during the installation:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: app-gateway
  namespace: alb-ns
  annotations:
    alb.networking.azure.io/alb-namespace: alb-ns
    alb.networking.azure.io/alb-name: alb-demo
spec:
  gatewayClassName: azure-alb-external
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:

3) Create Service to expose my previously deployed network-app:

apiVersion: v1
kind: Service
metadata:
  name: network-app-svc
  namespace: network-app
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: network-app

4) Create HTTP Route to link Gateway to the Service:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: network-app-route
  namespace: network-app
spec:
  parentRefs:
    - kind: Gateway
      name: app-gateway
      namespace: alb-ns
  rules:
    - matches:
        - path:
            value: /
      backendRefs:
        - name: network-app-svc
          port: 80

5) Create WAF Policy using Bicep template:

param location string = resourceGroup().location

resource firewallPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01' = {
  name: 'waf-policy'
  location: location
  properties: {
    customRules: [
      {
        name: 'FinlandRateLimit'
        priority: 10
        ruleType: 'RateLimitRule'
        action: 'Block'
        matchConditions: [
          {
            matchVariables: [
              {
                variableName: 'RemoteAddr'
              }
            ]
            operator: 'GeoMatch'
            negationConditon: false
            matchValues: ['FI']
          }
        ]
        rateLimitThreshold: 4000
        rateLimitDuration: 'OneMin'
        groupByUserSession: [
          {
            groupByVariables: [
              {
                variableName: 'ClientAddr'
              }
            ]
          }
        ]
      }
      // Custom rules are abbreviated
    ]
    policySettings: {
      requestBodyCheck: true
      maxRequestBodySizeInKb: 128
      fileUploadLimitInMb: 100
      state: 'Enabled'
      mode: 'Prevention'
    }
    managedRules: {
      managedRuleSets: [
        {
          ruleSetType: 'Microsoft_DefaultRuleSet'
          ruleSetVersion: '2.1'
        }
        {
          ruleSetType: 'Microsoft_BotManagerRuleSet'
          ruleSetVersion: '1.1'
        }
      ]
      exclusions: []
    }
  }
}

output wafPolicyId string = firewallPolicy.id

Deploy WAF Policy Bicep file:

aks_agc_waf_policy_id=$(az deployment group create \
 --resource-group $resource_group_name \
 --template-file others/agc/waf-policy.bicep \
 --query "properties.outputs.wafPolicyId.value" \
 --output tsv)

Here is the deployed WAF Policy:

Here are the configured Managed rules:

Here are the deployed Custom rules:

6) Associate WAF Policy with Gateway:

apiVersion: alb.networking.azure.io/v1
kind: WebApplicationFirewallPolicy
metadata:
  name: waf-policy
  namespace: alb-ns
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: app-gateway
    namespace: alb-ns
  webApplicationFirewall:
    id: $aks_agc_waf_policy_id

This association link can be seen in Application Gateway for Containers in securityPolicies:

The above association links to this resource that can be seen when you enable Show hidden types in the resource group:

It contains a link to the actual WAF Policy:

7) Finally, after all the above configurations, we’re ready to test our WAF Policy:

$ curl $aks_agc_gateway_address
Hello there!

$ curl -X POST --data 'INFO HOSTNAME' "$aks_agc_gateway_address/api/commands"
-> Start: INFO HOSTNAME
HOSTNAME: network-app-deployment-6956bdf4fc-8qnx2
<- End: INFO HOSTNAME 4.777ms

$ curl -X POST --data '--; DROP TABLE Logs' "$aks_agc_gateway_address/api/commands" --verbose
Access Forbidden

$ curl -X POST --data 'alert(document.cookie);' "$aks_agc_gateway_address/api/commands" --verbose
Access Forbidden

As you can see, the last two requests were blocked by the WAF policy since they contained potentially malicious payloads. Similarly, if you would make many requests in a short period of time, you would be rate-limited according to the deployed Custom rules.

Luckily, I had Azure Policy based diagnostics already enabled:

Since I had diagnostic setting configured for logging, I can view the WAF logs from the centralized Log Analytics workspace:

As mentioned, full example of the above can be found from GitHub so you can easily test this scenario yourself:

I hope you find this useful!