Allow HTTP_X_FORWARDED_FOR at server level instead of declaring it per-application

The stage 5 smoke test failure was a locked config section, but not one of the
two the installer unlocks. A diagnostic collected from the server returned:

  HTTP 500.52 - URL Rewrite Module Error
  Module RewriteModule, Handler httpplatformhandler
  Error Code 0x80070021
  Config Error: This configuration section cannot be used at this path.
  Config File: \\?\C:\shopdb-flask\web.config

handlers and httpPlatform were both overrideMode Allow and locked false, so
the unlock had worked. The section at fault was a third one,
system.webServer/rewrite/allowedServerVariables, which ships
overrideModeDefault="Deny". web.config declared <allowedServerVariables>
locally for the X-Forwarded-For rule, and IIS rejects that declaration
outright, failing the entire configuration before httpPlatformHandler ran.
python was therefore never launched and C:\shopdb-flask\logs stayed empty,
which reads as a dead application or a permissions fault and is neither.

Unlocking the section would let every site on the machine declare arbitrary
server variables. The installer now adds the single variable to the
server-level allow list, checking first because a duplicate add is an error,
and web.config no longer declares it. The rewrite rule is unchanged.

Verified by applying the installer's own uncommenting to the template and
parsing the result: one rewrite element, no allowedServerVariables, the rule
still setting HTTP_X_FORWARDED_FOR from REMOTE_ADDR.

shopdb-diagnose.py checked only the two sections the installer unlocks, so it
could not have named this one; the IIS error page did. It now reports the
lock state of the rewrite sections as well.
This commit is contained in:
cproudlock
2026-08-04 20:04:19 -04:00
parent 10ee3a3c58
commit 95b0b77c13
3 changed files with 61 additions and 6 deletions

View File

@@ -1915,6 +1915,39 @@ If not, re-run matching how the application is published today: pass
} finally { $ErrorActionPreference = $prevEap }
}
# URL Rewrite: a rule may only set a server variable that is ALLOWED, and
# system.webServer/rewrite/allowedServerVariables ships with
# overrideModeDefault="Deny". An <allowedServerVariables> block in the app's
# own web.config is therefore refused outright - IIS answered 500.52 with
# 0x80070021, "this configuration section cannot be used at this path",
# before it ever reached httpPlatformHandler. python was never launched, the
# stdout log stayed empty, and the stage 5 smoke test reported it as a dead
# application when the config had been rejected wholesale.
#
# Allow the ONE variable at server level rather than unlocking the section.
# Unlocking would let every site on this machine declare arbitrary server
# variables; this grants exactly HTTP_X_FORWARDED_FOR.
if (($ClientIpSource -eq 'direct') -and (-not $WhatIfOnly)) {
$varName = 'HTTP_X_FORWARDED_FOR'
$section = 'system.webServer/rewrite/allowedServerVariables'
$prevEap = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
# Adding a duplicate entry is an error, so look before writing.
$current = (& $appcmd list config /section:$section 2>&1 | Out-String)
if ($current -match [regex]::Escape($varName)) {
Write-Log "$varName is already allowed for URL Rewrite" 'OK'
} else {
Write-Log "allowing $varName for URL Rewrite (server level)"
& $appcmd set config /section:$section "/+[name='$varName']" /commit:apphost 2>&1 |
ForEach-Object { Write-Log " $_" }
if ($LASTEXITCODE -ne 0) {
Write-Log " could not allow $varName - IIS will return 500.52 and the app will not start" 'WARN'
}
}
} finally { $ErrorActionPreference = $prevEap }
}
# Stage 2 stops the pool on an upgrade so its files can be replaced. Nothing
# else starts it again, so the smoke test would fail against a stopped site
# and report it as a broken install.