diff --git a/cms-server-port-leak-fix.md b/cms-server-port-leak-fix.md new file mode 100644 index 0000000..28d29e2 --- /dev/null +++ b/cms-server-port-leak-fix.md @@ -0,0 +1,174 @@ +--- +title: CMS port-leak fix — URL Rewrite serverVariables на host IIS +type: concept +tags: [iis, url-rewrite, traefik, x-forwarded, asp-net, gotcha] +sources: [../sources/iis-host-migration-2026-05-19.md] +updated: 2026-05-19 +--- + +# CMS port-leak fix + +Решение для open issue #1 из [[recovery-architecture-snapshot]] (раньше «X-Forwarded headers не настроены, :4443 leak») и админ-utечки `:8089` обнаруженной 2026-05-19 вечером после attempt 2 host-IIS миграции. Документирует **root cause**, **почему VM работала**, **почему обходной path-B на Windows Docker Desktop невозможен**, и **что в итоге применено**. + +Связано: [[traefik-on-windows-docker-desktop]] Pitfall 5, [[iis-host-migration-2026-05-19]] Phase 10, [[docker-host-loopback-detect]]. + +## Симптом + +Admin URLs формата (после attempt 2 миграции на host-IIS:8089): +- `https://emspb.snolla.com:8089/admin/assets//getList?path=` +- `https://emspb.snolla.com:8089/admin/themes/getImageSizes/` +- `https://emspb.snolla.com:8089/admin/templates/editors.tmpl.html?v=2.006` + +Browser HSTS upgrade'ит `http://...:8089` → `https://...:8089` → TCP open, TLS handshake fails (8089 = plain HTTP) → admin SPA ломается. + +Аналогично, ранее (issue #1) — CMS делал HTTP→HTTPS redirect с `:4443` (traefik external port). + +## Root cause + +`MoreThenCms.Admin\Mis\Web\Mvc\UrlHelpers\UrlHelpers.cs:14-37` `Url.SiteRoot()`: + +```csharp +var port = context.Request.ServerVariables["SERVER_PORT"]; +if (usePort) { + if (port == null || port == "80" || port == "443") port = ""; + else port = ":" + port; +} +var protocol = context.Request.ServerVariables["SERVER_PORT_SECURE"]; +if (protocol == null || protocol == "0") protocol = "http://"; else protocol = "https://"; +var sOut = protocol + context.Request.ServerVariables["SERVER_NAME"] + port + appPath; +``` + +Читает **socket-level** server variables. На IIS site `snolla` binding `*:8089` HTTP → `SERVER_PORT=8089`, `SERVER_PORT_SECURE=0` → формирует `http://emspb.snolla.com:8089` → рендерится в Razor: + +```cshtml +@* C:\sites\snolla\Views\Shared\_Layout.cshtml:229 *@ +mis.siteRoot = '@Url.SiteRoot().Replace("http:", "https:")' + '/admin'; +@* и _LogInLayout.cshtml:156 *@ +mis.siteRoot = '@Url.SiteRoot()'; +``` + +Замена `http:→https:` в Layout была *прошлым* частичным patch'ем — не убирает порт. Десятки .cshtml в admin также используют `Url.SiteRoot()` для inline `