Прокси-заголовки: не доверять произвольному X-Forwarded-For
entrypoint.sh: --forwarded-allow-ips сужен с "*" до loopback + приватных сетей
compose (переопределяемо FORWARDED_ALLOW_IPS) — uvicorn сканирует XFF справа и
берёт реальный адрес, подставленное клиентом левое значение игнорируется.
Caddyfile: reverse_proxy перезаписывает X-Forwarded-For реальным пиром
(header_up {remote_host}) вместо добавления. Итог — достоверный IP для throttle
и аудита. Инфра-часть проверяется на test-клоне. #58
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
+10
-2
@@ -9,8 +9,16 @@ python -m app.bootstrap
|
|||||||
|
|
||||||
echo "[entrypoint] Запуск сервера…"
|
echo "[entrypoint] Запуск сервера…"
|
||||||
# --proxy-headers + доверие к X-Forwarded-* от реверс-прокси (Caddy на VPS):
|
# --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 сами не закрываются, и без лимита
|
# --timeout-graceful-shutdown: SSE-потоки /api/events сами не закрываются, и без лимита
|
||||||
# остановка ждала бы их до SIGKILL по stop_grace_period (30 с) — без lifespan-shutdown.
|
# остановка ждала бы их до 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 \
|
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
|
||||||
|
|||||||
+14
-3
@@ -5,8 +5,13 @@
|
|||||||
# forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV/TEST
|
# forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV/TEST
|
||||||
#
|
#
|
||||||
# Caddy сам терминирует TLS (он и есть edge: видит реального клиента), а вниз к
|
# 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.
|
# приложение это учитывает (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 + промежуточные одним файлом).
|
# Сертификаты — см. deploy/vps/README.md (fullchain = leaf + промежуточные одним файлом).
|
||||||
#
|
#
|
||||||
# SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/
|
# SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/
|
||||||
@@ -65,11 +70,14 @@ forbiddenstars.ru {
|
|||||||
handle @sse {
|
handle @sse {
|
||||||
reverse_proxy 127.0.0.1:9000 {
|
reverse_proxy 127.0.0.1:9000 {
|
||||||
flush_interval -1
|
flush_interval -1
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle {
|
handle {
|
||||||
encode zstd gzip
|
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
|
import edge
|
||||||
}
|
}
|
||||||
@@ -80,11 +88,14 @@ forbidden-stars.ru {
|
|||||||
handle @sse {
|
handle @sse {
|
||||||
reverse_proxy 127.0.0.1:9001 {
|
reverse_proxy 127.0.0.1:9001 {
|
||||||
flush_interval -1
|
flush_interval -1
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle {
|
handle {
|
||||||
encode zstd gzip
|
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
|
import edge
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user