--- name: telegram-web-proxy description: >- Deploy Telegram Desktop WEB Proxy (tproxy-server) on a VPS. category: devops tags: - telegram - web-proxy - tproxy - mtproto - caddy - dpi --- # Telegram WEB Proxy (tproxy-server) ## When to Use This Skill Use when you need to: - Развернуть **WEB Proxy для Telegram Desktop** (telegramdesktop/tproxy-server) на VPS — маскируется под обычный HTTPS-сайт на 443, настраивается в Telegram Desktop как WEB Proxy (hostname + secret) - Починить `mtproxy.service` после установки (203/EXEC, readyz 503) - Добавить ещё один секрет/профиль (мульти-секреты) - Понять, почему «снаружи не открываются порты 80/443» после установки ## Architecture ``` TD → HTTPS:443 → Caddy → tproxy-server:8080 → MTProxy:2398 → Telegram └ admin :8081 (/readyz, /healthz) ``` - Caddy отдаёт Let's Encrypt сертификат (tls-alpn) и проксирует ВЕСЬ трафик на tproxy-server (127.0.0.1:8080). tproxy-server сам различает: обычные запросы → сайт-маскировку (`public_dir`), запросы WEB Proxy → MTProxy. - Снаружи это просто HTTPS-сайт; наружу открыты только 80/443. - nft-правило (`tproxy-firewall`) закрывает MTProxy-бэкенд 2398/8888 от внешнего мира: `iifname != "lo" tcp dport {2398, 8888} drop`. ## Deploy 1. Склонировать upstream: `git clone https://github.com/telegramdesktop/tproxy-server.git` 2. Подготовить сайт-маскировку (каталог со статическим `index.html`; нейтральный, БЕЗ упоминаний Telegram) и secret: `openssl rand -hex 16` (32 hex). 3. Загрузить на сервер (rsync, либо tar+ssh — см. грабли #4). 4. Запустить: ```bash ./deploy/install.sh --hostname HOST --email ME@EXAMPLE.COM \ --site-dir /tmp/tproxy-site --secret "$(cat /tmp/tproxy-secret.txt)" ``` Ставит: ca-certificates curl nftables, Caddy 2.11.4, Go 1.26.5, собирает tproxy-server, MTProxy (pinned commit), создаёт systemd-сервисы: `tproxy-server`, `mtproxy`, `tproxy-firewall`, `caddy`, таймер `refresh-mtproxy-config` (daily, обновляет список IP Telegram в proxy-multi.conf). 5. Проверка: ```bash systemctl status tproxy-server mtproxy tproxy-firewall caddy # все active curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/readyz # 200 curl -s -o /dev/null -w '%{http_code}' https://HOST/ # 200 (сайт) nft list ruleset | grep -E '2398|8888' # drop для не-loopback ``` ## Pitfalls ### 1. mtproxy.service → 203/EXEC (readyz 503) **Симптом:** tproxy-server active, но `mtproxy` failed (203/EXEC), порт 2398 не слушается, `/readyz` → 503 (healthz при этом 200). **Причина:** install.sh собирает MTProxy в `/opt/MTProxy/objs/bin/mtproto-proxy`, но umask даёт бинарю и каталогам `700 root:root`. Юнит `mtproxy.service` запускается от пользователя `mtproxy` (NoNewPrivileges, ProtectSystem=strict) — не может прочитать бинарь → 203/EXEC. **Фикс:** ```bash chmod 755 /opt/MTProxy /opt/MTProxy/objs /opt/MTProxy/objs/bin chown root:mtproxy /opt/MTProxy/objs/bin/mtproto-proxy chmod 750 /opt/MTProxy/objs/bin/mtproto-proxy systemctl restart mtproxy # → active (running), слушает 0.0.0.0:2398, readyz → 200 ``` Повторный запуск install.sh безопасен (идемпотентен). ### 2. Флейк `go test ./...` внутри install.sh Первый прогон (холодный кэш Go, параллельная сборка) может упасть на `internal/config: TestLoadAcceptsSystemdCredentialReadPermissions`. Повторный — PASS. Это гонка при холодном прогоне, не баг кода. Лечение: перезапустить install.sh. ### 3. «Порты 80/443 закрыты снаружи» — ложная тревога Если после установки curl снаружи не отвечает: сначала `ss -tlnp` на сервере. Чаще всего на порту просто НИЧЕГО не слушает (curl = connection refused), а firewall провайдера ни при чём. Проверка: поднять временный слушатель (`python3 -m http.server 80`) и проверить TCP connect снаружи. У Hostkey отдельного firewall в панели НЕТ — порты открыты по умолчанию; настройки «прокси/фаервол» в панели искать не нужно. Различать: `timeout` = режется (firewall/DPI), `refused` = нет слушателя. ### 4. rsync отсутствует на Debian minimal Если на сервере нет rsync, грузить дерево tar+ssh: ```bash tar czf - tproxy-server/ | ssh root@HOST 'mkdir -p /tmp/tproxy-server && tar xzf - -C /tmp' ``` ## Multi-profile secrets (несколько секретов) Поддерживается: `/etc/tproxy-server/profiles.json` — **массив** профилей, у каждого свой `secret`. Лимит `max_profiles: 32` (в config.json). Все профили по умолчанию ведут в один MTProxy-бэкенд (127.0.0.1:2398). ```json {"profiles":[ {"name":"default","secret":"","backend":"127.0.0.1:2398"}, {"name":"second","secret":"","backend":"127.0.0.1:2398"} ]} ``` - systemd читает через `LoadCredential=profiles.json:/etc/tproxy-server/profiles.json` **при старте** → после правки `systemctl restart tproxy-server`. - Права файла должны быть 600 root (иначе Load откажет — тот самый `TestLoadAcceptsSystemdCredentialReadPermissions`). - Формат секрета: 16 байт = 32 hex (обычный), либо 17 байт с префиксом `dd` (fake-TLS/DD режим). `openssl rand -hex 16` — обычный 32-hex, для Telegram Desktop WEB Proxy это нужно. ## Client setup Telegram Desktop → Settings → Advanced → Connection Type → Use custom proxy → NEW PROXY → WEB Proxy: hostname + secret (32 hex). Ссылка для шаринга не `t.me/proxy?...` (это MTProto-ссылка), а передаётся полями (hostname/secret) — WEB Proxy не имеет share-ссылки вида `t.me/proxy`. ## Related - `mtproto-proxy` — классический MTProto proxy через Docker (seriyps), секреты с `ee` (Fake TLS), ссылки `t.me/proxy`. Другой тип прокси. - `networking-proxy` — umbrella по DPI-обходу (XRay, AmneziaWG, Shadowsocks, NNTP, SSH-туннели). - INSTALL_NOTES.md в репозитории `tproxy-web` (gitverse kpa39l) — полный опыт установки 2026-08-31.