diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index 3425430..0407db9 100644 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -9,8 +9,16 @@ python -m app.bootstrap echo "[entrypoint] Запуск сервера…" # --proxy-headers + доверие к X-Forwarded-* от реверс-прокси (Caddy на VPS): -# чтобы приложение знало, что снаружи запрос пришёл по HTTPS. +# чтобы приложение знало, что снаружи запрос пришёл по HTTPS и кто реальный клиент. +# +# forwarded-allow-ips НЕ "*" (#58): при "*" uvicorn брал ЛЕВОЕ значение X-Forwarded-For, +# и клиент мог подставить произвольный IP (снятие throttle, порча аудита). Доверяем только +# апстримам из приватной сети compose (туннель к Caddy ходит на app:8000) и loopback +# (healthcheck) — тогда uvicorn сканирует XFF справа и берёт первый недоверенный адрес, +# т.е. реальный, добавленный Caddy. Портов на хост нет, снаружи к :8000 никто не ходит. +# Переопределяемо через FORWARDED_ALLOW_IPS, если сеть отличается. # --timeout-graceful-shutdown: SSE-потоки /api/events сами не закрываются, и без лимита # остановка ждала бы их до SIGKILL по stop_grace_period (30 с) — без lifespan-shutdown. +FORWARDED_ALLOW_IPS="${FORWARDED_ALLOW_IPS:-127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}" exec uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 1 \ - --proxy-headers --forwarded-allow-ips="*" --timeout-graceful-shutdown 10 + --proxy-headers --forwarded-allow-ips="$FORWARDED_ALLOW_IPS" --timeout-graceful-shutdown 10 diff --git a/deploy/vps/Caddyfile b/deploy/vps/Caddyfile index 09cc82c..c4f4f7f 100644 --- a/deploy/vps/Caddyfile +++ b/deploy/vps/Caddyfile @@ -5,8 +5,13 @@ # forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV/TEST # # Caddy сам терминирует TLS (он и есть edge: видит реального клиента), а вниз к -# приложению добавляет X-Forwarded-Proto=https / X-Forwarded-For / Host — +# приложению передаёт X-Forwarded-Proto=https / X-Forwarded-For / Host — # приложение это учитывает (uvicorn --proxy-headers). Положи файл в /etc/caddy/Caddyfile. +# +# X-Forwarded-For ПЕРЕЗАПИСЫВАЕМ реальным пиром (header_up ... {remote_host}), а не +# добавляем: иначе клиент мог бы подставить своё левое значение и подменить IP для +# throttle и аудита (#58). Вместе с сужением forwarded-allow-ips в entrypoint.sh это +# делает клиентский IP достоверным. # Сертификаты — см. deploy/vps/README.md (fullchain = leaf + промежуточные одним файлом). # # SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/ @@ -65,11 +70,14 @@ forbiddenstars.ru { handle @sse { reverse_proxy 127.0.0.1:9000 { flush_interval -1 + header_up X-Forwarded-For {remote_host} } } handle { encode zstd gzip - reverse_proxy 127.0.0.1:9000 + reverse_proxy 127.0.0.1:9000 { + header_up X-Forwarded-For {remote_host} + } } import edge } @@ -80,11 +88,14 @@ forbidden-stars.ru { handle @sse { reverse_proxy 127.0.0.1:9001 { flush_interval -1 + header_up X-Forwarded-For {remote_host} } } handle { encode zstd gzip - reverse_proxy 127.0.0.1:9001 + reverse_proxy 127.0.0.1:9001 { + header_up X-Forwarded-For {remote_host} + } } import edge }