Some checks failed
Deploy / changes (push) Successful in 8s
Deploy Trade-In / changes (push) Successful in 12s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-worker (push) Successful in 48s
Deploy / build-backend (push) Successful in 49s
Deploy / build-frontend (push) Successful in 49s
Deploy / deploy (push) Successful in 1m37s
Deploy / deploy-status (push) Successful in 1s
Deploy Trade-In / build-frontend (push) Successful in 2m34s
Deploy / perimeter-smoke (push) Successful in 10s
Deploy Trade-In / test (push) Failing after 3m56s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Has been skipped
Deploy Trade-In / perimeter-smoke (push) Has been skipped
Deploy Trade-In / deploy-status (push) Failing after 1s
96 lines
5.9 KiB
Caddyfile
96 lines
5.9 KiB
Caddyfile
# Caddy config.
|
||
#
|
||
# - gendsgn.ru — main production site, auto-TLS via Let's Encrypt.
|
||
# - www.gendsgn.ru — 301 redirect to apex (canonical URL).
|
||
# - :80 — fallback for raw IP access. Closed by same auth gate (pilot phase).
|
||
#
|
||
# `handle /api/*` (NOT `handle_path`) — we keep the /api prefix because
|
||
# the FastAPI router is mounted at /api/v1/*.
|
||
#
|
||
# Auth gate: basic_auth for pilot phase (2026-05-23).
|
||
# Users managed via caddy/users.caddy.snippet (git history = audit trail).
|
||
# Public exclusions: /health (liveness probe), /preview/* (static mockups).
|
||
#
|
||
# #2558: с 2026-07 basic_auth гейтит ТОЛЬКО Site Finder (`/`, `/api/*`,
|
||
# `/analytics` и т.д.). `/trade-in/*` (+ `/sale-share` redirect) вынесены ВЫШЕ
|
||
# import'а — у trade-in своя авторизация (форма входа + opaque session-cookie,
|
||
# см. #2552) поверх RBAC (`tradein-mvp/backend/app/core/rbac.py`). Site Finder
|
||
# всё ещё легаси-пилотный basic_auth (roles.yaml dual-mode остаётся живым для
|
||
# него — НЕ трогать caddy/users.caddy.snippet).
|
||
#
|
||
# IMPORTANT: route { } block is required to preserve directive order.
|
||
# Without route { }, Caddy executes directives in hard-coded default order
|
||
# (basic_auth runs before handle), making /health and /preview/* exclusions
|
||
# ineffective. With route { }, handlers are matched top-to-bottom as written.
|
||
|
||
# #2213: global `log_credentials` УБРАН. Он заставлял Caddy писать raw
|
||
# Authorization header (Base64 `user:password`, тривиально обратимый) в ОБА лог-файла
|
||
# (gendsgn.ru.log + auth_audit.log). Даже при root-only доступе и коротком retention
|
||
# это plaintext-пароли пилотов на диске — недопустимый риск против скромной выгоды.
|
||
# Trade-off: glitchtip-auth-forwarder больше не извлекает `attempted_username` из 401
|
||
# (тег деградирует в "(none)" — forwarder это уже обрабатывает gracefully, не падает).
|
||
# Событие basic_auth 401 (remote_ip / uri / method) по-прежнему уходит в GlitchTip.
|
||
|
||
|
||
# ── Site-блоки вынесены по хостам (#3059, переезд 30.08) ────────────────────
|
||
# Раньше все восемь доменов жили прямо здесь. После разделения продуктов между
|
||
# двумя хостами это стало опасно: деплой синхронизирует рабочее дерево с
|
||
# origin/main и перечитывает конфиг, поэтому на Selectel приезжал бы файл
|
||
# целиком — и Caddy начинал бы выпускать сертификаты для obsidian/errors/git,
|
||
# чей DNS указывает на Beget. ACME падал бы на HTTP-01, с риском упереться в
|
||
# rate limit Let's Encrypt.
|
||
#
|
||
# caddy/sites/apps.caddy gendsgn.ru, www, meraocenka, merahome, meraotsenka
|
||
# -> уезжают на Selectel
|
||
# caddy/sites/infra.caddy obsidian, errors, git
|
||
# -> остаются на Beget (Forgejo, GlitchTip, CouchDB)
|
||
#
|
||
# CADDY_SITES выбирает подмножество. Дефолт `*` = оба файла = ТЕКУЩЕЕ поведение
|
||
# Beget, где сейчас обслуживаются все восемь доменов — то есть до переезда
|
||
# ничего не меняется. В окне: на Selectel CADDY_SITES=apps, на Beget=infra.
|
||
import caddy/sites/{$CADDY_SITES:*}.caddy
|
||
|
||
# Plain HTTP by IP — closed by same auth gate (prevent bypass via direct IP / SSH tunnel).
|
||
# Caddy issues no TLS here (no hostname). /health remains public.
|
||
:80 {
|
||
encode zstd gzip
|
||
|
||
route {
|
||
# /health — public, без auth (GHA deploy smoke check, liveness probe).
|
||
handle /health {
|
||
reverse_proxy backend:8000
|
||
}
|
||
|
||
# Auth gate (same snippet as gendsgn.ru).
|
||
import caddy/users.caddy.snippet
|
||
|
||
handle /api/* {
|
||
reverse_proxy backend:8000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
|
||
handle {
|
||
reverse_proxy frontend:3000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
# Test deploy flow 2026-05-15T21:43:32Z
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Локальные site-блоки, которых не может быть в git.
|
||
#
|
||
# Мотив: garmin.gendsgn.ru (личный remote MCP на этом же VPS). Апстрим-сервер
|
||
# аутентификации не имеет вовсе, а claude.ai custom connector ходит на голый URL
|
||
# без кастомных заголовков — единственный доступный рубеж это секрет в пути.
|
||
# Секрет в git класть нельзя, а блок, вписанный руками прямо сюда, сносится
|
||
# первым же деплоем (`git reset --hard origin/main`; 2026-08-16 так и вышло —
|
||
# контейнер остался жив, но хост пропал вместе со своим сертификатом).
|
||
#
|
||
# Поэтому: сам блок лежит на VPS как untracked `caddy/local/*.caddy` (reset
|
||
# --hard untracked не трогает), а в репозитории живёт только этот import.
|
||
# Пустой glob для Caddy не ошибка — `caddy validate` проходит, на машинах без
|
||
# локальных блоков строка просто ничего не делает.
|
||
import caddy/local/*.caddy
|