ru1: WEB-прокси Telegram вместо telemt

На ru.ruzzy.dev:443 поднят официальный tproxy-server (relay, carrier
websocket) перед официальным MTProxy на loopback. Фронт — nginx в
~/gost, сертификат только на ru.ruzzy.dev.

- MTProxy: --nat-info 10.8.1.4:161.97.93.252 вручную — трафик к
  Telegram выходит через wg0 с IP fr1, автоопределение install.sh
  ошиблось бы и прокси молча не отвечал бы.
- relay на 8090/8091: 8080 занят cadvisor.
- telemt удалён окончательно (контейнер, образ, конфиг, том).
- certbot до этого не работал вовсе: сертификат был просрочен с
  2026-07-01, продлён и запущен.

stacks/tproxy/: compose и nginx.conf стека ~/gost, setup-ru1.sh —
однократная root-установка relay и MTProxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
gilfanovr
2026-10-09 17:16:12 +03:00
co-authored by Claude Opus 5.5
parent 123cdaa19b
commit 2ab38bc1f8
5 changed files with 215 additions and 6 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
## Серверы
- [`servers/de1.md`](servers/de1.md) — 213.136.74.247. **Выведен** (все сервисы остановлены, метрики удалены): был прокси/VPN-нодой (telemt, AmneziaWG, OpenVPN) плюс gitea, openclaw, боты — всё уехало на fr1 или в бэкапы. ⚠️ Сам файл описания под вывод не переписан.
- [`servers/fr1.md`](servers/fr1.md) — `fr1.ruzzy.dev` (161.97.93.252). Основная нода (Ubuntu 24.04): мониторинг, gitea, AmneziaWG exit для ru1, боты (claude-channels, openclaw), статика `www`/`share`. Держит апекс `ruzzy.dev`.
- [`servers/ru1.md`](servers/ru1.md) — `85.198.109.86` (VDSina, РФ). VPN entry-нода (OpenVPN ×2, AmneziaWG **до fr1** после переключения, WireGuard клиентов, telemt), умный роутинг RU/зарубеж. ⚠️ Файл описания сверялся до переключения.
- [`servers/ru1.md`](servers/ru1.md) — `85.198.109.86` (VDSina, РФ). VPN entry-нода (OpenVPN ×2, AmneziaWG **до fr1** после переключения, WireGuard клиентов), умный роутинг RU/зарубеж; WEB-прокси Telegram (tproxy-server + MTProxy) на `ru.ruzzy.dev:443`, telemt удалён 2026-10-09. ⚠️ Файл описания сверялся до переключения.
## Поддержание описаний в актуальном состоянии
Тронул сервер — обнови `servers/<хост>.md` в том же прогоне. Контракт и
+36 -5
View File
@@ -1,7 +1,7 @@
# ru1 — `85.198.109.86` (RU entry node)
VPN entry-нода в РФ. Точка входа в топологии **RU↔DE**: умный роутинг (RU-трафик → напрямую, зарубежный → туннель AWG до de1). Полный runbook — `~/README.md` на сервере.
Секреты (MTProxy user-secrets, ключи WG) — только на сервере, не здесь.
Секреты (WEB-proxy secret в `/etc/tproxy-server/profiles.json` и `/etc/mtproxy/mtproxy.env`, `token.key`, ключи WG) — только на сервере, не здесь.
**Провайдер:** VDSina · РФ
**ОС:** Ubuntu 24.04.3 LTS · kernel 6.8.0-78
@@ -27,10 +27,34 @@ VPN entry-нода в РФ. Точка входа в топологии **RU↔D
| OpenVPN #2 | 1195/udp (`tun1`) | RU-выход (весь трафик через ens3, DNS Yandex). Cfg `/etc/openvpn/server/ruopenvpn.conf` |
| AmneziaWG `wg0` | (awg, `45451/udp`) | туннель до de1 (exit). `awg-quick@wg0`, cfg `/etc/amnezia/amneziawg/wg0.conf`, `Table=off` |
| WireGuard `wg1` | 51820/udp | VPN для клиентов (Docker `linuxserver/wireguard`), подсеть `10.8.2.0/24`, `~/wireguard/` |
| telemt (MTProxy) | 443 | FakeTLS под `ru.ruzzy.dev`, маска → nginx `127.0.0.1:4443`. API `9091`. Docker, `~/gost/` |
| nginx | 80, 4443 | маскировочный сайт `ru.ruzzy.dev` (Docker `nginx:alpine`) |
| WEB-прокси Telegram | 443 (через nginx) | `tproxy-server` (relay, systemd, `127.0.0.1:8090`, admin `:8091`) → официальный MTProxy (systemd, `2398`, stats `8888`, оба закрыты nftables для не-lo). Подробно — раздел ниже |
| nginx | 80, 443 | фронт `ru.ruzzy.dev`: весь 443 → relay. Docker `nginx:alpine`, `~/gost/` |
| certbot | — | `certbot renew` раз в 12 ч, webroot `~/gost/certbot/www` |
Контейнеры: `telemt`, `wireguard`, `nginx` (docker compose в `~/gost` и `~/wireguard`).
Контейнеры: `nginx`, `certbot` (`~/gost`), `wireguard` (`~/wireguard`), агент мониторинга (`~/monitoring-agent`). Compose, nginx.conf и скрипт установки — копия в репо `stacks/tproxy/`.
## WEB-прокси Telegram (с 2026-10-09)
Новый тип прокси в клиентах Telegram (Desktop ≥ 7.1.0; мобильные — по сообщениям с 13.0, официально не подтверждено). Клиент держит скрытый WebView к `https://ru.ruzzy.dev/` и гонит по HTTPS/WSS мультиплексированные MTProxy-потоки. Сервер — официальный [`telegramdesktop/tproxy-server`](https://github.com/telegramdesktop/tproxy-server), коммит `c8adb8b` (2026-09-29).
- **Клиенту:** server `ru.ruzzy.dev`, secret — 32 hex из `profiles.json`. Ссылка: `https://t.me/webproxy?server=ru.ruzzy.dev&secret=<secret>`; получить на сервере — последней строкой вывода `setup-ru1.sh`.
- **Цепочка:** клиент → nginx `:443` (TLS, `ru.ruzzy.dev`) → relay `127.0.0.1:8090` → MTProxy `127.0.0.1:2398` → middle-end Telegram через `wg0` → fr1.
- **Mode A, без base path:** relay — единственный шлюз хоста и сам отдаёт публичный сайт из `/srv/tproxy-site` (копия маскировочного `~/gost/nginx/html/index.html`; перечитывается только при рестарте relay). Запросы без валидной capability (`?bridge=` = HMAC от секрета и имени хоста) получают обычный сайт. Base path не включали: по документации его понимает только Desktop.
- ⚠️ **NAT для MTProxy задан вручную:** `MTPROXY_NAT_ARGS=--nat-info 10.8.1.4:161.97.93.252` в `/etc/mtproxy/mtproxy.env`. Официальный MTProxy выводит AES-ключи middle-end сессии из своего адреса; трафик к Telegram с ru1 уходит в wg0 и выходит с IP **fr1**. Автоопределение апстримного `install.sh` спросило бы ifconfig-сервис через ens3, получило 85.198.109.86 — и MTProxy принимал бы клиентов, никому не отвечая, без ошибок нигде. Сменится exit-IP fr1 или адрес wg0 — править здесь. Симптом: `journalctl -u mtproxy | grep 'Disconnected from RPC Middle-End'`.
- **Юниты:** `tproxy-firewall` (nft-таблица `inet tproxy_backend`: 2398/8888 только с lo), `mtproxy`, `tproxy-server`, `refresh-mtproxy-config.timer` (ежедневно тянет `proxy-multi.conf`, рестарт MTProxy только при изменении). Бинарник relay `/usr/local/bin/tproxy-server` собран локально (`CGO_ENABLED=0 GOOS=linux GOARCH=amd64`), MTProxy — `/opt/MTProxy`, собран `deploy/install-mtproxy.sh` (закреплённый коммит + sha256).
- **Carrier: `websocket`** (с 2026-10-09, было `https` по умолчанию) — поле `carrier_mode` профиля в `profiles.json`: один WSS-канал `/api/v1/ws` на все потоки клиента вместо POST + long poll. Клиенту менять нечего, режим выбирает сервер. Relay пингует WSS раз в 25 с простоя (`long_poll`), поэтому `proxy_read_timeout 90s` в nginx канал не рвёт. Смена режима — `systemctl restart tproxy-server`, живые сессии переподключаются. `websocket-lanes` не взят: отдельный WSS на поток, на macOS WebKit сериализует рукопожатия и подключение может не уложиться в таймаут клиента. Бэкап профиля до правки — `profiles.json.bak-20261009`.
- ⚠️ Порты relay **8090/8091**, а не дефолтные 8080/8081: `*:8080` занят cadvisor из `~/monitoring-agent`.
- ⚠️ **Логи nginx выключены** (`access_log off`): в URI bridge-страницы — capability, в `Sec-WebSocket-Protocol` — токен сессии. Не включать.
- **Проверено 2026-10-09:** `readyz` 200; 10 ME-соединений с `10.8.1.4`, `Disconnected` — 0; obfuscated2 + `req_pq_multi` на `127.0.0.1:2398` → `resPQ` за ~60 мс (3/3); capability через публичный фронт → bridge-страница (200, свой CSP). **Живой клиент не проверялся.**
- **Диагностика:** `curl -s 127.0.0.1:8091/readyz`, `curl -s 127.0.0.1:8091/metrics | grep tproxy_sessions`, `journalctl -u tproxy-server -u mtproxy`.
- **Маршрут:** весь Telegram (`91.108.4/22`, `91.108.8/22`, `91.108.56/22`, `95.161.64/20`, `149.154.160/20`) — в main-таблице `dev wg0`. Упал wg0 — прокси не работает.
- **Сертификат** `ru.ruzzy.dev` (LE, webroot, только это имя; перевыпущен 2026-10-09, до 2027-01-07). Продление: `certbot` в контейнере каждые 12 ч; nginx сам делает `reload` раз в 6 ч (цикл в `command` compose).
⚠️ До 2026-10-09 контейнера certbot **не было вовсе**, хотя он был описан в compose: сертификат истёк 2026-07-01 и три месяца отдавался просроченным.
- ⚠️ `nginx.conf` смонтирован в контейнер **одним файлом**: замена через `mv`/редактор с новым inode контейнер не видит, `nginx -s reload` перечитает старый. После такой замены — `docker compose restart nginx` (2026-10-09 так получили минуту простоя 443).
- Открытый прокси для всех, кто знает secret; лимиты — дефолтные апстрима (128 сессий, 4096 потоков).
### История 2026-10-09
1. Под «web-proxy для Telegram» сначала понят Telegram Web в браузере: nginx-прокси на `web.telegram.org` с переписыванием адресов DC (`zws*.ru.ruzzy.dev`) — **убран** в тот же день, не то.
2. telemt (MTProxy, FakeTLS под `ru.ruzzy.dev`, был на 443, API `9091` торчал наружу) снят, возвращён, затем **удалён окончательно** по решению владельца: контейнер, образ, `~/gost/telemt-config` (со старым секретом), `telemt-data`, том `gost_mtproxy-config`.
3. Поднят WEB-прокси (выше). Бэкапы compose/nginx.conf до изменений — `~/gost/*.bak-20261009*`.
## Умный роутинг (tun0 и wg1)
- `ipset ru_ips` (~11400 подсетей, источник ipdeny.com). До routing decision (mangle PREROUTING):
@@ -40,7 +64,7 @@ VPN entry-нода в РФ. Точка входа в топологии **RU↔D
- tun1 — без умного роутинга (всегда RU/ens3).
## AWG Watchdog
- `~/awg-watchdog.sh` в root-cron **каждые 3 мин**: если handshake `wg0` старше 5 мин → рестарт `awg-quick@wg0` + чистка дублей iptables. Лог `~/awg-watchdog.log`. (telemt зависит от wg0 — Telegram DC маршрутизируются через туннель до de1.)
- `~/awg-watchdog.sh` в root-cron **каждые 3 мин**: если handshake `wg0` старше 5 мин → рестарт `awg-quick@wg0` + чистка дублей iptables. Лог `~/awg-watchdog.log`. (WEB-прокси зависит от wg0: адреса Telegram с хоста идут через туннель.)
## Клиенты
- OpenVPN: `~/gen_client.sh --profile de|ru|both <имя>`, PKI `~/easy-rsa/pki`, профили `~/clients/`. CRL отключён.
@@ -60,3 +84,10 @@ VPN entry-нода в РФ. Точка входа в топологии **RU↔D
## ⚠️ Грабли (из README)
- `wg1.conf` обязан иметь `Table = off` — иначе wg-quick при рестарте сносит default route и сервер теряет SSH (восстановление: `ip route add default via 85.198.109.1 dev ens3`).
- Таблица маршрутизации — `vpn` (100), не `awg`.
## Открытые хвосты
- **Живой клиент через WEB-прокси не проверен** (2026-10-09): серверная цепочка проверена по частям (см. раздел WEB-прокси). Поддержка в мобильных клиентах подтверждена только новостью про Telegram 13.0.
- **DNS `*.ru.ruzzy.dev`** (CNAME → `ru.ruzzy.dev`, Namecheap) остался от веб-прокси Telegram Web — больше не нужен, удалить. Имена `zws*.ru.ruzzy.dev` уже попали в CT-логи через сертификат от 2026-10-09 (Let's Encrypt) — это не отменить.
- `/tmp/tproxy-server` на сервере — исходники и `setup-ru1.sh`, нужны для `deploy/update-relay.sh`; `/tmp` чистится при ребуте — при обновлении заливать заново.
- `~/gost/manage.sh` — от прежнего gost/SOCKS5, не используется.
- Замечено при снапшоте 2026-10-09, не разбиралось: `iperf3 --server` (порт 5201 наружу, пользователь `iperf3`, PPID 1), cadvisor `unhealthy`. Остальное описание (OpenVPN, AWG, клиенты) сверялось до переключения на fr1 — см. строку в `AGENTS.md`.
+25
View File
@@ -0,0 +1,25 @@
# ru1: ~/gost/docker-compose.yml (каталог и имя проекта `gost` — исторические).
# Фронт WEB-прокси Telegram. Сам relay и MTProxy — systemd на хосте
# (setup-ru1.sh), nginx в host-network проксирует на 127.0.0.1:8090.
services:
nginx:
image: nginx:alpine
container_name: nginx
network_mode: host
restart: unless-stopped
# certbot живёт в отдельном контейнере и не может дёрнуть nginx —
# перечитываем сертификаты раз в 6 часов сами.
command: ["/bin/sh", "-c", "while :; do sleep 6h; nginx -s reload; done & exec nginx -g 'daemon off;'"]
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./certbot/conf:/etc/letsencrypt:ro
- ./certbot/www:/var/www/certbot:ro
certbot:
image: certbot/certbot
container_name: certbot
restart: unless-stopped
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --quiet; sleep 12h & wait $${!}; done;'"
+67
View File
@@ -0,0 +1,67 @@
events {}
http {
server_tokens off;
# Ничего не логируем по ru.ruzzy.dev: в URI bridge-страницы — capability,
# в Sec-WebSocket-Protocol — токен сессии (требование tproxy-server).
access_log off;
map $http_upgrade $connection_upgrade {
default upgrade;
'' "";
}
upstream tproxy_relay {
server 127.0.0.1:8090; # tproxy-server (systemd), 8080 занят cadvisor
keepalive 32;
}
server {
listen 80;
server_name ru.ruzzy.dev;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# Mode A из tproxy-server: relay — единственный шлюз хоста, он же отдаёт
# публичный сайт (/srv/tproxy-site). Никаких отдельных location под сайт,
# иначе ответы сайта и транспорта станут различимы для зондирования.
server {
listen 443 ssl;
http2 on;
server_name ru.ruzzy.dev;
ssl_certificate /etc/letsencrypt/live/ru.ruzzy.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ru.ruzzy.dev/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
gzip on;
# Пачки uplink до 2 MiB; дефолтный 1 MiB ломает транспорт с 413.
client_max_body_size 4m;
location / {
proxy_pass http://tproxy_relay;
proxy_http_version 1.1;
proxy_set_header Host $host;
# Ровно один адрес: список relay отвергает.
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# long poll relay — 25 с; не резать и не буферизовать.
proxy_read_timeout 90s;
proxy_send_timeout 90s;
proxy_buffering off;
proxy_request_buffering off;
}
}
}
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Однократная установка WEB-прокси Telegram (tproxy-server + официальный MTProxy)
# на ru1. Запускать от root из каталога с исходниками tproxy-server:
# sudo bash /tmp/tproxy-server/setup-ru1.sh
# Повторный запуск безопасен: секрет и token.key не перегенерируются.
#
# Отличия от deploy/install.sh апстрима:
# - без Caddy: 80/443 держит nginx в docker (~/gost), он проксирует всё в relay;
# - relay на 127.0.0.1:8090/8091 — 8080 занят cadvisor (*:8080);
# - NAT задан вручную: трафик к Telegram идёт через wg0 и выходит с IP fr1,
# а автоопределение install.sh спросило бы ifconfig-сервис через ens3
# и получило 85.198.109.86 — MTProxy молча перестал бы отвечать клиентам;
# - без base path: его пока понимает только Desktop.
set -euo pipefail
umask 077
repo="$(cd "$(dirname "$0")" && pwd)"
hostname=ru.ruzzy.dev
nat_args="--nat-info 10.8.1.4:161.97.93.252"
[[ "${EUID}" -eq 0 ]] || { echo "run as root" >&2; exit 1; }
cd "$repo"
# 1. Официальный MTProxy (закреплённый коммит, sha256) + конфиг Telegram.
bash deploy/install-mtproxy.sh
# 2. Relay.
id tproxy >/dev/null 2>&1 || useradd --system --home /nonexistent --shell /usr/sbin/nologin tproxy
install -o root -g root -m 0755 tproxy-server.bin /usr/local/bin/tproxy-server
install -d -o root -g root -m 0755 /srv/tproxy-site
install -o root -g root -m 0644 /home/mik/gost/nginx/html/index.html /srv/tproxy-site/index.html
install -d -o root -g tproxy -m 0750 /etc/tproxy-server
bash deploy/ensure-token-key.sh
if [[ ! -f /etc/tproxy-server/profiles.json ]]; then
secret="$(openssl rand -hex 16)"
printf '{"profiles":[{"name":"default","secret":"%s","backend":"127.0.0.1:2398"}]}\n' \
"$secret" > /etc/tproxy-server/profiles.json
printf 'MTPROXY_SECRET=%s\nMTPROXY_NAT_ARGS=%s\n' "$secret" "$nat_args" > /etc/mtproxy/mtproxy.env
unset secret
fi
cat > /etc/tproxy-server/config.json <<EOF
{
"public_hostname": "$hostname",
"base_path": "",
"listen": "127.0.0.1:8090",
"admin_listen": "127.0.0.1:8091",
"public_dir": "/srv/tproxy-site",
"static_routes": "exact",
"profiles_file": "/run/credentials/tproxy-server.service/profiles.json"
}
EOF
chown root:tproxy /etc/tproxy-server/config.json /etc/tproxy-server/profiles.json
chmod 0640 /etc/tproxy-server/config.json
chmod 0400 /etc/tproxy-server/profiles.json
chown root:mtproxy /etc/mtproxy/mtproxy.env
chmod 0640 /etc/mtproxy/mtproxy.env
# 3. Юниты, firewall (2398/8888 только с lo), ежедневное обновление конфига Telegram.
for unit in tproxy-server.service mtproxy.service tproxy-firewall.service \
refresh-mtproxy-config.service refresh-mtproxy-config.timer; do
install -o root -g root -m 0644 "deploy/$unit" "/etc/systemd/system/$unit"
done
install -o root -g root -m 0644 deploy/firewall.nft /etc/tproxy-server/firewall.nft
install -o root -g root -m 0755 deploy/refresh-mtproxy-config.sh /usr/local/sbin/refresh-mtproxy-config
/usr/local/bin/tproxy-server -config /etc/tproxy-server/config.json \
-profiles-file /etc/tproxy-server/profiles.json -check
systemctl daemon-reload
systemctl enable --now tproxy-firewall.service
systemctl enable --now mtproxy.service
systemctl restart mtproxy.service
systemctl enable --now tproxy-server.service
systemctl enable --now refresh-mtproxy-config.timer
for _ in $(seq 20); do
curl -fs -o /dev/null http://127.0.0.1:8091/readyz && break
sleep 1
done
curl -fsS http://127.0.0.1:8091/readyz >/dev/null && echo "relay: ready"
echo "Ссылка для клиента: sudo sed -n 's/.*\"secret\":\"\\([0-9a-f]*\\)\".*/https:\\/\\/t.me\\/webproxy?server=$hostname\\&secret=\\1/p' /etc/tproxy-server/profiles.json"