fix(tradein/security): defense-in-depth trusted-header auth + rate-limiter fix (#2213) #2229

Merged
lekss361 merged 1 commit from fix/tradein-defense-in-depth-auth into main 2026-07-02 21:15:59 +00:00
Owner

Summary

  • Shared-secret Caddy↔tradein-backend: при заданном TRADEIN_INTERNAL_AUTH_SECRET каждый запрос с X-Authenticated-User обязан нести X-Internal-Auth-Secret (secrets.compare_digest, constant-time) — подделка username изнутри docker-сети даёт 401 + WARNING. Fail-open до провижининга (env не задан → поведение байт-как-раньше + WARNING на старте). Caddy header_up перезаписывает клиентский заголовок (та же механика, что защищает X-Authenticated-User).
  • Rate-limiter: per-user (user:<name>, лимит ×5) / per-IP (ip:<addr>) ключи вместо освобождения всех authenticated; client IP из rightmost XFF (единственный доверенный прокси Caddy аппендит real IP; leftmost клиент-контролируем и игнорируется).
  • Caddyfile: убран глобальный log_credentials (писал обратимые base64 basic-auth пароли в оба лога). glitchtip-auth-forwarder деградирует gracefully (attempted_username(none), 401-события продолжают уходить — проверено).
  • Тесты: 13 новых/переписанных (secret-gate negative paths + per-user isolation + XFF-spoof).

⚠️ Провижининг секрета (human, ПОРЯДОК КРИТИЧЕН)

  1. openssl rand -hex 32.
  2. Сначала Caddy-стек: значение в /opt/gendesign/.envrecreate caddy-контейнера (reload env не перечитает).
  3. Потом tradein-стек: то же значение в рантайм-окружение tradein-backend → restart backend.
  4. Обратный порядок (backend раньше Caddy) = fail-closed 401 на всё приложение. Пока секрет не задан нигде — ничего не меняется (fail-open, WARNING в логах).
  5. Значения в двух env-файлах обязаны совпадать; дрейф = outage.

Как проверено

  • Полный локальный pytest: 2983 passed, 6 skipped, 1 deselected.
  • code-reviewer (adversarial): APPROVE — fail-open/fail-closed семантика проверена (полу-провижининг = fail-closed, не bypass); SSR-форжинг невозможен (все страницы "use client", route handlers отсутствуют); bypass-анализ чист (_PUBLIC_PATHS только /health /docs /redoc /openapi.json); Caddy аппендит XFF (rightmost корректен); forwarder None-safe.
  • Секретов в diff нет (только имена env-переменных).

Refs #2213

## Summary - **Shared-secret Caddy↔tradein-backend:** при заданном `TRADEIN_INTERNAL_AUTH_SECRET` каждый запрос с `X-Authenticated-User` обязан нести `X-Internal-Auth-Secret` (`secrets.compare_digest`, constant-time) — подделка username изнутри docker-сети даёт 401 + WARNING. **Fail-open до провижининга** (env не задан → поведение байт-как-раньше + WARNING на старте). Caddy `header_up` перезаписывает клиентский заголовок (та же механика, что защищает X-Authenticated-User). - **Rate-limiter:** per-user (`user:<name>`, лимит ×5) / per-IP (`ip:<addr>`) ключи вместо освобождения всех authenticated; client IP из **rightmost** XFF (единственный доверенный прокси Caddy аппендит real IP; leftmost клиент-контролируем и игнорируется). - **Caddyfile:** убран глобальный `log_credentials` (писал обратимые base64 basic-auth пароли в оба лога). glitchtip-auth-forwarder деградирует gracefully (`attempted_username` → `(none)`, 401-события продолжают уходить — проверено). - Тесты: 13 новых/переписанных (secret-gate negative paths + per-user isolation + XFF-spoof). ## ⚠️ Провижининг секрета (human, ПОРЯДОК КРИТИЧЕН) 1. `openssl rand -hex 32`. 2. **Сначала** Caddy-стек: значение в `/opt/gendesign/.env` → **recreate** caddy-контейнера (reload env не перечитает). 3. **Потом** tradein-стек: то же значение в рантайм-окружение tradein-backend → restart backend. 4. Обратный порядок (backend раньше Caddy) = fail-closed **401 на всё приложение**. Пока секрет не задан нигде — ничего не меняется (fail-open, WARNING в логах). 5. Значения в двух env-файлах обязаны совпадать; дрейф = outage. ## Как проверено - Полный локальный pytest: **2983 passed, 6 skipped, 1 deselected**. - code-reviewer (adversarial): ✅ APPROVE — fail-open/fail-closed семантика проверена (полу-провижининг = fail-closed, не bypass); SSR-форжинг невозможен (все страницы "use client", route handlers отсутствуют); bypass-анализ чист (_PUBLIC_PATHS только /health /docs /redoc /openapi.json); Caddy аппендит XFF (rightmost корректен); forwarder None-safe. - Секретов в diff нет (только имена env-переменных). Refs #2213
lekss361 added 1 commit 2026-07-02 21:08:15 +00:00
fix(tradein/security): defense-in-depth trusted-header auth + rate-limiter fix (#2213)
All checks were successful
CI / changes (pull_request) Successful in 7s
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 Trade-In / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m43s
07450aeed2
- shared-secret Caddy↔tradein-backend: X-Internal-Auth-Secret
  (secrets.compare_digest, constant-time), fail-open до провижининга
  TRADEIN_INTERNAL_AUTH_SECRET (WARNING на старте); при заданном секрете
  подделка X-Authenticated-User изнутри docker-сети даёт 401
- rate-limiter: per-user/per-IP ключ вместо освобождения всех authenticated;
  client IP из rightmost XFF (один доверенный прокси Caddy, который
  аппендит real IP); leftmost XFF клиент-контролируем и игнорируется
- Caddyfile: убран глобальный log_credentials (писал обратимые base64
  basic-auth пароли в логи; glitchtip-forwarder деградирует gracefully)
- тесты: secret-gate (401/200/backward-compat/wrong/no-user) + per-user
  rate-limit + XFF-spoof нейтрализован

Провижининг (human, порядок критичен): значение секрета СНАЧАЛА в
окружение Caddy-стека (+recreate caddy), ПОТОМ в рантайм-окружение
tradein-backend (+restart). Обратный порядок = fail-closed 401 на всё
приложение. Значение секрета в репо не попадает.

Refs #2213
lekss361 merged commit 5fb5675871 into main 2026-07-02 21:15:59 +00:00
lekss361 deleted branch fix/tradein-defense-in-depth-auth 2026-07-02 21:15:59 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2229
No description provided.