diff --git a/AGENTS.md b/AGENTS.md index 31498fd..d98458e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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` в том же прогоне. Контракт и diff --git a/servers/ru1.md b/servers/ru1.md index 1e25858..f5e923b 100644 --- a/servers/ru1.md +++ b/servers/ru1.md @@ -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=`; получить на сервере — последней строкой вывода `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`. diff --git a/stacks/tproxy/docker-compose.yml b/stacks/tproxy/docker-compose.yml new file mode 100644 index 0000000..a6fb7ab --- /dev/null +++ b/stacks/tproxy/docker-compose.yml @@ -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;'" diff --git a/stacks/tproxy/nginx.conf b/stacks/tproxy/nginx.conf new file mode 100644 index 0000000..5038e25 --- /dev/null +++ b/stacks/tproxy/nginx.conf @@ -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; + } + } +} diff --git a/stacks/tproxy/setup-ru1.sh b/stacks/tproxy/setup-ru1.sh new file mode 100644 index 0000000..f2a2c2a --- /dev/null +++ b/stacks/tproxy/setup-ru1.sh @@ -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 </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"