commit 088f648eeea2cde5c67d5b45958035081c9c37bf Author: estorozhenko Date: Sun Sep 6 13:51:09 2026 +0000 Initial commit: Hermes skill telegram-web-proxy diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..78f5282 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,150 @@ +--- +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. \ No newline at end of file diff --git a/references/tproxy-server-web-proxy.md b/references/tproxy-server-web-proxy.md new file mode 100644 index 0000000..e0219d9 --- /dev/null +++ b/references/tproxy-server-web-proxy.md @@ -0,0 +1,109 @@ +# tproxy-server — детали установки 2026-08-31 (VPS03, Hostkey) + +Полный опыт развёртывания telegramdesktop/tproxy-server на Debian 13 +(77.67.89.154, vps03.nixg.ru). Резюме в SKILL.md; здесь — команды и транскрипты. + +## Установка + +```bash +# 1. Clone upstream (локально, потом tar+ssh на сервер — rsync отсутствует) +git clone --depth 1 https://github.com/telegramdesktop/tproxy-server.git + +# 2. Secret +openssl rand -hex 16 > /tmp/tproxy-secret.txt # 32 hex, chmod 600 + +# 3. Сайт-маскировка — /tmp/tproxy-site/index.html +# НЕЙТРАЛЬНАЯ страница ("личная страница"), без слова Telegram и сигнатур. + +# 4. install.sh +./deploy/install.sh --hostname vps03.nixg.ru --email kpa39l@yandex.ru \ + --site-dir /tmp/tproxy-site --secret "$(cat /tmp/tproxy-secret.txt)" +``` + +install.sh (v.2026) ставит: ca-certificates, curl, nftables; Caddy 2.11.4 +(бинарник с GitHub); Go 1.26.5 (/opt/go*); собирает tproxy-server → /usr/local/bin; +MTProxy (pinned commit f36d8af) → /opt/MTProxy; systemd: tproxy-server, mtproxy, +tproxy-firewall, caddy; таймер refresh-mtproxy-config (daily). + +## Схема сервисов + +- tproxy-server: `ExecStart=/usr/local/bin/tproxy-server -config /etc/tproxy-server/config.json`, + `LoadCredential=profiles.json:/etc/tproxy-server/profiles.json` +- listen 127.0.0.1:8080 (relay), admin 127.0.0.1:8081 (/readyz /healthz) +- mtproxy: пользователь `mtproxy`, ExecStart=/opt/MTProxy/objs/bin/mtproto-proxy ... +- firewall (nft): `iifname != "lo" tcp dport {2398,8888} drop` — бэкенд закрыт + от внешнего мира, наружу только 80/443. + +## Диагностика «не открыты порты» (важно!) + +Симптом: `curl http://HOST/` и `https://HOST/` снаружи не отвечают (fail), хотя +локально порты слушаются (python3 http.server на :80 отвечает). Подозрение на +firewall провайдера (Hostkey) — в панели настроек firewall НЕТ. + +Правда: порты у Hostkey открыты по умолчанию, firewall нет. curl не отвечал, +потому что на 80/443 НИЧЕГО не слушало (refused). Точная проверка — различить: + +```bash +# снаружи (другая машина): +timeout 8 bash -c 'cat < /dev/null > /dev/tcp/HOST/80 && echo OPEN || echo "rc=$?"' +# refused → нет слушателя (нормально, лечится установкой) +# timeout → firewall/DPI режет +``` + +## Грабли mtproxy 203/EXEC + +После install.sh: `systemctl status mtproxy` → failed, status=203/EXEC; readyz 503, +healthz 200, порт 2398 не слушается. Причина: umask даёт `/opt/MTProxy/objs/bin` +и бинарю права 700 root:root; юнит с User=mtproxy + NoNewPrivileges + +ProtectSystem=strict не может прочитать бинарь. + +```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), readyz → 200 +``` + +## Флейк go test + +Первый `go test ./...` (холодный кэш) упал: +`internal/config: TestLoadAcceptsSystemdCredentialReadPermissions: +group/other-readable profiles file outside a credential directory was accepted`. +Повторный (тёплый кэш) — PASS. Это гонка холодного параллельного прогона. +Перезапустить install.sh — идемпотентен. + +## Мульти-секреты + +profiles.json (пример на проде): + +```json +{"profiles":[{"name":"default","secret":"","backend":"127.0.0.1:2398"}]} +``` + +Добавить второй профиль → массив из двух объектов, `systemctl restart +tproxy-server`. Лимит max_profiles 32 (config.json). Права profiles.json — 600 +root; LoadCredential читается при старте. + +Формат secret: 16 байт (32 hex) обычный, или 17 байт с префиксом `dd` +(fake-TLS/DD). `openssl rand -hex 16` → обычный 32-hex, для Telegram Desktop +WEB Proxy это правильный формат. + +## Проверка после установки (полная) + +```bash +systemctl status tproxy-server mtproxy tproxy-firewall caddy +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://vps03.nixg.ru/ # 200 +openssl s_client -connect vps03.nixg.ru:443 -servername vps03.nixg.ru # LE cert +ss -tlnp | grep -E ':80 |:443|:2398|:8080|:8081' +nft list ruleset | grep -E '2398|8888' +``` + +Снаружи: 443 OPEN, 8080 refused (бэкенд скрыт), 2398 timeout (nft drop — не +светится). Сайт отдаётся на /, HTTP → 308 → HTTPS. + +## Загрузка дерева без rsync + +```bash +tar czf - tproxy-server/ | ssh root@HOST 'mkdir -p /tmp/tproxy-server && tar xzf - -C /tmp' +``` \ No newline at end of file