Files
telemt/docs/WEB/WEB_PROXY.ru.md
T
2026-08-23 03:32:40 +03:00

18 KiB
Raw Blame History

WEB-режим прокси

English | Русский | Deutsch

WEB-режим переносит обычные MTProxy-потоки через ограниченный HTTPS long-poll transport, совместимый с типом прокси WEB в Telegram Desktop. В первой реализации Telemt не терминирует TLS: публичный сертификат обслуживает NGINX или HAProxy, который передаёт обычный HTTP/1.1 на приватный listener Telemt.

Important

WEB-режим реализован и настраивается в текущем дереве исходного кода. Для первого развёртывания нужен бинарный файл, собранный из ревизии с этой реализацией, и перезапуск процесса Telemt. Готовый пакет можно использовать только после проверки, что он содержит эту ревизию. Сквозная проверка с целевой сборкой Telegram Desktop и реальным публичным TLS endpoint остаётся обязательным приёмочным шагом оператора.

Путь трафика

Telegram Desktop
    | HTTPS :443
    v
NGINX или HAProxy (TLS termination, канонические Host и X-Forwarded-For)
    | обычный HTTP/1.1 в приватной сети
    v
WEB-listener Telemt
    |-- аутентифицированный carrier --> bounded logical MTProxy relays --> Telegram
    `-- обычный или некорректный запрос --> настроенный decoy site

Направляйте в Telemt весь публичный vhost. Если TLS-терминатор будет выделять только известные carrier paths, поведение обычных и аутентифицированных запросов станет наблюдаемо различным, а decoy policy Telemt будет обойдена.

Поддерживаемый контракт клиента

  • Публичный endpoint всегда имеет вид https://HOST:443.
  • Поддерживаются 16-байтовые MTProxy-секреты plain и dd. FakeTLS-секреты ee в WEB-режиме не поддерживаются.
  • Первая версия carrier использует сериализованные HTTPS uplink-запросы и HTTPS long polling. WebSocket- и lane-carriers не анонсируются.
  • Capability, bootstrap и session credentials — отдельные значения с ограниченным сроком жизни. Carrier credentials считаются секретами и не должны попадать в access logs.
  • Внутренняя MTProxy-аутентификация ограничена пользователем и режимом секрета, выбранными профилем vhost. Некорректный внутренний handshake закрывает только свой logical stream и никогда не попадает в TCP masking path.

В WEB-ссылках Telegram Desktop нет порта, потому что клиент требует порт 443:

tg://webproxy?server=proxy.example.com&secret=0123456789abcdef0123456789abcdef
tg://webproxy?server=proxy.example.com&secret=dd0123456789abcdef0123456789abcdef

Telemt печатает ссылки для WEB-профилей, выбранных в [general.links].show, через существующий log target telemt::links.

Предварительные требования

  • Отдельный публичный FQDN и действующий TLS-сертификат на NGINX или HAProxy.
  • Стабильный публичный IP этого hostname. В public_addr должен быть указан именно этот конкретный IP с портом 443, поскольку адрес участвует во внутреннем destination tuple relay.
  • Приватный или loopback HTTP-путь от TLS-терминатора до Telemt.
  • Обычный decoy site: приватный HTTP origin либо immutable snapshot локального каталога.
  • Совместимая сборка Telegram Desktop с типом прокси WEB.

Если один hostname обслуживается одновременно по IPv4 и IPv6, в первой реализации используйте отдельные hostname или отдельные экземпляры Telemt. Forwarded client address и public_addr должны принадлежать одному семейству IP.

Минимальная конфигурация Telemt

В примере WEB-listener остаётся на loopback, а decoy использует приватный HTTP origin:

[general.links]
show = ["web-user"]

[access.users]
web-user = "0123456789abcdef0123456789abcdef"

[[server.listeners]]
ip = "127.0.0.1"
port = 18080
transport = "web"
proxy_protocol = false
web_client_ip_source = "x_forwarded_for"
web_trusted_proxy_cidrs = ["127.0.0.1/32"]

[web]
enabled = true

[[web.vhosts]]
host = "proxy.example.com"
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 обязательны 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.

Вместо origin можно использовать immutable snapshot статического сайта:

[web.vhosts.decoy]
mode = "static_directory"
directory = "/var/lib/telemt/public"
index = "index.html"

Статические файлы читаются при запуске и успешном reload конфигурации. Число элементов, размер одного файла и общий размер snapshot ограничены [web.limits]. Symlinks и пути с выходом из настроенного каталога запрещены. Не изменяйте каталог одновременно с построением snapshot в Telemt.

Все WEB-ключи и defaults перечислены в справочнике конфигурации.

Терминация TLS на NGINX

upstream telemt_web {
    server 127.0.0.1:18080;
    keepalive 64;
}

server {
    listen 443 ssl;
    server_name proxy.example.com;
    access_log off;

    ssl_certificate     /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;

    client_max_body_size 2m;

    location / {
        proxy_pass http://telemt_web;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Connection "";

        proxy_connect_timeout 5s;
        proxy_send_timeout 35s;
        proxy_read_timeout 35s;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_next_upstream off;
    }
}

client_max_body_size должен быть не меньше web.limits.max_body_bytes. Значения proxy_read_timeout и proxy_send_timeout должны превышать web.timeouts.long_poll_secs, по умолчанию равный 25 секундам. Перезаписывайте X-Forwarded-For, а не дополняйте его. Не включайте upstream retries: byte-identical retry выполняет сам bridge по своему sequence protocol.

Терминация TLS на HAProxy

frontend public_https
    mode http
    no log
    bind :443 ssl crt /etc/haproxy/certs/proxy.example.com.pem alpn h2,http/1.1
    acl telemt_web_host hdr(host) -i proxy.example.com proxy.example.com:443
    use_backend telemt_web if telemt_web_host

backend telemt_web
    mode http
    option http-keep-alive
    retries 0
    timeout connect 5s
    timeout server 35s
    http-request set-header Host proxy.example.com
    http-request del-header X-Forwarded-For
    http-request set-header X-Forwarded-For %[src]
    server telemt_web_1 127.0.0.1:18080 check

Во frontend или секции defaults также задайте timeout client выше long-poll deadline. Не переписывайте path, raw query, body и carrier headers Authorization, Content-Type, X-Up-Seq, X-Down-Cursor.

Lifecycle и reload

Конфигурация Поведение runtime
Состав WEB-listeners, bind address и trust policy Принадлежат процессу; перезапустите Telemt.
Любое значение [web.limits] Process-owned контракт памяти и ресурсов; перезапустите Telemt.
web.enabled, timeouts, vhosts, profiles и decoys Применяются config watcher или runtime generation reload.
Существующие HTTP connections и WEB sessions Сохраняют лимиты и deadlines своего момента создания; новые logical streams используют активное runtime generation.
Завершение процесса Использует последнее применённое значение web.timeouts.shutdown_secs.

Каждый logical stream сохраняет client IP своей сессии и владеет уникальным в пределах процесса ненулевым synthetic source port до завершения relay. Это сохраняет один стабильный непересекающийся source/destination tuple для Direct и Middle-End KDF routing.

Управление через API

Управление через API доступно, но намеренно ограничено. Отдельных endpoint /v1/web и WEB-specific runtime statistics endpoint сейчас нет.

Операция Поддержка API
Чтение или изменение [web], vhosts, profiles, decoys, timeouts или limits Нет. GET /v1/config не возвращает [web]; PATCH /v1/config отвечает 400 section_not_editable на ключ web.
Сохранение server.listeners Да, через PATCH /v1/config, но изменённый WEB-listener остаётся deferred до перезапуска процесса.
Применение WEB-конфигурации, изменённой вне API Да, через POST /v1/system/reload с последующей проверкой статуса операции.
Управление [access.users] Да, через /v1/users. Создание пользователя не создаёт WEB-профиль.
Отзыв отдельного пользователя Да. /v1/users/{username}/disable немедленно обновляет admission и завершает активные сессии пользователя.

Привяжите API к loopback, оставьте узким whitelist непосредственных peers, настройте точное значение authorization header и используйте read_only = false только там, где нужны мутации:

[server.api]
enabled = true
listen = "127.0.0.1:9091"
whitelist = ["127.0.0.0/8"]
auth_header = "Bearer replace-with-a-random-control-token"
read_only = false

API whitelist проверяет непосредственный TCP peer и не доверяет X-Forwarded-For. Изменения самой секции [server.api] требуют перезапуска процесса.

После атомарного изменения TOML-файла администратором или системой управления конфигурацией задайте в TELEMT_API_AUTH точное значение auth_header и отправьте наблюдаемый generation reload:

curl -sS -X POST http://127.0.0.1:9091/v1/system/reload \
  -H "Authorization: ${TELEMT_API_AUTH}" \
  -H 'Content-Type: application/json' \
  -d '{"mode":"drain","timeout_secs":30,"failure_policy":"rollback"}'

# Use data.reload_id from the response.
curl -sS http://127.0.0.1:9091/v1/system/reload/RELOAD_ID \
  -H "Authorization: ${TELEMT_API_AUTH}"

Терминальный статус succeeded подтверждает активацию runtime. Если deferred_process_fields содержит server.listeners или web.limits, файл валиден и сохранён, но эти настройки всё ещё требуют перезапуска Telemt.

Операции с access users используют существующие endpoints, например:

curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/disable \
  -H "Authorization: ${TELEMT_API_AUTH}"

curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/rotate-secret \
  -H "Authorization: ${TELEMT_API_AUTH}" \
  -H 'Content-Type: application/json' \
  -d '{}'

После ротации секрета config watcher перестраивает WEB capabilities. Users API возвращает секрет, но не URL tg://webproxy; соберите ссылку из настроенного hostname и представления plain или dd соответствующего профиля. Перед удалением пользователя, на которого ссылается WEB-профиль, сначала удалите и примените этот профиль, чтобы итоговая конфигурация оставалась валидной.

Полный контракт запросов, revisions, ошибок и всех user endpoints приведён в документации Control API.

Инварианты развёртывания

  • Никогда не публикуйте plain HTTP WEB-listener в недоверенной сети. Закрепите это host firewall rules, даже если listener использует loopback.
  • Отключите логирование request target и authorization на TLS-терминаторе либо используйте проверенный формат с редактированием. Raw queries содержат bridge capabilities, а Authorization — bootstrap или session bearer credentials.
  • Сохраняйте один стабильный публичный адрес на vhost. Если DNS возвращает несколько ingress addresses, каждый deployment должен использовать адрес своего внешнего пути.
  • Bootstrap- и session-registries локальны для процесса. Для multi-process или multi-host upstream pool нужна affinity всего vhost: bridge GET, создание сессии, uplink, downlink и DELETE. Одному процессу Telemt дополнительная affinity не нужна.
  • Decoy входит в anti-probing contract. До распространения ссылок проверьте через публичный TLS endpoint его обычный ответ 404 и response timing.

Первичная проверка

  1. Запустите пересобранный Telemt с WEB-конфигурацией и убедитесь, что приватный listener привязан.
  2. Через публичный TLS endpoint проверьте, что GET /, неизвестный path и некорректный query bridge возвращают настроенный decoy site.
  3. Убедитесь, что Telemt получает один канонический адрес X-Forwarded-For и Host: proxy.example.com либо Host: proxy.example.com:443.
  4. Импортируйте напечатанную ссылку tg://webproxy в целевую сборку Telegram Desktop и установите соединение через прокси.
  5. Проверьте reconnect и как минимум один long poll длительнее 25 секунд, чтобы frontend timeouts не обрывали carrier.
  6. Проверяйте лимиты пользователя и logical MTProxy connections по logical-stream counters, а не по числу HTTP connections.

Диагностика

Симптом Что проверить
WEB-конфигурация валидна на диске, но поведение listener’а не изменилось Проверьте deferred_process_fields; listener и [web.limits] требуют перезапуска.
Carrier-запросы попадают в decoy Проверьте точный vhost, secret mode ссылки, CIDR непосредственного proxy и единственное каноническое значение X-Forwarded-For.
Long polls разрываются через фиксированный интервал Поднимите client, server, send и read timeouts NGINX/HAProxy выше web.timeouts.long_poll_secs.
Telegram Desktop отклоняет ссылку Не указывайте порт, используйте валидный FQDN, внешний порт 443 и только plain или dd.
Один узел работает, но load-balanced pool нестабилен Настройте affinity всего vhost: WEB credential registries локальны для процесса.