All checks were successful
Deploy production / deploy (push) Successful in 10m51s
66 lines
5.4 KiB
Markdown
66 lines
5.4 KiB
Markdown
# Регистрация только из локальной сети
|
||
|
||
Переключатель в **серверном** backend `.env` (не frontend и не секрет runner):
|
||
|
||
```dotenv
|
||
REGISTRATION_LAN_ONLY_ENABLED=true
|
||
```
|
||
|
||
Если переменной нет, она пустая или равна `false`, ограничение **выключено**.
|
||
Включает его только `true` (регистр и пробелы по краям не важны). После изменения
|
||
`.env` нужен перезапуск процессов Django или штатный деплой: настройки читаются
|
||
при старте. Сам `.env` в Git не добавляется.
|
||
|
||
При включении оба адреса `/api/auth/register/` и `/api/v1/auth/register/`
|
||
принимают регистрацию только из `172.11.0.0/23`. Снаружи возвращается HTTP 403
|
||
с понятным сообщением до валидации, создания пользователя, отправки письма и LDAP.
|
||
Вход, OIDC, восстановление пароля и подтверждение уже отправленного письма этим
|
||
ограничением не затрагиваются.
|
||
|
||
Frontend запрашивает `/api/auth/registration-availability/` (есть и alias `/api/v1/auth/registration-availability/`).
|
||
Ответ `{"registration_allowed": true/false}` использует ту же проверку, что POST регистрации,
|
||
не содержит IP или конфигурацию сети и имеет `Cache-Control: private, no-store`.
|
||
Ссылка «Создать аккаунт» видна только после разрешающего ответа.
|
||
При прямом открытии `/register` извне показывается объяснение вместо формы.
|
||
Во время проверки и при ошибке форма скрыта; при ошибке можно повторить запрос.
|
||
Проверка повторяется при возврате фокуса, восстановлении страницы и соединения.
|
||
Авторитетной защитой остаётся POST API — скрытие кнопки её не заменяет.
|
||
|
||
## Адрес клиента и границы доверия
|
||
|
||
Рабочий маршрут: клиент → Synology `172.11.1.254` → nginx CORP → ASGI на loopback.
|
||
Оба прокси должны дописывать настоящий IP своего непосредственного клиента
|
||
в `X-Forwarded-For`. На CORP это `$proxy_add_x_forwarded_for`.
|
||
|
||
Проверка идёт справа налево, пропуская только явно доверенные прокси
|
||
(`127.0.0.1`, `::1`, `172.11.1.254`), до первого недоверенного адреса.
|
||
Подставленный клиентом префикс не влияет на решение. Другие заголовки адреса
|
||
не используются. Если запрос пришёл от доверенного прокси, но адрес клиента
|
||
отсутствует, повреждён или состоит только из адресов прокси, доступ запрещён.
|
||
Router `172.11.0.1`, прокси и network/broadcast адреса не считаются пользователями.
|
||
`172.11.0.0/16` и произвольные частные сети не разрешены.
|
||
|
||
Нельзя открывать backend-порты наружу или добавлять всю LAN в доверенные прокси.
|
||
Если пограничный NAT скрывает внешний адрес за другим LAN-IP, сначала исправить
|
||
передачу исходного адреса/добавить этот шлюз в исключения: по одному IP приложение
|
||
не отличит такой запрос от локального. Список прокси не является списком клиентов.
|
||
|
||
16 сентября 2026 проверены реальные заголовки через Synology:
|
||
|
||
- обычный LAN-запрос: `172.11.1.128, 172.11.1.254`;
|
||
- запрос с подставленным XFF: `172.11.0.123, 172.11.1.128, 172.11.1.254`.
|
||
|
||
## Проверка
|
||
|
||
`pytest tests/api/auth/test_registration_network.py` проверяет оба режима,
|
||
обе версии API, успешную регистрацию, отсутствие побочных эффектов при отказе,
|
||
подмену заголовков, IPv6, повреждённые цепочки и доступность остальных auth API.
|
||
В production проверять без создания учёток: POST пустого JSON `{}` из LAN
|
||
должен вернуть 400 (валидация), извне при включённом флаге — 403.
|
||
|
||
Отключение: `REGISTRATION_LAN_ONLY_ENABLED=false` и перезапуск Django/деплой.
|
||
Это снова разрешит регистрацию извне; другие настройки авторизации не меняются.
|
||
|
||
Основа реализации: [DRF permissions](https://www.django-rest-framework.org/api-guide/permissions/)
|
||
и [nginx proxy_add_x_forwarded_for](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#var_proxy_add_x_forwarded_for).
|