Skip to content

Front Door Ban Ladder

Daniel Truong edited this page Oct 13, 2026 · 2 revisions

Front Door ban ladder

Reference for the two rules in rulesdemidocs that turn away document downloads from listed addresses. The rule set is on the demi-documents route (/demi-search/documents/*) of eagle-edge-<env>. The rules are banmanual (a person adds addresses) and banauto (DEMI's edge-ban timer adds addresses, described on the DEMI wiki page Edge-Ban-Detector). The Front Door side is in eagle-edge (azure/modules/front-door.bicep, README section "Ban ladder"). Other Front Door rules are in Front-Door-Per-App-Split.

Rule order in rulesdemidocs

Order Rule Does
1 stripauthsearch Drops Authorization
2 blockscraperua 302 to /robots.txt for the scraper user agent, stops the rule set
3 stampedgesecret Stamps X-Edge-Secret
4 banmanual Counts or blocks addresses in its list
5 banauto Counts or blocks addresses in its list
6 redirect Legacy redirect=1&inline=1 links
7, 8 rewrites 24-hex and UUID download paths onto APIM /api/documents/*

Legacy permalinks on ruleseagleapi redirect into this route first, so they pass the ladder too.

Match condition

Both rules match SocketAddr with IPMatch: the address that opened the connection to Front Door. RemoteAddress is not used because it follows X-Forwarded-For, which a client can set.

One list entry is an IPv4 /32 or an IPv6 /64. The detector refuses anything wider. Do not add a wider prefix by hand: it turns away every download behind it.

Front Door rejects a condition with no values. An empty list deploys the placeholder 192.0.2.1/32 (TEST-NET-1, never seen on a public edge).

Parameters

Parameter Default Does
edgeBanAction count count or block, for both rules. Set in main.<env>.bicepparam; test and prod set block.
edgeBanManual [] banmanual addresses, filled by deploy-infra.sh from the live rule
edgeBanAuto [] banauto addresses, filled by deploy-infra.sh from the live rule
demiIdentityPrincipalId empty Object id of demi-identity-<env>. Gets CDN Profile Contributor on the rulesdemidocs rule set only. Empty grants nobody.

demi-identity-<env> is in c4b0a8-test-rg on test and rg-demi-prod on prod:

az identity show --subscription <sub> -g <rg> -n demi-identity-<env> --query principalId -o tsv

Count and block

edgeBanAction Request from a listed address
count Passes. The rule adds X-Edge-Ban: count toward DEMI, which ignores it. The client sees nothing. The rule name shows in rulesEngineMatchNames_s in the access log.
block Answers 302 to /blocked.html on the same host and stops the rule set.

/blocked.html is a file on the eagle-public static site, served by the default route. That route does not use rulesdemidocs, and spafallback rewrites only paths with no file extension, so the redirect cannot loop. Publish the page on the site before setting block. Only /demi-search/documents/* is covered. Legacy /api/... document links redirect there first, so they are covered too.

A change takes 7 to 25 minutes to reach every edge. There is no instant rollback.

Lists are runtime state

The address lists are not in the repo. A deploy writes every rule again, lists included, so scripts/deploy-infra.sh reads the live lists with az afd rule show and passes them back as EDGE_BAN_MANUAL and EDGE_BAN_AUTO. A missing rule gives the placeholder. Any other read error stops the script before it deploys.

A timer write between that read and the deploy is lost; the next timer run writes it again. A deploy that skips the script, such as az deployment group create by hand, resets both lists to the placeholder.

Add or remove a manual ban

  1. Read the list:

    az afd rule show -g <rg> --profile-name eagle-edge-<env> --rule-set-name rulesdemidocs -n banmanual \
      --query "conditions[0].parameters.matchValues"
  2. Write it back with the address added or removed. Use an IPv4 /32 or IPv6 /64. Keep the placeholder only while the list is otherwise empty.

    az afd rule update -g <rg> --profile-name eagle-edge-<env> --rule-set-name rulesdemidocs -n banmanual \
      --set 'conditions[0].parameters.matchValues=["203.0.113.7/32"]'

The next deploy keeps the list. To clear banauto, set EDGE_BAN_MODE=off on DEMI's function app (see the DEMI wiki page Edge-Ban-Detector). banmanual is cleared only by hand, as above.

Log Analytics Reader grant

The detector reads the access log as demi-identity-<env>. scripts/bootstrap-cicd.sh step 5 assigns Log Analytics Reader on workspace eagle-logs-<env> to that identity. If the workspace or the identity does not exist, the step prints skipped and the script continues; rerun it once both exist.

Soak query

Run in eagle-logs-<env>. It lists each address a ban rule matched, per hour, with the distinct documents it asked for:

AzureDiagnostics
| where Category == "FrontDoorAccessLog" and rulesEngineMatchNames_s contains "rulesdemidocs_ban"
| extend rule = extract(@"rulesdemidocs_(ban[a-z]+)", 1, rulesEngineMatchNames_s), doc = extract(@"/documents/([^/?]+)/download", 1, requestUri_s)
| summarize requests = count(), documents = dcount(doc), statuses = make_set(httpStatusCode_s, 5) by socketIp_s, rule, bin(TimeGenerated, 1h)
| order by TimeGenerated desc, requests desc

The rollout steps for the detector are on the DEMI wiki page Edge-Ban-Detector.

Clone this wiki locally