Files
ipam_control/docs/changes/034-caddy-publication/SUMMARY.md
T
ayurishchevandClaude Opus 5.5 8384c2c311 Задачи 034-035: публикация через Caddy, последний вход пользователя
034 Публикация стенда как https://rxipam.rxmsk.ru через общий Caddy хоста (Caddyfile вне репозитория):
    UI и /api/v1 доступны из интернета, /docs, /redoc, /openapi.json — только из частных сетей
    (RFC 1918/4193, loopback). Caddy проксирует на опубликованный порт приложения — общая Docker-сеть
    отклонена из-за коллизии имён app/db с Nextcloud. TRUSTED_PROXIES=172.16.0.0/12: реальный IP
    клиента из X-Forwarded-For. Интеграционный тест журнала больше не проверяет подделку XFF с хоста
    (хост за Caddy доверенный), логику покрывает unit-тест.
035 Дата и IP последнего входа в разделе «Пользователи»: users.last_login_at / last_login_ip
    (миграция 0014 с заполнением из журнала), запись при успешном входе, UserOut, колонка в UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:45:30 +03:00

7.1 KiB

Итог: публикация через Caddy — rxipam.rxmsk.ru (изменение 034)

План: PLAN.md. Стенд ipam_control_006 опубликован как https://rxipam.rxmsk.ru через общий Caddy хоста (/opt/lvraid/apps/caddy). Из интернета доступны UI и /api/v1; Swagger и OpenAPI — только из частных сетей.

Отклонение от плана: без общей Docker-сети

План предлагал подключить приложение к сети Caddy app_internal (docker-compose.caddy.yml, алиас ipam-app). При применении обнаружилась коллизия имён. Compose регистрирует имя сервиса как DNS-алиас в каждой сети контейнера, а в app_internal уже есть db и app стека Nextcloud:

  • приложение по имени db ушло в базу Nextcloud и не стартовало;
  • Caddy адресует Nextcloud как app:9000 — второй app в той же сети ломал бы Nextcloud.

Подключение откатили примерно через минуту: приложение всё это время перезапускалось, в логе Nextcloud ошибок нет, rxcloud отвечает. Итоговая схема: Caddy проксирует на опубликованный порт приложения через шлюз своей сети — reverse_proxy 172.19.0.1:8088. Сети не объединяются, docker-compose.yml не менялся, docker-compose.caddy.yml не создавался.

Что сделано

Где Изменение
/opt/lvraid/apps/caddy/Caddyfile (вне репозитория) Блок rxipam.rxmsk.ru: access-лог rxipam.log (JSON, INFO, ротация); @docs_external (/docs, /docs/*, /redoc, /redoc/*, /openapi.json и not remote_ip private_ranges) → 404; reverse_proxy 172.19.0.1:8088; HSTS, -Server. Бэкап: Caddyfile.bak-2026-09-27. Правка на месте, inode сохранён — контейнер видит новый файл; применено caddy reload
.env стенда (вне репозитория) TRUSTED_PROXIES=172.16.0.0/12: запросы Caddy приходят в приложение с шлюза Docker-моста (172.31.0.1) через NAT хоста. Диапазон вместо /32 — подсеть проекта может смениться при пересоздании сети
.env.example Закомментированный пример TRUSTED_PROXIES для публикации через Caddy на том же хосте
README.md «Публикация и эксплуатация» — фактическая схема вместо примера; строка 034 в истории
tests/test_journal.py Из интеграционного test_client_ip_and_request_meta_recorded убрана проверка «подделка XFF игнорируется»: с TRUSTED_PROXIES=172.16.0.0/12 запросы тестов с хоста (через 172.31.0.1) доверенные. Логику покрывает unit-тест test_resolve_client_ip_trusts_forwarded_header_only_from_proxies

Проверки

  • caddy validate — конфигурация корректна; caddy reload без рестарта. Соседние сайты после reload: vault, artstore, aycv — 200, rxcloud — 302 (редирект на вход).
  • Сертификат Let's Encrypt для rxipam.rxmsk.ru выпущен (DNS-01, Cloudflare).
  • Из частной сети через Caddy: / 200, /api/v1/auth/me без токена 401, /docs и /openapi.json 200. Заголовки HSTS и CSP присутствуют.
  • Matcher Swagger проверен на временном Caddy того же образа (тот же path, без условия по IP): /docs, //docs, /%64ocs, /docs/oauth2-redirect, /openapi.json, /%6fpenapi.json, /redoc, /%72edoc → 404; / 200, API 401. Проверка важна: приложение само отдаёт Swagger на URL-кодированные пути (/%64ocs → 200), и закрывает их именно Caddy.
  • private_ranges раскрывается в 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 127.0.0.1/8, fd00::/8, ::1.
  • Реальный IP клиента: запрос через Caddy с LAN-адреса хоста и подложным X-Forwarded-For: 8.8.8.8 → в журнале 192.168.5.9. Подложный заголовок отброшен Caddy, реальный адрес получен из XFF.
  • pytest -q — 15 passed (после правки теста выше).
  • Пробные неудачные входы (rv-probe-034, rv-probe-034b) остались в журнале как события session.failed.

Не проверено (требует пользователя)

  • DNS: CNAME rxipam → mkt.rxmsk.ru (proxy Cloudflare выключен) ещё не создан — имя не резолвится.
  • Доступ из интернета (мобильный интернет или внешний хост): / 200 и вход в UI; /docs, /openapi.json → 404; в rxipam.log у этих запросов публичный remote_ip; в журнале приложения — публичный client_ip события входа.

Рекомендации

  • Доверие 172.16.0.0/12 означает, что процессы и контейнеры на самом хосте (приходят через шлюз Docker-моста) могут подставить свой IP в X-Forwarded-For. LAN- и интернет-клиенты этого не могут: у LAN-клиентов, идущих напрямую на :8088, в журнале реальные адреса. Исключить хост из доверенных можно только прямой сетью Caddy ↔ приложение без NAT — это требует переименовать сервисы app/db стенда (коллизия выше), отдельная задача.
  • Split DNS на MikroTik (rxipam.rxmsk.ru → 192.168.5.9 в LAN): иначе LAN-клиенты через hairpin видны как 192.168.5.253 и делят лимит «20 неудач на IP».
  • План 031, часть 1 (DOCS_ENABLED=false по умолчанию) — вторая линия защиты Swagger, отдельной задачей.
  • 172.19.0.1 — шлюз внешней сети app_internal. При её пересоздании с другой подсетью обновить reverse_proxy в Caddyfile.

Откат

  • Caddy: содержимое Caddyfile.bak-2026-09-27 записать поверх Caddyfile на месте, затем docker exec caddy caddy reload --config /etc/caddy/Caddyfile.
  • Стенд: убрать TRUSTED_PROXIES из .env, docker compose -p ipam_control_006 up -d.