mirror of
https://github.com/by-sonic/tglock.git
synced 2026-11-05 07:11:56 +03:00
fix: медиа и «прокси не настроен»: пакет на кадр, настоящие фронты, kwsN-1 только для медиа; beta.19 (#75)
* fix(tunnel): медиа и «прокси не настроен»: один пакет на кадр, настоящие фронты, kwsN-1 только для медиа Три причины, каждая проверена на живом Telegram запросом req_DH_params: сервер сверяет номер DC внутри него с собой и отвечает -444 на чужой. - WebSocket-фронт разбирает один пакет на входящий кадр, остаток ждёт следующего. Клиент пишет запросы пачками, TGLock слал их одним кадром, и всё, что шло вторым, застревало: текст проходил, файлы вставали. Теперь поток режется на пакеты, как в Flowseal/tg-ws-proxy. Пачка через beta.18 висит на всех DC, через эту сборку принимается на всех. - Кластер kwsN-1 отвергает обычное соединение с -444, а Telegram Desktop в ответ выключает прокси как настроенный неверно. При сбое kwsN основное соединение откатывалось именно туда. Теперь kwsN-1 только для медиа. - Адреса из списка DC не говорят на TLS ни из какой сети (проверено с чистой сети), поэтому первый маршрут каждого DC не работал никогда. Закреплены настоящие фронты, и DC1, DC3, DC5 стали доступны по WebSocket, чего не было. У CDN203 своего фронта нет: остались TCP, Worker и CF-прокси. Обрыв туннеля, через который уже шли данные, больше не ставит маршрут на паузу. Таймаут соединения 8 с, как в tg-ws-proxy. probe_proxy.mjs получил --identity и --batched: они и нашли эти баги. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: журнал ошибок в диагностике, --log-file для роутеров; 2.0.0-beta.19 «Скопировать диагностику» отдавала только счётчики, и по ней нельзя было понять, какой маршрут не ответил. Теперь в отчёте есть строки сбоев туннелей, а домены Worker и CF-прокси в них заменены на <домен>. --log-file и --log-max-mb (log_file, log_max_mb в настройках), как в tg-ws-proxy: журнал идёт в файл с ротацией, а не в системный журнал роутера. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(routes): запасной WebSocket на порту 80 родных адресов, включая CDN203 Родные адреса Telegram принимают WebSocket без TLS на порту 80. Измерено req_DH_params: DC определяется адресом, имя хоста не важно, а 91.105.192.100 отдаёт ключи CDN. Для CDN203, откуда часто идёт видео, это первый прямой WebSocket: на 443 фронта у него нет, и там, где провайдер закрыл только 443, до него раньше было не достать без Worker. Защита та же, что у родного TCP: внутри тот же обфусцированный MTProto. В Host идёт адрес, имени Telegram в открытом виде нет. Маршрут разрешён только до закреплённых адресов и пробуется последним. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): паузы маршрутов по результатам ревью, пределы и журнал - Обрыв не наказывает маршрут, только если туннель работал и получал ответы 15 с. Одного ответа мало: маршрут, который ответил раз и умер, оставался предпочтительным навсегда. - Клиент спросил, не дождался ответа за 5 с и ушёл: маршрут считается неработающим. Иначе CDN, принявший TCP и молчащий, держал первое место, и очередь до порта 80 не доходила. - Предел пакета 2 МиБ вместо 16: чужое устройство в LAN-режиме не заставит роутер копить десятки мегабайт на соединение. - Журнал: разовая ошибка записи его больше не выключает, неудачная ротация не теряет бэкап и держит предел, права 0600 и на уже существующем файле. - В отчёт диагностики не попадает Worker, заданный адресом. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: babin <Thartewerner536e@engineer.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+42
-12
@@ -200,25 +200,55 @@ Ping приходит в читающую половину, а отвечает
|
||||
Для каждого DC и отдельно для media-соединений строится список маршрутов:
|
||||
|
||||
1. сохранённый успешный маршрут;
|
||||
2. точный Telegram IP с `kwsN` или `kwsN-1` в TLS SNI и WebSocket Host;
|
||||
3. явно настроенный пользователем Cloudflare Worker;
|
||||
4. дополнительные Telegram IP и варианты хоста;
|
||||
5. системный DNS (кроме CDN DC203).
|
||||
2. основной WebSocket-фронт DC (`web_fronts`) с настоящим именем хоста в TLS SNI
|
||||
и WebSocket Host;
|
||||
3. явно настроенные пользователем CF-прокси и Cloudflare Worker;
|
||||
4. запасные фронты DC;
|
||||
5. системный DNS;
|
||||
6. WebSocket без TLS на порту 80 адреса DC и родной MTProto TCP, только если всё
|
||||
остальное не ответило.
|
||||
|
||||
Поддерживаются DC1–5 и CDN DC203. Для DC203 первым используется обычный MTProto
|
||||
TCP к закреплённому `91.105.192.100:443`; этот сервер может принимать MTProto,
|
||||
не принимая TLS/WebSocket. Резерв через Worker соединяется с тем же IP.
|
||||
WebSocket-попытки также сохраняют закреплённый адрес. DNS fallback на DC2 для DC203 запрещён:
|
||||
успешный WebSocket handshake с другим DC не доставляет CDN-запрос в нужный
|
||||
датацентр. Worker также получает именно IP DC203.
|
||||
Адреса фронтов и родные адреса DC разные. `149.154.167.51`, `.91`, `149.154.175.50`,
|
||||
`.100`, `149.154.171.5` и `91.105.192.100` говорят только на MTProto и на TLS не
|
||||
отвечают, в том числе из сети без блокировок. WebSocket принимают `149.154.167.220`
|
||||
и `.99` (DC2, DC4), `149.154.174.100` и `.200`, `149.154.175.209` (DC1, DC3),
|
||||
`149.154.170.100`, `.200` и `.96` (DC5).
|
||||
|
||||
Фронт выбирает дата-центр по имени хоста, а не по заголовку клиента, поэтому имя и
|
||||
адрес нельзя менять местами. Какой DC стоит за маршрутом, проверяет
|
||||
`probe_proxy.mjs --identity`: сервер сверяет номер DC внутри `req_DH_params` с собой
|
||||
и отвечает `-444`, если номер чужой. `resPQ` этого не показывает, на него отвечает
|
||||
любой сервер при любом номере в заголовке.
|
||||
|
||||
Кластер `kwsN-1` только для медиа: обычное соединение он отвергает с `-444`, а
|
||||
Telegram Desktop на этот код выключает прокси как настроенный неверно. Основной
|
||||
кластер `kwsN` принимает оба вида соединений и остаётся запасным для медиа.
|
||||
|
||||
Фронт разбирает один транспортный пакет на входящий кадр, остаток ждёт следующего
|
||||
кадра. Клиенты пишут запросы пачками, поэтому поток режется на пакеты
|
||||
(`PacketSplitter`), и каждый уходит отдельным кадром. Для Worker это не нужно: он
|
||||
пишет кадры в обычный TCP.
|
||||
|
||||
На порту 80 родные адреса принимают WebSocket без TLS. Там DC определяет адрес, а
|
||||
не имя хоста, поэтому в `Host` идёт сам адрес и имя Telegram в открытом виде не
|
||||
появляется. Защита та же, что у родного TCP: внутри тот же обфусцированный MTProto,
|
||||
а сервер подтверждает себя в самом протоколе. Маршрут разрешён только до
|
||||
закреплённых адресов.
|
||||
|
||||
Поддерживаются DC1–5 и CDN DC203. У DC203 нет фронта на 443, а `kws2` на любом
|
||||
фронте это обычный DC2, поэтому его маршруты: MTProto TCP к закреплённому
|
||||
`91.105.192.100:443`, Worker, CF-прокси и порт 80 того же адреса.
|
||||
|
||||
После ошибки маршрут получает exponential cooldown от 30 секунд до 30 минут.
|
||||
Успешный маршрут становится первым для следующего соединения того же DC и
|
||||
типа трафика. Попытки запускаются с интервалом 250 мс, не больше трёх
|
||||
одновременно на соединение; остальные отменяются после первого успеха.
|
||||
Если все маршруты на паузе, возвращается причина и время до повторной
|
||||
попытки. Новое подключение клиента не обходит cooldown. Ошибка upstream
|
||||
после handshake также снимает предпочтение маршрута и добавляет cooldown.
|
||||
попытки. Новое подключение клиента не обходит cooldown. Маршрут тоже теряет
|
||||
предпочтение и получает cooldown, если Telegram закрыл соединение с кодом ошибки,
|
||||
туннель оборвался, не проработав 15 секунд, или клиент спросил, не дождался ответа
|
||||
за 5 секунд и ушёл сам. Обрыв туннеля, который работал дольше и получал ответы,
|
||||
маршрут не наказывает: это обычное дело, а пауза оставила бы весь DC без связи.
|
||||
|
||||
## TLS policy
|
||||
|
||||
|
||||
@@ -40,6 +40,26 @@ authenticate the responding server, prove its DC identity, log into an account,
|
||||
or verify message sending, media downloads or Android lifecycle. In particular,
|
||||
`requestedDc` describes the request, not an independently confirmed backend.
|
||||
|
||||
## Confirm which data centre answers
|
||||
|
||||
`--identity` continues the handshake with `req_DH_params`. The DC number sealed
|
||||
inside it is checked by the server against itself, and a mismatch is answered
|
||||
with transport error `-444`, so this does prove the backend. `resPQ` alone proves
|
||||
nothing here: every server answers it for any DC in the header. The handshake
|
||||
stops before an authorization key exists. CDN DC203 uses its own RSA keys, so
|
||||
the report says `identity: "skipped: CDN keys"` for it.
|
||||
|
||||
`--batched` implies `--identity` and sends `req_DH_params` glued behind another
|
||||
packet in one write, the way real clients batch requests. A WebSocket frontend
|
||||
reads one packet per frame, so a proxy that forwards the write as a single frame
|
||||
times out here while a lone request still passes. Run it before every release:
|
||||
|
||||
```sh
|
||||
for dc in 1 2 -2 3 4 -4 5 203; do
|
||||
node scripts/probe_proxy.mjs --port 18080 --secret-file /private/path/tglock-secret --dc $dc --batched
|
||||
done
|
||||
```
|
||||
|
||||
## Isolate CDN transport failures
|
||||
|
||||
To distinguish an unavailable CDN WebSocket endpoint from an unavailable CDN
|
||||
|
||||
@@ -77,6 +77,21 @@ chmod 755 /etc/init.d/tglock
|
||||
logread -e tglock
|
||||
```
|
||||
|
||||
Состояние меняется часто, и stdout сервиса уходит в системный журнал. Чтобы TGLock
|
||||
его не забивал, задайте отдельный файл в `/etc/tglock/tglock.toml`:
|
||||
|
||||
```toml
|
||||
# /tmp лежит в оперативной памяти: журнал не изнашивает flash и не переживает
|
||||
# перезагрузку. Заполненный файл переименовывается в tglock.log.1, больше двух
|
||||
# файлов на диске не бывает.
|
||||
log_file = "/tmp/tglock.log"
|
||||
log_max_mb = 1
|
||||
```
|
||||
|
||||
Смотреть его: `tail -f /tmp/tglock.log`. То же самое флагами: `--log-file`,
|
||||
`--log-max-mb`. Ошибки запуска по-прежнему попадают в `logread`. Если журнал не
|
||||
нужен совсем, есть `quiet = true`.
|
||||
|
||||
Обновление: остановите сервис, замените бинарь после проверки контрольной суммы,
|
||||
сохраните `/etc/tglock`, запустите сервис снова. Для отмены автозапуска используйте
|
||||
`/etc/init.d/tglock stop` и `/etc/init.d/tglock disable`.
|
||||
|
||||
Reference in New Issue
Block a user