docs(tradein/caddy): исправить обоснование scrub + scrub в ui-preview (#2558)
All checks were successful
CI / changes (pull_request) Successful in 14s
CI Trade-In / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
All checks were successful
CI / changes (pull_request) Successful in 14s
CI Trade-In / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deep-review PR #2569 (echo-стенд на caddy:2, caddy adapt): комментарий у /trade-in/api* и @tradein утверждал, что старая Set-форма (header_up X-Authenticated-User {http.auth.user.id}) пропустила бы клиентский заголовок насквозь — измерено, что это неверно. Caddy подставляет неразрешённый плейсхолдер как литеральную строку (ReplaceKnown), т.е. старая форма была бы fail-closed-but-сломанной (get_role("{http.auth.user.id}") -> 403 для всех), а не bypass. Решение (-Field вместо Set) остаётся правильным по другой причине: это единственная форма с документированной delete- семантикой, не зависящая от трактовки пустого/нерезолвленного плейсхолдера. Также: @uipreview (единственный блок в trade-in-периметре без scrub) теперь тоже явно удаляет X-Authenticated-User - единообразие периметра, хоть сейчас и инертно (статичная страница без секрета). И: комментарий у @saleshare уточнён - снятие Caddy basic_auth с /trade-in/sale-share не делает страницу публичной, она по-прежнему за RouteGuard + session-required API (редирект на /login без валидной сессии). Провалидировано: caddy validate --config Caddyfile --adapter caddyfile (caddy:2, локальный docker) -> "Valid configuration".
This commit is contained in:
parent
01bf4a91e4
commit
2b24408c3f
1 changed files with 35 additions and 17 deletions
52
Caddyfile
52
Caddyfile
|
|
@ -77,7 +77,15 @@ gendsgn.ru {
|
|||
# Оба ДО auth-import, иначе ассеты страницы уходят в @tradein (под auth) → 401 → без CSS.
|
||||
@uipreview path /trade-in/ui-preview/* /trade-in/_next/static/*
|
||||
handle @uipreview {
|
||||
reverse_proxy tradein-frontend:3000
|
||||
reverse_proxy tradein-frontend:3000 {
|
||||
# #2558 review: тот же периметр-scrub, что и у /trade-in/api/* и
|
||||
# @tradein ниже — этот блок тоже теперь ДО basic_auth, клиент
|
||||
# мог бы прислать свой X-Authenticated-User. Сейчас инертно
|
||||
# (страница статична, у tradein-frontend нет секрета для
|
||||
# X-Internal-Auth-Secret), но убираем ради единообразия периметра,
|
||||
# а не полагаясь на то, что downstream ничего не делает с заголовком.
|
||||
header_up -X-Authenticated-User
|
||||
}
|
||||
}
|
||||
|
||||
# #2558: Trade-In MVP subproject (tradein-mvp/) — gendesign-tradein docker
|
||||
|
|
@ -91,19 +99,23 @@ gendsgn.ru {
|
|||
# X-Authenticated-User — ЯВНОЕ УДАЛЕНИЕ (`header_up -X-Authenticated-User`),
|
||||
# НЕ `header_up X-Authenticated-User {http.auth.user.id}`. Причина: этот
|
||||
# блок больше не идёт ПОСЛЕ basic_auth, поэтому `{http.auth.user.id}`
|
||||
# никогда не резолвится Caddy'ем (плейсхолдер пуст) — а поведение
|
||||
# `header_up <field> <value>` при пустом value НЕ задокументировано как
|
||||
# надёжное удаление заголовка (это Set-операция; нет гарантии, что Caddy
|
||||
# трактует пустую строку как "не отправлять поле" во всех версиях).
|
||||
# Полагаться на это было бы небезопасно: если бы Caddy просто НЕ
|
||||
# устанавливал заголовок (leave as-is) при пустом value, то ЛЮБОЙ
|
||||
# клиентский `X-Authenticated-User: someone` прошёл бы НАСКВОЗЬ до
|
||||
# tradein-backend — а backend в auth_mode="dual" (`app/core/config.py`)
|
||||
# всё ещё принимает legacy trusted-header путь (`rbac.py:186`) → внешняя
|
||||
# подмена личности без единого валидного пароля. `-Field` — единственная
|
||||
# ФОРМА С ЯВНО ЗАДОКУМЕНТИРОВАННОЙ семантикой "удалить заголовок"
|
||||
# (Caddyfile reverse_proxy directive: `-<field>` = delete), поэтому
|
||||
# используем её вместо Set с пустым значением — гарантия, а не догадка.
|
||||
# никогда не резолвится авторизованным юзером на этом пути.
|
||||
# Проверено эмпирически (echo-стенд на образе caddy:2, `caddy adapt`):
|
||||
# старая Set-форма (`header_up X-Authenticated-User {http.auth.user.id}`)
|
||||
# НЕ пропустила бы клиентский заголовок насквозь и НЕ оставила бы поле
|
||||
# пустым — Caddy подставляет НЕРАЗРЕШЁННЫЙ плейсхолдер как ЛИТЕРАЛЬНУЮ
|
||||
# строку (`ReplaceKnown`), т.е. upstream получил бы буквально
|
||||
# `X-Authenticated-User: {http.auth.user.id}`. Для backend (auth_mode=
|
||||
# "dual", `app/core/config.py`) это НЕ подмена личности — legacy path
|
||||
# (`rbac.py:186`) сделал бы `get_role("{http.auth.user.id}")`, юзер не
|
||||
# найден в roles.yaml → 403 для всех. Т.е. старая форма была бы не
|
||||
# security-дырой, а fail-closed-but-сломанной (все trade-in запросы без
|
||||
# session-cookie получали бы 403 вместо ожидаемого 401/редиректа на логин).
|
||||
# `-Field` остаётся правильным выбором не потому что Set был бы дырой, а
|
||||
# потому что это ЕДИНСТВЕННАЯ форма с явно задокументированной семантикой
|
||||
# "удалить заголовок" (Caddyfile reverse_proxy directive: `-<field>` =
|
||||
# delete) — корректное поведение не должно зависеть от того, как именно
|
||||
# Caddy трактует нерезолвленный/пустой плейсхолдер в Set-операции.
|
||||
# X-Internal-Auth-Secret НЕ трогаем — #2213-секрет всегда перезаписывается
|
||||
# из env (Set-операция с непустым значением, никак не связана с auth-гейтом
|
||||
# basic_auth), это единственное, что теперь отсекает подделку заголовков
|
||||
|
|
@ -124,9 +136,15 @@ gendsgn.ru {
|
|||
# (тот же tradein-frontend контейнер; query-string сохраняется). True vanity-URL
|
||||
# в адресной строке требует отдельного Next-app с basePath=/sale-share.
|
||||
# #2558: перенесён ВЫШЕ auth-import вместе с trade-in — редирект ведёт на
|
||||
# /trade-in/sale-share, который теперь публичен без basic_auth; оставлять
|
||||
# короткий /sale-share под basic_auth, а канонический адрес назначения без
|
||||
# него — несогласованно (короткий URL был бы строже целевой страницы).
|
||||
# /trade-in/sale-share, для которого теперь нет Caddy basic_auth (как и
|
||||
# для остального /trade-in). Это НЕ делает страницу публичной: она всё
|
||||
# ещё за собственной авторизацией trade-in — `RouteGuard` во фронте
|
||||
# (`app/layout.tsx`, «Доступ: pilot + admin») и сессия для
|
||||
# `/api/v1/buildings/sale-share*` на бэке; без валидной сессии юзер
|
||||
# получит редирект на /login, а не контент. Смысл переноса — не открыть
|
||||
# страницу всем, а убрать несогласованность: короткий URL не должен быть
|
||||
# строже (Caddy basic_auth) целевого адреса, к которому и так уже нет
|
||||
# basic_auth-барьера (только собственный login trade-in).
|
||||
@saleshare path /sale-share /sale-share/
|
||||
handle @saleshare {
|
||||
redir /trade-in/sale-share permanent
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue