Web Limits Wildcard in Docs

This commit is contained in:
Alexey
2026-10-28 21:29:00 +03:00
parent 4eef3d8608
commit 1870ef82a9
3 changed files with 221 additions and 34 deletions
+98 -14
View File
@@ -27,6 +27,7 @@
- [web](#web)
- [web.debug](#webdebug)
- [web.limits](#weblimits)
- [web.limits.wildcard](#weblimitswildcard)
- [web.timeouts](#webtimeouts)
- [web.vhosts](#webvhosts)
- [web.vhosts.decoy](#webvhostsdecoy)
@@ -332,6 +333,7 @@
data_path = "/var/lib/telemt"
```
## quota_state_path
- **Периодические квоты**: Файл также хранит календарные отметки UTC по пользователям. Если [`user_quota_periods`](#user_quota_periods) не пуст, некорректное существующее состояние останавливает запуск; отсутствие файла создаёт новое состояние. Для завершения запуска также обязательна успешная первоначальная запись состояния на диск.
- **Ограничения / валидация**: `Path`. Относительные пути разрешаются от рабочего каталога процесса.
- **Описание**: JSON-файл состояния для сохранения runtime-расхода квот по пользователям.
- **Пример**:
@@ -696,6 +698,7 @@
direct_relay_copy_buf_c2s_bytes = 65536
```
## direct_relay_copy_buf_s2c_bytes
- **Сброс буфера**: В обоих направлениях direct adaptive copy продвигает сброс накопленного вывода, когда источник переходит в ожидание, не дожидаясь EOF или следующего входящего пакета. Формирование TLS-записей, настроенные пределы буферов и контроль квот сохраняются.
- **Ограничения / валидация**: Должно быть в пределах `8192..=2097152` (байт).
- **Описание**: CoРазмер буфера копирования для направления DC > клиент в режиме прямой пересылки (direct relay).
- **Пример**:
@@ -1119,6 +1122,9 @@
disable_colors = false
```
## me_socks_kdf_policy
Корректные публичные SOCKS `BND.ADDR/BND.PORT` с ненулевым портом задают полный исходный кортеж KDF и имеют приоритет над настроенными или обнаруженными NAT-данными. Семейство должно совпадать с логическим ME-назначением, независимо от семейства TCP-соединения к SOCKS. IPv4-mapped IPv6 назначения считаются IPv4 и требуют обычного IPv4 BND; IPv4-compatible назначения остаются IPv6. `strict` отклоняет отсутствующий или некорректный BND, `compat` сохраняет прежний fallback. Поля и порты KDF, кодирование назначения SOCKS CONNECT, проверка публичного адреса и STUN не меняются.
- **Ограничения / валидация**: `"strict"` или `"compat"`.
- **Описание**: Политика fallback-поведения KDF, привязанная к SOCKS, для Middle-End-handshake.
- **Пример**:
@@ -2063,6 +2069,9 @@
proxy_protocol_trusted_cidrs = ["127.0.0.1/32", "10.0.0.0/8"]
```
## metrics_port
Если задан только `metrics_port`, Telemt пытается слушать IPv4 и IPv6 loopback. `metrics_listen` заменяет этот вариант привязки. Одной публикации Docker-порта недостаточно для listener на loopback: задайте адрес, доступный через сеть контейнера, и добавьте непосредственный адрес сборщика метрик или Docker peer в whitelist.
- **Ограничения / валидация**: `u16` (необязательный параметр).
- **Описание**: Порт для Prometheus-совместимого endpoint’а метрик. При задании включает прослушивание метрик (поведение прослушивания может быть переопределено через `metrics_listen`).
- **Пример**:
@@ -2081,6 +2090,9 @@
metrics_listen = "127.0.0.1:9090"
```
## metrics_whitelist
Авторизация проверяет IP непосредственного peer в исходном виде и, для IPv4-mapped IPv6, в каноническом IPv4-виде. Явные IPv6 CIDR продолжают действовать. Forwarded-заголовки не предоставляют доступ.
- **Ограничения / валидация**: `IpNetwork[]`.
- **Описание**: Белый список CIDR для доступа к endpoint’у метрик.
- **Пример**:
@@ -2462,6 +2474,9 @@
synlimit_burst = 24
```
## synlimit_ios_seconds (server.listeners)
Это эвристика по форме пакета, а не достоверное распознавание устройств Apple. IPv4 SYN длиной `60` попадает в общий bucket; универсального исправления iOS или восстановления после фонового режима здесь нет.
- **Ограничения / валидация**: `u32`, должно быть `> 0`. Значение по умолчанию: `1`.
- **Описание**: Token-bucket interval для SYN packets, совпавших с iOS-like classifier. IPv4 match: packet length `64` и TTL `< 65`; IPv6 match: packet length `84` и hop limit `< 65`.
- **Пример**:
@@ -2595,6 +2610,7 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| Ключ | Тип | По умолчанию | Hot-Reload |
| --- | --- | --- | --- |
| `enabled` | `bool` | `false` | `✔` |
| `allow_wildcard_hosts` | `bool` | `false` | `✘` |
| `carrier` | `"https"`, `"https-lanes"`, `"websocket"` или `"websocket-lanes"` | `"https"` | `✔` |
| `carriers` | `false` или непустой массив уникальных carrier | `false` | `✔` |
| `carrier_learning` | `bool` | `true` | `✔` |
@@ -2679,11 +2695,11 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| `control_bytes_global` | `usize` | `16777216` | Process-wide резерв только для control frames. |
| `max_bootstraps_global` | `usize` | `512` | Активные bootstrap credentials на весь процесс. |
| `max_bootstraps_per_ip` | `usize` | `64` | Активные bootstrap credentials на один client IP. |
| `max_vhosts` | `usize` | `8` | Настроенные WEB virtual hosts. |
| `max_profiles` | `usize` | `32` | WEB-профили всех vhosts. |
| `max_static_files` | `usize` | `4096` | Элементы static snapshot всех vhosts. |
| `max_vhosts` | `usize` | `8` | Точные имена WEB-доменов во всех группах; каждое точное имя в `hosts` учитывается отдельно. Шаблоны учитывает `max_rules`. |
| `max_profiles` | `usize` | `32` | Точные пары домен/профиль и один общий набор профилей на каждую группу wildcard. |
| `max_static_files` | `usize` | `4096` | Записи статических снимков всех групп; общий снимок группы учитывается один раз. |
| `max_static_file_bytes` | `usize` | `8388608` | Максимальный размер одного статического файла. |
| `max_static_bytes` | `usize` | `67108864` | Размер static snapshots всех vhosts. |
| `max_static_bytes` | `usize` | `67108864` | Байты статических снимков всех групп; общий снимок группы учитывается один раз. |
| `debug_records_capacity` | `usize` | `65536` | Максимальное число сохранённых WEB debug records. |
| `debug_bytes_global` | `usize` | `67108864` | Глобальная байтовая граница сохранённых и находящихся в обработке WEB debug данных; минимум 4096. |
| `memory_envelope_bytes` | `usize` | `1342177280` | Заявленный envelope для HTTP heads, bodies, общих queues/WebSocket I/O, состояния lanes, carrier learning, static snapshots и bounded debug/status buffers; максимум 4 GiB. |
@@ -2719,31 +2735,76 @@ WEB-режим переносит MTProxy-трафик Telegram Desktop внут
| `carrier_negotiation_deadlines_secs` | `[u64; 4]` | `[3, 5, 8, 12]` | `✔` | Строго возрастающие cumulative offsets: bridge применяет их перед первым запросом `/session`, сервер — при приёме первой automatic attempt. Checkpoints для одного—четырёх кандидатов: `[d3]`, `[d0, d3]`, `[d0, d1, d3]` и `[d0, d1, d2, d3]`; последний кандидат всегда использует `d3`. |
| `carrier_learning_secs` | `u64` | `600` | `✔` | Фиксированный срок двух process-local окон evidence; диапазон `2..=86400`. |
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Срок неиспользованного bootstrap и replay-marker закрытого token. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Максимальная неактивность carrier до закрытия сессии. |
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Максимальная подтверждённая неактивность peer до закрытия сессии; пустые опросы и активность только со стороны backend не продлевают этот срок. |
| `http_idle_secs` | `u64` | `75` | `✔` | Лимит простоя между HTTP-обменами и при отсутствии прогресса уже выданного response body. Явно ограниченные фазы request body, long poll, decoy и ожидания Upgrade сохраняют собственные deadlines и не обрываются этим таймером. Значение фиксируется при приёме connection. |
| `http_overload_timeout_ms` | `u64` | `250` | `✔` | Deadline каждой фазы в миллисекундах для ожидания capacity или записи повторяемого ответа после принятия перегруженного socket; диапазон `1..=60000`. Timeout ожидания и запись ответа получают не более одного бюджета фазы каждый. |
| `shutdown_secs` | `u64` | `15` | `✔` | Один абсолютный бюджет завершения процесса, общий для всех listener acceptors и connections, а также для WEB sessions и auxiliary tasks. Активное значение фиксируется один раз при начале shutdown. |
| `decoy_header_secs` | `u64` | `30` | `✔` | Deadline подключения и получения response head от HTTP decoy. |
| `decoy_resolve_secs` | `u64` | `5` | `✔` | Таймаут DNS-запроса для каждой уникальной пары имя/порт: `1..=3600` секунд; не общий срок подготовки и не таймаут HTTP-обмена. |
# [web.limits.wildcard]
Эти лимиты фиксируются на срок жизни процесса и применяются к wildcard-доменам.
См. [правила развёртывания и безопасности](../WEB/WEB_PROXY.ru.md#wildcard-хосты).
Frontend должен ограничивать Host: неизвестные или вытесненные чужие capability
могут попасть в decoy. Консервативное маскирование по форме применяется только
к диагностическим копиям.
| Ключ | Тип | По умолчанию | Значение |
| --- | --- | --- | --- |
| `max_rules` | `usize` | `32` | Общее число суффиксных селекторов и catch-all. |
| `max_profiles_per_request` | `usize` | `256` | Предел полного перебора группы wildcard и стоимости холодного HMAC. |
| `cache_hosts` | `usize` | `256` | Записи положительного FIFO-кэша; `0` отключает хранение. |
| `cache_bytes` | `usize` | `8388608` | Байты записей и индексов, включая вытесненные записи, ещё используемые запросами. |
| `cold_hmac_per_minute` | `u32` | `1000000` | Устойчивый бюджет холодных HMAC-вычислений на процесс. |
| `cold_hmac_burst` | `u32` | `65536` | Допустимый всплеск вычислений, не меньше `max_profiles_per_request`. |
При включённом wildcard все пределы, кроме `cache_hosts`, положительны. Холодный
запрос атомарно резервирует бюджет для всех профилей группы; при нехватке сразу
возвращается HTTP 503. Кэш заполняется только успешной выдачей bootstrap.
Аутентифицированные carriers обходят этот кэш и холодный бюджет. Вытеснение не
меняет квоты. Снимки runtime отделяют записи кэша при любом виде reload.
Дополнительные проверяемые резервы памяти действуют только в wildcard-режиме;
со стандартными лимитами нужно не менее 1290 MiB вместо прежних 1280 MiB.
`memory_envelope_bytes = 1610612736` задаёт для примера бюджет 1536 MiB.
# [[web.vhosts]]
| Ключ | Тип | Обязательный | Hot-Reload | Описание |
| --- | --- | --- | --- | --- |
| `host` | `String` | да | `✔` | Уникальный канонический lowercase ACE FQDN без порта, пути, credentials и завершающей точки. |
| `host` | `String` | один селектор | `✔` | Точное имя или разрешённый wildcard; несовместим с `hosts`. |
| `hosts` | `String[]` | один селектор | `✔` | Непустой список селекторов с общими profiles, decoy, `base_path` и `public_addr`. |
| `base_path` | `String` | нет | `✔` | Точный регистрозависимый WEB-prefix без начального и завершающего слеша; по умолчанию пуст. Не более 128 ASCII-байт в разделённых слешами сегментах `[A-Za-z0-9][A-Za-z0-9_-]*`. |
| `public_addr` | `SocketAddr` | да | `✔` | Конкретный публичный IP на порту `443`, используемый во внутреннем destination tuple relay. |
| `decoy` | таблица | да | `✔` | Обычный сайт для неаутентифицированного или некорректного трафика. |
| `profiles` | массив таблиц | при включённом WEB | `✔` | Явные пользователи и client secret modes для этого hostname. |
| `profiles` | массив таблиц | при включённом WEB | `✔` | Явные пользователи и режимы secret, доступные независимо на каждом домене группы. |
Hostname нормализуется при валидации и должен приниматься Telegram Desktop. Пустой `base_path` сохраняет root-capability v1 и прежний шестнадцатеричный secret ссылки. Непустой путь использует capability v2 по host/path и Telegram Desktop path-ссылку с percent-encoded `HOST/BASE` и base64url-маркером secret `0x70`. Маршрутизация требует точного prefix с завершающим слешем и никогда не перенаправляет, не нормализует и не удаляет его. Bootstrap является bearer credential: адрес клиента и его IP-семейство могут измениться до создания session. Неиспользованный bootstrap остаётся действительным после reload конфигурации, только пока активен профиль с той же identity.
Нужен ровно один селектор `host` или `hosts`, даже при выключенном WEB. Пустые
селекторы и одновременное указание обоих запрещены. Каждое имя должно быть
поддерживаемым Telegram Desktop FQDN, не IP-адресом, без порта, пути, credentials
и завершающей точки. При проверке оно нормализуется в lowercase ACE; дубли внутри
групп и между ними запрещены. Для `*.suffix` и `*` требуется
`web.allow_wildcard_hosts = true`, вступающий в силу после перезапуска. Приоритет:
точное имя, самый длинный суффикс, catch-all. Суффикс охватывает потомков любой
глубины, но не сам домен. Встроенные wildcard и regex не поддерживаются.
Нормализованная конфигурация сохраняет выбранный селектор; при сериализации
списки не разворачиваются. Пустой `base_path` сохраняет capability v1 и прежний
hexadecimal secret ссылки. Непустой путь использует capability v2 для host/path
и ссылку Telegram Desktop с percent-encoded `HOST/BASE` и маркером base64url
secret `0x70`. Маршрутизация требует точного префикса с завершающим слешем и не
перенаправляет, не нормализует и не удаляет его. Bootstrap — bearer credential:
до создания сессии IP клиента и семейство адреса могут измениться. Неиспользованный
bootstrap переживает reload только при сохранении активной идентичности профиля.
# [web.vhosts.decoy]
Для `http_upstream` параметр `resolve` принимает `"never"` (по умолчанию) или `"startup"`. По умолчанию нужен IP-адрес из loopback, link-local или private диапазона. `startup` также разрешает DNS-имена: асинхронная подготовка конфигурации разрешает каждую уникальную пару имя/порт, проверяет все ответы по допустимым классам адресов и действующим WEB listeners и закрепляет первый адрес в порядке ответа resolver. Ошибка, пустой ответ, публичный адрес или прямая петля к listener отклоняют подготовку. В upstream authority сохраняется настроенное имя. DNS при обработке запросов и автоматического перехода к другим ответам нет; reload готовит новый снимок. `web.timeouts.decoy_resolve_secs` ограничивает отдельный DNS-запрос для каждой уникальной пары имя/порт. Запросы выполняются последовательно, общего таймаута подготовки нет. Косвенные петли через другие прокси оператор должен исключить самостоятельно.
Обязателен ровно один decoy mode:
| Mode | Обязательные ключи | Валидация |
| --- | --- | --- |
| `http_upstream` | `upstream` | `http://` origin с loopback, link-local или private IP literal; без credentials, path, query и fragment. |
| `http_upstream` | `upstream` | `http://` origin без credentials, пути, query и fragment. По умолчанию private IP literal; DNS-имя разрешено только с `resolve = "startup"`. |
| `static_directory` | `directory`; необязательный `index = "index.html"` | Абсолютный реальный каталог и одно безопасное имя index-файла. Symlinks и выход за пределы каталога запрещены; immutable snapshot загружается в пределах `[web.limits]`. |
# [[web.vhosts.profiles]]
@@ -2752,18 +2813,20 @@ Hostname нормализуется при валидации и должен п
| --- | --- | --- | --- | --- |
| `user` | `String` | да | — | Существующий ключ длиной 1–64 байта из `[access.users]`; ограничение сохраняет bounded runtime-status и фильтры. |
| `secret_mode` | `"plain"` или `"dd"` | да | — | Точное представление секрета для Telegram Desktop. `ee` не поддерживается. |
| `max_sessions` | `usize` | нет | `web.limits.max_sessions_global` | Активные сессии этого профиля. |
| `max_streams` | `usize` | нет | `web.limits.max_streams_global` | Активные logical streams этого профиля. |
| `max_sessions` | `usize` | нет | `web.limits.max_sessions_global` | Живые сессии профиля на каждом конкретном домене. |
| `max_streams` | `usize` | нет | `web.limits.max_streams_global` | Живые логические потоки профиля на каждом конкретном домене. |
| `max_streams_per_session` | `usize` | нет | `web.limits.max_streams_per_session` | Активные logical streams в одной сессии профиля. |
Лимиты профиля должны быть ненулевыми и не превышать соответствующие глобальные границы. Повторяющиеся профили `(user, secret_mode)` в одном vhost запрещены.
Пределы профиля должны быть положительными и не превышать соответствующие глобальные лимиты. Повторные `(user, secret_mode)` внутри группы запрещены. У каждого конкретного домена отдельная идентичность квоты; ограничения процесса, IP и пользователя доступа продолжают действовать. Максимумы профиля не выделяют сессии или потоки заранее. Группа разделяет неизменяемую политику и один снимок decoy, а привязанные к домену capability и ограниченные индексы защиты от утечки остаются раздельными.
## Lifecycle WEB и управление через API
До добавления шаблонов через API в действующем процессе уже должен быть включён `web.allow_wildcard_hosts`. Запись желаемого флага не включает шаблоны немедленно; преждевременное обновление отклоняется атомарно до перезапуска.
- Config watcher и generation reload применяют `web.enabled`, policy carrier/negotiation, `web.debug`, `web.timeouts`, vhosts, profiles и decoy snapshots без перезапуска процесса. Валидируется и активируется один immutable expanded source snapshot; watcher candidate generation запускается только после активации этого поколения. Существующие сессии и начатые negotiation chains сохраняют issuance-time carrier candidates, limits, timeouts и абсолютные deadlines; новые bridge sessions используют одно зафиксированное активное поколение.
- Изменение `base_path` атомарно заменяет и маршрут новых запросов, и производную capability. Сначала выпустите новые ссылки и завершите затронутые активные sessions: установленные WebSockets и уже маршрутизированные обмены продолжаются; последующие запросы к старому base с подлинным для процесса bootstrap- или session-token получают локальный no-store `404`, а ставшая неактивной прежняя capability обрабатывается как обычный decoy traffic.
- Состав WEB-listeners и их trust policy в `server.listeners`, а также все значения `web.limits` принадлежат процессу и требуют перезапуска.
- `GET /v1/config` возвращает полное авторское дерево `[web]`, кроме производного snapshot `web.runtime`. `PATCH /v1/config` принимает sparse object `web`, глубоко сливает tables, целиком заменяет arrays, валидирует полный candidate и указывает `web.limits` в `deferred_process_fields` до перезапуска.
- Состав WEB-listeners и их trust policy в `server.listeners`, флаг `web.allow_wildcard_hosts` и все значения `web.limits` принадлежат процессу и требуют перезапуска.
- `GET /v1/config` возвращает полное авторское дерево `[web]`, кроме производного snapshot `web.runtime`. `PATCH /v1/config` принимает sparse object `web`, глубоко сливает tables, целиком заменяет arrays, валидирует полный candidate и указывает `web.allow_wildcard_hosts`, `web.limits` в `deferred_process_fields` до перезапуска.
- `GET /v1/runtime/web/status`, `/sessions`, `/sessions/{session_ref}` и `/operations/{operation_id}` предоставляют bounded несекретное runtime-состояние. POST controls закрывают выбранные сессии, очищают debug или сбрасывают carrier learning и требуют текущий случайный `runtime_instance`.
- `web.enabled = false` после активации прекращает новую выдачу bootstrap/session credentials, но не закрывает активные сессии. Для close-all дождитесь `manager.issuance_enabled = false`, отправьте асинхронный selector `all` и опрашивайте его operation.
- Существующих access users можно создавать, изменять, ротировать, включать, выключать и удалять через `/v1/users`. Создание пользователя не добавляет WEB-профиль. Отключение пользователя немедленно обновляет admission и завершает его активные сессии.
@@ -3446,6 +3509,7 @@ Hostname нормализуется при валидации и должен п
| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | `✔` |
| [`user_expirations`](#user_expirations) | `Map<String, DateTime<Utc>>` | `{}` | `✔` |
| [`user_data_quota`](#user_data_quota) | `Map<String, u64>` | `{}` | `✔` |
| [`user_quota_periods`](#user_quota_periods) | `Map<String, "daily" / "weekly" / "monthly">` | `{}` | `✔` |
| [`user_max_unique_ips`](#user_max_unique_ips) | `Map<String, usize>` | `{}` | `✔` |
| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | `✔` |
| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | `✔` |
@@ -3531,6 +3595,26 @@ Hostname нормализуется при валидации и должен п
# Alice receives a 1 GiB quota.
alice = 1073741824
```
## user_quota_periods
- **Ограничения / валидация**: Каждый ключ должен соответствовать существующему `access.users` с положительным `access.user_data_quota`. Значения строго `"daily"`, `"weekly"` или `"monthly"`. Если таблица не пуста, общее число настроенных пользователей не должно превышать 65 536.
- **По умолчанию**: Пустая таблица. Без периода сохраняется прежняя квота с ручным сбросом и без автоматического обновления.
- **Календарь**: Все границы в UTC: ежедневно в полночь, еженедельно в понедельник в полночь, ежемесячно первого числа в полночь. Месяц календарный, а не фиксированное число дней.
- **Активация**: Включение или смена периода сохраняет расход и запускает автоматические сбросы со следующей границы. Удаление периода сохраняет расход и записанную календарную отметку. Hot reload применяет политику без смены secret и перезапуска процесса.
- **Жизненный цикл**: Ротация secret сохраняет расход и календарь. У отключённых пользователей и пользователей с истёкшим сроком календарь продолжает работать, но доступ не возобновляется. Удаление и повторное создание начинают учёт заново; CREATE требует успешной очистки сохранённого состояния до записи нового пользователя, в том числе без периодической квоты. Ручной `reset-quota` не сдвигает границы UTC. Откат часов не повторяет уже обработанный сброс, даже после удаления и возврата периода.
- **Хранение**: Worker записывает всю группу сбросов до публикации новых счётчиков. При ошибке записи расход не сбрасывается и календарные отметки не продвигаются; текущий трафик продолжает учитываться. Повторы используют монотонные задержки 1, 2, 4, 8, 16, 32, затем 60 секунд. После задержки или перезапуска выполняется один сброс к последнему наступившему периоду, без воспроизведения каждого пропущенного интервала. Запись только метаданных не затирает трафик, накопленный во время записи.
- **Запуск**: При включённых периодах существующий нечитаемый, некорректный, слишком большой или неподдерживаемый календарный checkpoint останавливает запуск. Отсутствующий файл создаёт новое состояние; корректный прежний формат сохраняет расход и получает календарные метаданные. Пределы файла — 16 MiB и 65 536 записей. Даже при корректном или отсутствующем файле первоначальная запись состояния должна завершиться успешно. Храните `general.quota_state_path` на постоянном носителе, доступном для записи.
- **Учёт**: Сброс заменяет поколение учёта; уже выданные relay-резервы остаются в своём поколении. Возврат старого резерва не уменьшает расход нового периода. Накопительная телеметрия трафика не меняется. Журнал предварительной записи трафика не добавляется: сохраняется прежний риск потери расхода после последнего checkpoint при аварии.
- **API**: Create/PATCH пользователя принимает `quota_period`; статус различает желаемую конфигурацию и действующий сохранённый календарь. См. [контракт API](../Architecture/API/API.md#recurring-quota-periods).
- **Пример**:
```toml
[access.user_data_quota]
alice = 1073741824
[access.user_quota_periods]
alice = "monthly"
```
## user_max_unique_ips
- **Ограничения / валидация**: `Map<String, usize>`.
- **Описание**: Ограничение на количество уникальных IP-адресов, с которых выполняется подключение, для каждого пользователя.