# ═══════════════════════════════════════════════════════════════════════════ # caddy/sites/infra.caddy — инфраструктурные домены, остающиеся на Beget # # Переезд GenDesign с Beget (46.173.16.127) на Selectel Poincare # (188.124.37.140), окно 30.08.2026. Три домена этого файла ОСТАЮТСЯ на # Beget — переезжают только Site Finder/МЕРА-домены (см. # caddy/sites/apps.caddy). Общий механизм переключения (переменная # CADDY_SITES, `import caddy/sites/{$CADDY_SITES:*}.caddy` в корневом # Caddyfile, мотив с ACME rate-limit) описан в шапке apps.caddy — не # дублируем здесь, читай там. # # Домены в этом файле (после переезда — на Beget, CADDY_SITES=infra): # - obsidian.gendsgn.ru — Obsidian Self-hosted LiveSync (CouchDB backend) # - errors.gendsgn.ru — GlitchTip, self-hosted error tracking # - git.gendsgn.ru — Forgejo, self-hosted git + Actions CI # # На Selectel (CADDY_SITES=apps) этот файл не импортируется вообще — Caddy # там даже не видит эти хосты и не пытается выпускать для них сертификаты. # ═══════════════════════════════════════════════════════════════════════════ # Obsidian Self-hosted LiveSync (CouchDB backend). # Auto-TLS Let's Encrypt. CORS уже включён на стороне CouchDB через bootstrap # (см. scripts/setup-couchdb.sh). Basic-auth — на стороне CouchDB (admin user). # # DNS: A-record obsidian.gendsgn.ru → IP VPS. # Клиенты Obsidian + Self-hosted LiveSync plugin указывают на этот URL. obsidian.gendsgn.ru { encode zstd gzip reverse_proxy couchdb:5984 { # Большие документы (vault attachments / images) — увеличиваем timeout transport http { response_header_timeout 120s } } } # GlitchTip — self-hosted error tracking (Sentry-compatible). # DNS: A-record errors.gendsgn.ru → IP VPS. errors.gendsgn.ru { encode zstd gzip reverse_proxy glitchtip-web:8080 log { output file /var/log/caddy/errors.gendsgn.ru.log } } # Forgejo — self-hosted git (migration 2026-05-16). # DNS: A-record git.gendsgn.ru → IP VPS. # Forgejo container из forgejo-migration/docker-compose.yml на shared # gendesign_default network. HTTP port 3000 (default Forgejo). # Был добавлен вручную при migration, потерян при первом auto-deploy после # изменения Caddyfile (deploy.yml делает git reset --hard). См. fix issue. git.gendsgn.ru { encode zstd gzip reverse_proxy forgejo:3000 log { output file /var/log/caddy/git.gendsgn.ru.log } } # Мониторинг — Grafana + приёмник метрик и логов (задача #3078). # DNS: A-record metrics.gendsgn.ru → 46.173.16.127 (заведена 2026-08-26). # # ПОЧЕМУ ЗДЕСЬ, А НЕ В apps.caddy. Наблюдатель сознательно поставлен у ДРУГОГО # провайдера, чем наблюдаемое: продукт живёт на Selectel Poincare, и если ляжет # он, мониторинг обязан выжить и сказать об этом. Стек поднимается отдельным # compose-проектом gendesign-metrics и виден Caddy через сеть gendesign_shared. # # ДВА КОНТУРА С РАЗНЫМИ УЧЁТКАМИ: # /ingest/* — машинный. Агент Alloy с Poincare шлёт сюда remote_write и логи # исходящим HTTPS. Благодаря этому на Poincare не открыт НИ ОДИН # входящий порт сверх 22/80/443. # всё прочее — витрина Grafana под отдельным паролем. # Пароль приёмника по построению лежит в открытом виде на продуктовом хосте; # разделив учётки, мы не отдаём вместе с ним доступ к дашбордам. metrics.gendsgn.ru { encode zstd gzip # handle_path срезает префикс: Prometheus получает /api/v1/write, # как если бы обращались к нему напрямую. handle_path /ingest/prometheus/* { import ../metrics-ingest.caddy.snippet reverse_proxy prometheus:9090 } # Loki ожидает путь /loki/api/v1/push — он и остаётся после среза /ingest/loki. handle_path /ingest/loki/* { import ../metrics-ingest.caddy.snippet reverse_proxy loki:3100 } # Кнопка «Принял в работу» под клиентским инцидентом (#3078). # # БЕЗ АВТОРИЗАЦИИ НАМЕРЕННО. Кнопку жмут с телефона, ночью, когда лежит # прод. Требовать в этот момент ввод пароля — значит получить ноль нажатий # и вернуться к вопросу «а кто-нибудь вообще это читает». # # Защита — неугадываемость и одноразовость: путь содержит 128-битный токен, # выданный ровно под это сообщение и живущий сутки. Худшее, чего добьётся # угадавший, — ложная отметка «принято» в чате, где сразу видно, что её # поставил не человек. Прав в системе токен не даёт никаких. # # Сервис отвечает только на /ack/* и /healthz; всё прочее — 404. handle /ack/* { reverse_proxy alert-ack:8080 } # Вход ОДИН — собственный вход Grafana (#3078). Внешний basic_auth снят по # решению владельца: два запроса пароля подряд мешали работе, а Grafana имеет # собственную аутентификацию с ролями и `GF_USERS_ALLOW_SIGN_UP=false`. # # Что при этом теряется, чтобы решение было осознанным: basic_auth отсекал # сканеры до Grafana и прикрывал бы её собственную будущую уязвимость. Теперь # страница входа Grafana видна из интернета напрямую. # # Приём метрик НАРОЧНО остаётся под basic_auth (см. handle_path выше): туда # ходит агент по паролю, который лежит в открытом виде в окружении # продуктового хоста, и отдельная учётка там ограничивает ущерб записью. handle { reverse_proxy grafana:3000 } log { output file /var/log/caddy/metrics.gendsgn.ru.log } }