Пункты 14 и 16 ревью 2026-09-28 17:35 (docs/changes/025):
- TRUSTED_PROXIES (CIDR, по умолчанию пусто): X-Forwarded-For учитывается
только от доверенного peer, цепочка разбирается справа налево; иначе
заголовок игнорируется — подделать IP нельзя. Реальный IP — в блокировке
входа и событиях auth.*; неверный CIDR — отказ старта;
- docker-compose: "${APP_BIND:-0.0.0.0}:${APP_PORT:-8000}:8000"; стенд на
8001 через APP_PORT в .env, override-файл больше не нужен.
Тесты: 36 из 36. Стенд: поддельный X-Forwarded-For проигнорирован,
заблокирован реальный адрес; боевые данные не изменены. Ручная проверка
UI пользователем на момент коммита не подтверждена.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
План: 025 — доверенные прокси (реальный IP клиента) и параметризация порта
Context
Ревью docs/reviews/2026-09-28-1735-codebase-review.md:
- п. 14 — блокировка входа по IP (021) и IP в событиях
auth.*берутrequest.client.host. За reverse-proxy все клиенты видны с адреса прокси: одна блокировка на всех и журнал без реальных адресов. РазбораX-Forwarded-Forи доверенных прокси нет. - п. 16 —
docker-compose.ymlпубликует жёстко"8000:8000"; порт 8000 хоста занят посторонним процессом, стенд работает на 8001 через override-файл вне репозитория.
Решения пользователя:
- п. 16 — переменные
APP_PORT(порт на хосте, по умолчанию 8000) иAPP_BIND(адрес публикации, по умолчанию0.0.0.0), как вipam_control. В.envстенда оркестратор добавилAPP_PORT=8001— стенд поднимается обычнымdocker compose up -d, override не нужен. - п. 14 —
TRUSTED_PROXIESпустой по умолчанию и на стенде:X-Forwarded-Forигнорируется, пока прокси явно не указаны.
Изменения
п. 16 — порт (docker-compose.yml, .env.example, README)
ports: - "${APP_BIND:-0.0.0.0}:${APP_PORT:-8000}:8000". Внутренний порт контейнера не меняется (8000)..env.example:APP_PORT=8000,APP_BIND=0.0.0.0с комментарием (127.0.0.1— только за reverse-proxy на том же хосте).- Приложение эти переменные не читает (только compose);
Settings—extra="ignore", конфликтов нет.
п. 14 — реальный IP клиента (app/config.py, app/security.py или новый app/client_ip.py, app/ui/routes.py)
Settings.trusted_proxies: str = ""— CIDR через запятую (10.0.0.0/8,172.16.0.0/12); одиночный адрес — как/32//128. Невалидный CIDR — проблема вinsecure_settings(отказ старта, как у секретов): «TRUSTED_PROXIES: неверная сеть <значение>» (это не секрет — значение можно показать).client_ip(request) -> str:peer = request.client.host(или"unknown").- Если
peerне входит в доверенные сети (или список пуст) — вернутьpeer,X-Forwarded-Forигнорируется. - Иначе разобрать
X-Forwarded-For(все заголовки, через запятую) справа налево: пропускать адреса из доверенных сетей; первый недоверенный — IP клиента. Невалидная запись в цепочке — остановиться и вернуть последний валидный разобранный адрес (илиpeer, если таких нет). Все адреса доверенные / заголовка нет —peer. - Сети разбираются один раз (кэш по строке настройки).
- Если
login(app/ui/routes.py):ip = client_ip(request)вместоrequest.client.host— ключ блокировкиlogin:<ip>иdata.ipвauth.*. Другие места сrequest.client— найти grep'ом и перевести наclient_ip.- uvicorn
--proxy-headersне включать (иначе uvicorn сам подменитrequest.clientпо своей логике--forwarded-allow-ips).
Тесты (минимально, tests/test_app.py)
client_ip: пустойTRUSTED_PROXIES+ XFF → peer (подделка игнорируется); доверенный peer +XFF: 1.2.3.4, 10.0.0.2при10.0.0.0/8→1.2.3.4; недоверенный peer + XFF → peer; мусор в XFF → последний валидный / peer; IPv6.- Блокировка входа за прокси:
TestClient(client=("10.0.0.5", …)),TRUSTED_PROXIES=10.0.0.0/8, 5 неверных сXFF: 1.1.1.1→ 429 для1.1.1.1, сXFF: 2.2.2.2вход проходит. insecure_settings: невалидныйTRUSTED_PROXIES→ проблема.
Документация
README: «Конфигурация» (APP_PORT, APP_BIND, TRUSTED_PROXIES), «Быстрый старт» (адрес с APP_PORT), «Безопасность» (вход за прокси —
заменить «не поддерживается» на описание TRUSTED_PROXIES и предупреждение: доверие к сети Docker-моста делает доверенными и процессы хоста),
«Эксплуатация» (стенд на 8001 — через APP_PORT в .env, override не нужен), число тестов, строка 025 в истории. summary.md — оркестратор.
Исполнение
Исполнитель (Sonnet): код, compose, .env.example, тесты, README; пересборка стенда обычной командой
docker compose up -d --build --force-recreate (без override — порт 8001 берётся из .env). Тесты не запускает, не коммитит, .env не читает.
Проверка
pytest— все зелёные.docker compose config→ опубликован8001(из.env); контейнер Up на0.0.0.0:8001->8000;/login200; новый код.- Подделка: с хоста
curl -H "X-Forwarded-For: 9.9.9.9"— 5 неверных входов → 6-й 429 уже без заголовка (блокируется реальный адрес, XFF проигнорирован); событиеauth.failedсодержит реальный IP (адрес шлюза Docker), не9.9.9.9. Блокировка снимается перезапуском контейнера. - Боевые данные — сверка по ID.
- Ручная проверка — пользователь: UI на
http://<хост>:8001.