Хардненинг по итогам пен-теста #25 (#56–#62) #63

Merged
NotBigGhost merged 7 commits from issue-56-62-hardening into dev 2026-09-13 18:38:11 +03:00
2 changed files with 24 additions and 5 deletions
Showing only changes of commit ec65f104ee - Show all commits
+10 -2
View File
@@ -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
View File
@@ -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
} }