mirror of
https://github.com/telemt/telemt.git
synced 2026-10-31 21:02:01 +03:00
WEB: Common Profiles for multiple vhosts
This commit is contained in:
+170
-6
@@ -115,8 +115,170 @@ max_streams_per_session = 64
|
||||
|
||||
`decoy_fasttrack_mode` управляет только capability processing для `GET/HEAD` на настроенном base root. Значение `off` по умолчанию сохраняет полный прежний scan без fast-track counters. `shadow` учитывает, какие структурно невозможные запросы могли бы обойти scan, но всё равно выполняет его полностью. `enforce` обходит capability work только для `HEAD` либо отсутствующего или неканонического query `bridge`. Каждый точный канонический bridge GET на base root выполняет полный scan всех профилей выбранного vhost и при совпадении, и при промахе. Настройка требует перезапуска процесса: reload сохраняет желаемое значение, но сообщает `web.decoy_fasttrack_mode` как deferred. Fast-track не защищает от злонамеренной CPU-нагрузки, потому что scanner всегда может отправлять канонические candidates; кроме того, `enforce` может создать публично наблюдаемый timing class формы запроса, особенно при статическом decoy. Не включайте этот режим без внешних timing measurements через production TLS-терминатор.
|
||||
|
||||
## Общие профили для нескольких доменов
|
||||
|
||||
Замените селектор `host` непустым списком `hosts`, чтобы несколько доменов
|
||||
использовали общие определения профилей и один снимок decoy:
|
||||
|
||||
```toml
|
||||
[[web.vhosts]]
|
||||
hosts = ["proxy.example.com", "backup.example.com"]
|
||||
base_path = "telegram/web"
|
||||
public_addr = "203.0.113.10:443"
|
||||
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://127.0.0.1:18081"
|
||||
|
||||
[[web.vhosts.profiles]]
|
||||
user = "web-user"
|
||||
secret_mode = "dd"
|
||||
max_sessions = 8
|
||||
max_streams = 512
|
||||
max_streams_per_session = 64
|
||||
```
|
||||
|
||||
Этот фрагмент дополняет пользователя доступа и WEB listener из предыдущего
|
||||
примера. Укажите ровно один селектор: `host` или `hosts`. При загрузке имена
|
||||
нормализуются; совпадающие после нормализации имена запрещены. Шаблоны wildcard
|
||||
и `*` требуют явного включения режима, описанного ниже. У всех доменов группы
|
||||
общие `base_path`, `public_addr`, профили и decoy. Если эти настройки различаются,
|
||||
создайте отдельные группы. TLS-терминатор должен обслуживать каждый домен из
|
||||
списка и передавать исходный канонический `Host`.
|
||||
|
||||
У каждого домена свои capability, bootstrap/session credentials и квота профиля.
|
||||
В примере профиль допускает до восьми сессий на каждом домене с учётом общих
|
||||
ограничений процесса, IP и пользователя доступа. При запуске ссылки формируются
|
||||
отдельно для каждого выбранного пользователя/профиля и конкретного домена.
|
||||
Группировка не перенаправляет клиентов и не позволяет переносить credentials
|
||||
между доменами.
|
||||
|
||||
В режиме точных имён `max_vhosts` учитывает каждый домен, а `max_profiles` —
|
||||
каждую пару домен/профиль. Для восьми доменов со 100 профилями нужны пределы
|
||||
не менее восьми vhosts и 800 profiles. Статические файлы и байты снимка одной
|
||||
группы учитываются один раз; у отдельных групп снимки отдельные. Настроенные
|
||||
максимумы сессий не резервируют сессии заранее. Общая неизменяемая политика
|
||||
уменьшает дублирование, но индексы capability растут с числом пар домен/профиль.
|
||||
|
||||
Сначала установите новую сборку с прежней конфигурацией одиночных `host`, затем
|
||||
объедините одинаково настроенные записи через reload. Группировка и перестановка
|
||||
доменов сохраняют совместимые bootstrap credentials, идентичность квот и данные
|
||||
обучения профиля. Удаление домена запрещает новые запросы к нему, не вводя новых
|
||||
правил завершения существующих соединений. Увеличение лимитов требует перезапуска
|
||||
и должно вступить в силу до добавления записей сверх действующих пределов.
|
||||
Перед откатом на сборку без поддержки `hosts` разверните группы в одиночные
|
||||
записи `host` и повторно проверьте бюджет статических снимков.
|
||||
|
||||
## Wildcard-хосты
|
||||
|
||||
По умолчанию используются точные `host` / `hosts`. Для произвольных поддоменов
|
||||
установите `web.allow_wildcard_hosts = true` и перезапустите Telemt. После этого
|
||||
селекторы также принимают `*.example.com` и `*`; точные имена и шаблоны можно
|
||||
смешивать в одном списке:
|
||||
|
||||
```toml
|
||||
[web]
|
||||
enabled = true
|
||||
allow_wildcard_hosts = true
|
||||
|
||||
[web.limits]
|
||||
# Additional wildcard reservations must fit the declared envelope.
|
||||
memory_envelope_bytes = 1610612736
|
||||
|
||||
[web.limits.wildcard]
|
||||
max_rules = 32
|
||||
max_profiles_per_request = 256
|
||||
cache_hosts = 256
|
||||
cache_bytes = 8388608
|
||||
cold_hmac_per_minute = 1000000
|
||||
cold_hmac_burst = 65536
|
||||
|
||||
[[web.vhosts]]
|
||||
hosts = ["proxy.example.com", "*.example.com"]
|
||||
base_path = "relay"
|
||||
public_addr = "203.0.113.10:443"
|
||||
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://127.0.0.1:8080"
|
||||
|
||||
[[web.vhosts.profiles]]
|
||||
user = "web-user"
|
||||
secret_mode = "dd"
|
||||
```
|
||||
|
||||
Этот пример дополняет ранее показанные listener и пользователя доступа. Запрос
|
||||
выбирает одну группу: сначала точное имя, затем самый длинный подходящий суффикс,
|
||||
затем `*`. Суффикс охватывает потомков любой глубины, включая `a.b.example.com`,
|
||||
но не сам `example.com`. Неуспешная аутентификация не переключает запрос на
|
||||
другое правило. Встроенные wildcard и регулярные выражения запрещены, как и
|
||||
дубли селекторов после нормализации между группами. Выбранная группа сохраняет
|
||||
свои фиксированные `public_addr`, базовый путь, профили и decoy. Заголовок Host
|
||||
не выбирает адрес upstream.
|
||||
|
||||
В этом режиме HTTP Host нормализуется без учёта регистра ASCII; удаляется только
|
||||
необязательный буквальный порт `:443`. Повторные Host, IP-адреса вместо домена,
|
||||
некорректные DNS/ACE-метки, завершающая точка и другие порты запрещены.
|
||||
`X-Forwarded-Host` не используется. Capability вычисляется по фактическому
|
||||
каноническому имени с существующим клиентским алгоритмом; менять клиентов не
|
||||
нужно. Квоты сессий/потоков/профиля остаются привязаны к конкретной тройке
|
||||
`(host, user, secret_mode)`. Ограничения процесса, IP и пользователя доступа
|
||||
продолжают действовать. Ссылки при запуске выводятся только для точных имён.
|
||||
|
||||
Frontend **обязан ограничивать допустимые Host**, а незашифрованный WEB backend
|
||||
должен оставаться в закрытой сети. Сертификаты TLS должны покрывать каждое
|
||||
фактическое имя клиента. Это явная граница безопасности: capability неизвестного
|
||||
или вытесненного из кэша wildcard-домена, отправленная на другой домен, может
|
||||
попасть в его decoy, в том числе на точный домен смешанной конфигурации.
|
||||
Capability текущего домена, конечного списка точных доменов, известного
|
||||
положительного кэша и подлинные bootstrap/session tokens процесса не передаются
|
||||
в decoy в URI и заголовках, включая percent-encoded формы. Публичные URL, cookies
|
||||
и заголовки не отклоняются лишь за сходство с credentials. Диагностические копии,
|
||||
напротив, консервативно маскируют похожие строки, включая обрезанные префиксы;
|
||||
байты публичных запросов/ответов и исходные хеши User-Agent не меняются. Поэтому
|
||||
диагностика может маскировать и обычные длинные строки.
|
||||
|
||||
Перед вычислениями холодный запрос атомарно резервирует HMAC-бюджет для всех
|
||||
профилей выбранной группы. При нехватке сразу возвращается HTTP 503 с no-store,
|
||||
без очереди и частичного перебора. В пределах запроса одна таблица используется
|
||||
для защиты от утечки credentials и аутентификации bridge. Попадание в кэш не
|
||||
требует вычислений. Аутентифицированные carriers полностью обходят кэш таблиц
|
||||
capability и их вычисление. Режимы fast-track Off/Shadow/Enforce по-прежнему
|
||||
управляют проверкой корня; ошибочно размещённые credentials текущего домена
|
||||
удерживаются локально в любом режиме.
|
||||
|
||||
FIFO-кэш заполняется только после успешной выдачи bootstrap для bridge/recovery.
|
||||
`cache_hosts = 0` отключает хранение. Пределы количества записей и байтов независимы;
|
||||
вытесненная запись продолжает занимать байтовый бюджет, пока запрос удерживает
|
||||
ссылку на неё. Неудача вставки не отменяет корректный bootstrap; вытеснение не
|
||||
сбрасывает квоты. Ключ кэша содержит уникальный идентификатор неизменяемого WEB
|
||||
снимка, в том числе при reload внутри одного поколения runtime. Записи не
|
||||
удерживают decoy или поколение runtime.
|
||||
|
||||
`max_vhosts` учитывает точные имена; `max_profiles` — точные привязки и один
|
||||
общий набор профилей на каждую группу с wildcard. `max_rules` ограничивает число
|
||||
шаблонов, а `max_profiles_per_request` — полный перебор группы wildcard. Все
|
||||
пределы, кроме `cache_hosts`, должны быть положительными; HMAC burst должен
|
||||
вмещать полную таблицу запроса. Проверяемый бюджет памяти включает шаблоны/правила,
|
||||
кэш, рабочую память обработчиков и живые привязки доменов только при включённом
|
||||
wildcard. С остальными стандартными лимитами прежних 1280 MiB недостаточно;
|
||||
1290 MiB достаточно по текущему расчёту, а пример задаёт 1536 MiB. Бюджет и
|
||||
обработка режима только с точными именами не меняются.
|
||||
|
||||
Флаг включения и все `web.limits` требуют перезапуска. После включения селекторы
|
||||
и профили можно менять через reload. API может сохранить флаг и достаточные
|
||||
лимиты как отложенные изменения. Добавление шаблонов до фактического перезапуска
|
||||
отклоняется атомарно, даже если флаг уже записан в желаемой конфигурации. Для
|
||||
отключения сначала замените шаблоны точными именами, затем перезапустите процесс
|
||||
с выключенным флагом. Перед откатом на старую сборку удалите wildcard-селекторы,
|
||||
новый флаг и таблицу лимитов.
|
||||
|
||||
## Server-side negotiation carrier
|
||||
|
||||
Для `websocket-lanes` отказ или таймаут до Upgrade отдельной lane запускает общее восстановление сессии. Сбой после Upgrade остаётся локальным для lane. Это свойство протокола не подтверждает совместимость восстановления нативных macOS/iOS клиентов после фонового режима.
|
||||
|
||||
Неизменяемая основа bridge кэшируется, а nonce, bootstrap, host и base path подставляются заново для каждого ответа. Обработка истечения сроков использует консервативные оценки ближайших сроков, не продлевая сроки. Эти внутренние оптимизации не меняют форматы протокола, лимиты приёма или таймауты и сами по себе не доказывают сокращения полного времени подключения.
|
||||
|
||||
Auto-negotiation необязателен и выключен, пока `carriers` не задан явным непустым массивом. Настроенный `carrier` остаётся последним fallback и добавляется ровно один раз, даже если уже присутствует в массиве:
|
||||
|
||||
```toml
|
||||
@@ -175,7 +337,9 @@ WebSocket codec buffers и находящиеся в обработке read/wri
|
||||
|
||||
Для WEB-listener обязательны `proxy_protocol = false` и `reuse_allow = false`. В нём нельзя использовать `client_mss`, `synlimit`, `announce` и `announce_ip`. Массив `web_trusted_proxy_cidrs` должен быть непустым и содержать только непосредственные адреса NGINX или HAProxy; сети `/0` запрещены.
|
||||
|
||||
HTTP decoy origin должен быть loopback, link-local или private IP literal. Для обычных запросов Telemt сохраняет method, path, query, headers, streamed body, response status, headers и body, удаляя hop-by-hop headers. Перед отправкой некорректного carrier-запроса в decoy Telemt удаляет из него carrier credentials и body. Literal decoy endpoint отклоняется, если он точно совпадает с effective WEB-listener либо покрывается wildcard address того же IP-семейства на том же порту. Косвенные loops через DNS, NGINX, HAProxy или другой forwarding layer нельзя доказать из конфигурации Telemt; оператор обязан исключить их самостоятельно.
|
||||
Для `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-запрос для каждой уникальной пары имя/порт. Запросы выполняются последовательно, общего таймаута подготовки нет. Косвенные петли через другие прокси оператор должен исключить самостоятельно.
|
||||
|
||||
В обычных запросах сохраняются метод, путь, query, заголовки, потоковое тело, статус и тело ответа, кроме hop-by-hop заголовков. Из некорректных carrier-запросов перед передачей в decoy удаляются credentials и тело.
|
||||
|
||||
Вместо origin можно использовать immutable snapshot статического сайта:
|
||||
|
||||
@@ -307,7 +471,7 @@ proxy.example.com {
|
||||
}
|
||||
|
||||
handle {
|
||||
# обычный сайт
|
||||
# ordinary site
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -335,7 +499,7 @@ log {
|
||||
| Конфигурация | Поведение runtime |
|
||||
| --- | --- |
|
||||
| Состав WEB-listeners, bind address и trust policy | Принадлежат процессу; перезапустите Telemt. |
|
||||
| Любое значение `[web.limits]` | Process-owned контракт памяти и ресурсов; перезапустите Telemt. |
|
||||
| `web.allow_wildcard_hosts` и любое значение `[web.limits]` | Process-owned контракт памяти и ресурсов; перезапустите Telemt. |
|
||||
| `web.enabled`, policy carrier/negotiation, `web.debug`, timeouts, vhosts, profiles и decoys | Применяются config watcher или runtime generation reload. |
|
||||
| Изменение `base_path` vhost | Атомарно переключает маршрутизацию новых HTTP-запросов и derivation capability. Выпустите новую сгенерированную ссылку. Уже upgraded WebSockets и начатые маршрутизированные обмены продолжаются. Последующие запросы к старому base с подлинным для процесса bootstrap- или session-token получают локальный no-store `404`, а ставшая неактивной прежняя capability обрабатывается как обычный decoy traffic. Существующий session bearer остаётся пригодным только на новом точном base, а неиспользованный bootstrap, выданный для старой capability, не может создать session на новом base. |
|
||||
| Operator pause/drain state | Process-owned и ephemeral; переживает generation reload, никогда не записывает конфигурацию и после перезапуска процесса возвращается в `running`. |
|
||||
@@ -356,7 +520,7 @@ HTTP idle accounting защищает только явно ограниченн
|
||||
|
||||
| Операция | Поддержка API |
|
||||
| --- | --- |
|
||||
| Чтение или изменение `[web]`, vhosts, profiles, decoys, timeouts или limits | Да, через `GET` или `PATCH /v1/config`. Производный snapshot `web.runtime` не возвращается и недоступен для записи. Вложенные tables сливаются по полям; arrays целиком заменяют прежний array. Любое изменение `[web.limits]` принимается как desired configuration, но помечается deferred до перезапуска процесса. |
|
||||
| Чтение или изменение `[web]`, vhosts, profiles, decoys, timeouts или limits | Да, через `GET` или `PATCH /v1/config`. Производный snapshot `web.runtime` не возвращается и недоступен для записи. Вложенные tables сливаются по полям; arrays целиком заменяют прежний array. Изменения `web.allow_wildcard_hosts` и `[web.limits]` откладываются до перезапуска процесса. Добавлять шаблоны можно только после включения wildcard в действующем процессе. |
|
||||
| Сохранение `server.listeners` | Да, через `PATCH /v1/config`, но изменённый WEB-listener остаётся deferred до перезапуска процесса. |
|
||||
| Применение WEB-конфигурации, изменённой вне API | Да, через `POST /v1/system/reload` с последующей проверкой статуса операции. |
|
||||
| Просмотр bounded серверных WEB request- и lifecycle-деталей | Да, через аутентифицированный `GET /web-status`. |
|
||||
@@ -441,7 +605,7 @@ curl -sS http://127.0.0.1:9091/v1/system/reload/RELOAD_ID \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
```
|
||||
|
||||
Терминальный статус `succeeded` подтверждает активацию runtime. Изменённые carrier, candidates, deadlines или learning policy используют новые bridge sessions; существующие сессии и начатые attempt chains не мигрируют. Если `deferred_process_fields` содержит `server.listeners` или `web.limits`, файл валиден и сохранён, но эти настройки всё ещё требуют перезапуска Telemt.
|
||||
Терминальный статус `succeeded` подтверждает активацию runtime. Изменённые carrier, candidates, deadlines или learning policy используют новые bridge sessions; существующие сессии и начатые attempt chains не мигрируют. Если `deferred_process_fields` содержит `server.listeners`, `web.allow_wildcard_hosts` или `web.limits`, файл валиден и сохранён, но эти настройки всё ещё требуют перезапуска Telemt.
|
||||
|
||||
Операции с access users используют существующие endpoints, например:
|
||||
|
||||
@@ -485,7 +649,7 @@ curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/rotate-secret \
|
||||
|
||||
| Симптом | Что проверить |
|
||||
| --- | --- |
|
||||
| WEB-конфигурация валидна на диске, но поведение listener’а не изменилось | Проверьте `deferred_process_fields`; listener и `[web.limits]` требуют перезапуска. |
|
||||
| WEB-конфигурация валидна на диске, но поведение listener’а не изменилось | Проверьте `deferred_process_fields`; listener и `web.allow_wildcard_hosts` / `[web.limits]` требуют перезапуска. |
|
||||
| Carrier-запросы попадают в decoy | Проверьте точный vhost, secret mode ссылки, CIDR непосредственного proxy и единственное корректно разбираемое значение `X-Forwarded-For`. |
|
||||
| Ссылка перестала работать после изменения `base_path` | Импортируйте заново напечатанную path-ссылку и убедитесь, что полный новый prefix без изменений попадает в Telemt. Существующие sessions могут восстановиться только через новый точный base; старые capabilities нельзя использовать повторно. |
|
||||
| `/telegram/web` перенаправляет на `/telegram/web/` | Добавьте точный non-WEB handler для path без слеша. В WEB-контракт Telemt входит только настроенное поддерево с завершающим слешем. |
|
||||
|
||||
Reference in New Issue
Block a user