# cms-port-leak-fix ## Goal Убрать утечку internal-портов в URL'ы, которые CMS генерирует серверно. Сейчас часть admin-роутов выдают URL'ы вида `https://labtools.snolla.com:8089/admin/...` — `:8089` это IIS site binding на хосте, не должен попадать наружу. Связанный, тот же класс баг (open issue #1 в [recovery-architecture-snapshot.md](../.wiki/concepts/recovery-architecture-snapshot.md)): CMS делает HTTP→HTTPS redirect с `:4443` (traefik external HTTPS port на хосте). Желательно фиксить общим решением. ## Симптомы (от user, 2026-05-19) Сломанные URLs admin'ки на `labtools.snolla.com` (наверняка и на остальных 10 cms доменах тоже): - `https://labtools.snolla.com:8089/admin/assets//getList?path=` - `https://labtools.snolla.com:8089/admin/themes/getImageSizes/` - `https://labtools.snolla.com:8089/admin/templates/editors.tmpl.html?v=2.006` Browser сразу делает request на `:8089` напрямую → fail (этот порт не открыт наружу через router NAT; traefik слушает 443/4443 only). ## Hypothesis — root cause Цепочка: 1. Traefik backend = `http://host.docker.internal:8089/`, `passHostHeader: true` → IIS видит `Host: labtools.snolla.com` (без порта). 2. IIS binding на site `snolla` = `*:8089` → connection-level порт = 8089. 3. `HttpRequest.Url.Port` / `Url.Authority` в ASP.NET возвращают **порт сокета**, не порт из Host header. Значит `Authority = "labtools.snolla.com:8089"`. 4. Где-то в admin views / controllers / partials CMS строит absolute URL через `Request.Url.GetLeftPart(UriPartial.Authority)` или `string.Format("{0}://{1}", scheme, host_with_port)` → утечка `:8089`. 5. Альтернатива — admin SPA получает baseUrl в server-rendered JS-переменной (`window.adminBaseUrl = '@Request.Url.Scheme://@Request.Url.Authority/admin'` или подобное). Это нужно подтвердить grep'ом по deployed-ASP-views + .js. Аналогичная hypothesis для `:4443`: - Не из `Url.Port` (он = 8089 на attempt2-host, был 18080 на VM-эпохе). - Скорее CMS читает `Request.Headers["X-Forwarded-Host"]` который traefik по умолчанию шлёт как `host:port`, ИЛИ кто-то один раз протестил через `https://host:4443/` напрямую и попал в captured-redirect. - Возможно: CMS делает `Response.Redirect` с absolute URL, и старый CMS-код в эпоху production-traefik (на синке тоже :4443?) научился возвращать публичный port из конфига. - **Эта часть требует тест-кейса** — повторить на host'е сейчас и посмотреть, попадает ли вообще `:4443` в response Location header. ## Key files (текущие deployed) - `C:\sites\snolla\Views\Shared\_Layout.cshtml` — main layout, есть `var hostingUrl = new Uri("https://" + AppSettings["primaryDomain"]);` (line 11-12). Не порт, но паттерн «строим absolute URL» подсветить. - `C:\sites\snolla\Areas\Admin\Views\_ViewStart.cshtml` — корень всех admin views, скорее всего тянет admin-specific layout. - `C:\sites\snolla\Areas\Admin\Views\**\Shared\*` — admin layout (искать; возможно тут baseUrl). - `C:\sites\snolla\admin\templates\*.tmpl.html` — статические template'ы admin SPA. Сами URL'ы не генерируют, но загружаются из JS которая видит baseUrl откуда-то. - `C:\sites\snolla\Web.config` — appSettings (`primaryDomain` уже есть), будущие `` rules. - `C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\*.yml` — site routes (тут middleware подключаем). - `C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\snolla.yml` `:1` — already has `passHostHeader: true`. Добавить middleware на forwarded-headers. ## План этапов ### Phase 1 — diagnostic (не write-op) - [ ] Сделать XHR call на `https://labtools.snolla.com/admin/themes/getImageSizes/` (с auth-cookie если есть) и записать response headers + body. Подтвердить откуда именно `:8089` приходит (Location header? `` tag? `