# 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