Skip to content

WAF and Custom Domain

This page is for the platform team. It describes how to put the portal behind an Application Gateway with a Web Application Firewall (WAF) and how to serve it under a custom domain. It is a reference design: the settings below are what works for the portal, but your gateway module, names and priorities will differ. The WAF is optional and is arranged before the deployment starts (Prerequisites).

flowchart LR
    user[User] -->|HTTPS, custom domain| agw[Application Gateway<br/>WAF policy, HTTPS listener]
    agw -->|HTTPS, rewrite headers| app[Portal web app<br/>private endpoint]
    agw -.->|probe| h[/.backstage/health/v1/liveness/]

Custom domain

  1. Entra app registration. Add the redirect URI https://<portal-domain>/api/auth/microsoft/handler/frame to the app registration of the environment.
  2. Terraform. Set the domain_name variable of the backstage module in the environment root. The module then sets the app settings APP_CONFIG_app_baseUrl and APP_CONFIG_backend_baseUrl, so the backend knows the URL that users use.
module "backstage" {
  source      = "../../modules/backstage"
  # ...
  domain_name = "portal.contoso.example"
}
  1. DNS and certificate. The customer provides the DNS record that points to the gateway (or web app) and a TLS certificate. See Prerequisites.

WAF policy

Use one WAF policy per environment, attached to the HTTPS listener of the portal. Microsoft's Default Rule Set 2.1 (DRS 2.1) works best with the portal when you also apply Microsoft's fine-tuning guidance for it.

Setting Value
Mode Prevention
Rule set Microsoft_DefaultRuleSet 2.1
Request body check Off in the example (maximum body size 128 KB, file upload limit 100 MB)
Log scrubbing On

Rule overrides in the example policy:

Rule group Rule Setting Reason
SQLI 942110, 942150, 942260, 942430, 942440 Disabled Microsoft's fine-tuning guidance for DRS 2.1
LFI 930120 Enabled, action Log The portal can generate requests that trigger false positives
PROTOCOL-ENFORCEMENT 920440 Enabled, action Log Same

Tune in Log mode first

If you see blocked requests that you cannot explain, switch the rule to Log, find the request in the WAF logs and tune from there.

Application Gateway

Create the following elements for the portal. The names are examples.

Element Configuration
Backend pool FQDN of the web app, <app-service-name>.azurewebsites.net
Backend setting HTTPS on port 443, host name set to the web app FQDN, request timeout 30 seconds, cookie-based affinity disabled
HTTPS listener Host name <portal-domain>, SNI required, certificate from Key Vault, the WAF policy attached
HTTP listener Redirects to the HTTPS listener (permanent, keep path and query), so a URL pasted without protocol works
Routing rules One for the HTTPS listener (backend pool, backend setting, rewrite rule set), one for the HTTP listener (redirect)
Rewrite rule set Adds request headers X-Forwarded-Host with {var_host} and X-Original-URL with {var_uri_path}
Health probe HTTPS, host set to the web app FQDN, interval 30 s, timeout 30 s, unhealthy threshold 3, expects status 200

Health probe endpoint

The portal has two health endpoints.

Endpoint Behaviour
/.backstage/health/v1/liveness Returns 200 while the process is up
/.backstage/health/v1/readiness Also returns 503 before the backend has finished starting, while it shuts down, and while the Microsoft auth provider credential fails validation

The example probe uses liveness. With it, the portal stays reachable while a failing credential is being fixed. Use readiness if you want the gateway to take an instance out of the pool during start-up, shutdown or a credential failure.

Web app access

Decide whether the web app is reachable only through the gateway. The backstage module has a setting for public network access with three values: none, full (the default) and a restricted value intended for the Azure AI agent service. See Architecture. If you use the gateway as the only entry, restrict direct access to the web app.