gendesign/caddy/sites/infra.caddy
bot-backend 34c15ecf08
Some checks failed
CI Trade-In / changes (pull_request) Successful in 15s
CI / changes (pull_request) Successful in 20s
CI Trade-In / browser-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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 1m39s
feat(ops): маршрут ретранслятора Telegram на metrics.gendsgn.ru
Без маршрута сервис tg-relay недостижим снаружи и настройка
TELEGRAM_RELAY_BASE_URL на продуктовом хосте ни к чему не приводит.

Путь несёт токен бота, поэтому помечен log_skip: иначе он осядет в
файловом логе сайта и в stdout-копии, которую читает Alloy (#3154).
Таймаут ответа поднят до 80с — getUpdates висит long-poll'ом до ~40с,
дефолтного не хватает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
2026-09-12 14:19:58 +03:00

164 lines
9.4 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ═══════════════════════════════════════════════════════════════════════════
# 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/*, /glitchtip и /healthz; всё прочее — 404.
handle /ack/* {
reverse_proxy alert-ack:8080
}
# Резервный приёмник алертов GlitchTip (#3471). Основной получатель —
# продуктовый бэкенд на Selectel, то есть тот самый сервис, за которым эти
# алерты и следят: пока он лежит, его собственные ошибки доставлять некому.
# Этот путь живёт у другого провайдера и с чистой сетью до Telegram, поэтому
# переживает падение Selectel целиком.
#
# Секрет — в значении query-параметра, а не в пути: путь сам по себе не
# секрет, и его попадание в access-лог безопасно. Само значение вырезает
# scrub_credentials в Alloy до записи в Loki (#3154).
handle /glitchtip* {
reverse_proxy alert-ack:8080
}
# Ретранслятор Bot API продукта (#3471). Путь от Selectel до
# api.telegram.org теряет примерно каждый четвёртый короткий запрос, тот же
# замер с Beget в те же минуты — чистый. Поэтому продуктовый бот ходит в
# Telegram отсюда, а не напрямую.
#
# Путь содержит токен бота (/tg-relay/bot<TOKEN>/<method>) — он не должен
# осесть ни в файловом логе сайта, ни в stdout-копии (#3154), поэтому
# запрос помечен log_skip.
@tg_relay path /tg-relay/*
log_skip @tg_relay
handle_path /tg-relay/* {
reverse_proxy tg-relay:8080 {
# getUpdates — long-poll до ~40с, дефолтный таймаут ответа короче.
transport http {
response_header_timeout 80s
}
}
}
# Вход ОДИН — собственный вход 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
}
}