mirror of
https://github.com/by-sonic/tglock.git
synced 2026-08-02 08:35:51 +03:00
feat(cli): headless tglock-cli, GUI behind a feature, deep test coverage (#25)
Реализует направление PR #15 поверх архитектуры 2.0. Сам PR смерджить нельзя: он патчит src/bypass.rs, src/network.rs и src/ws_proxy.rs, которых больше нет, и правит системный DNS — в 2.0 это не нужно, потому что адреса Telegram зашиты в маршрутах, а SNI остаётся настоящим. Взято разделение GUI/CLI и произвольный bind-адрес, отброшены DNS-менеджмент и проверка root: CLI не требует прав. Closes #10, #17 — GUI не создаёт окно без 3D-ускорения, на машине без монитора и в виртуалке. Причина в WebView под Tauri, поэтому лечится не программным рендером, а бинарём, в котором WebView нет вовсе: при выключенной фиче gui Tauri и фронтенд в сборку не попадают. Отдельная задача CI собирает и гоняет CLI на голом ubuntu без Node.js и без libwebkit2gtk. Структура: - src/lib.rs — ядро (mtproto, proxy, transport, config), без Tauri - src/main.rs — GUI, required-features = ["gui"] - src/bin/cli.rs — headless-бинарь на clap - build.rs вызывает tauri_build только при включённой фиче gui Исправлено по пути: - Определение протокола: SOCKS5 и MTProto различались по первому байту, но is_reserved_init не исключает 0x05, поэтому примерно одно соединение из 256 уезжало в SOCKS5-ветку и умирало. Теперь неоднозначный первый байт решается по полному 64-байтному init и секрету. - Ярлык маршрута в UI: код 2 подписывался как «Cloudflare Worker», хотя это запасной Telegram IP, а системный DNS и настоящий Worker оба показывались как «Автоматический маршрут». Метки переехали в transport::route_label, общий для обоих интерфейсов. - Секрет прокси: под DynamicUser и ProtectHome домашней папки нет, secret_path возвращает None и секрет генерировался заново при каждом старте, ломая всем настроенным клиентам tg://-ссылку. Добавлен --secret-file. - README обещал Rust 1.75+, тогда как Cargo.toml требует 1.88 и CI это проверяет. Это и есть первопричина #3. Политика доступа: прямые не-Telegram соединения разрешены только на loopback, на любом сетевом адресе нужен явный --allow-direct. Правило из ISSUE_AUDIT о том, что LAN не должен становиться открытым SOCKS5, теперь выражено в типе ListenConfig и покрыто тестами. Тесты: 46 в библиотеке + 12 в CLI. Появился сквозной тест туннеля против мок-релея, который реализует сторону Telegram по obfuscated2 — проверяется не внутренняя консистентность, а что реле получает ровно тот открытый текст, который отправил клиент, и обратно. Плюс расписание backoff, фолбэк при всех маршрутах в cooldown, валидация Worker-доменов, отказы SOCKS5, устойчивость секрета к перезапуску и корректная остановка по SIGTERM. Документация: секция CLI в README с юнитом systemd и Dockerfile. Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
This commit is contained in:
@@ -79,6 +79,9 @@ TGLock — это **локальный SOCKS5-прокси** на твоём к
|
||||
| **Windows 10/11** (x64) | `.exe` installer | |
|
||||
| **macOS** (Apple Silicon + Intel) | universal `.dmg` | |
|
||||
| **Linux** (x86_64) | `.AppImage` / `.deb` | |
|
||||
| **Сервер / без монитора** (любая ОС) | `tglock-cli-*` | |
|
||||
|
||||
> **🖥 `tglock-cli`** — тот же туннель без графического интерфейса, одним бинарём. Нужен, если окно не создаётся: сервер, контейнер, виртуалка, машина без монитора или без 3D-ускорения. Подробности — [ниже](#-без-графического-интерфейса-tglock-cli).
|
||||
|
||||
> **🍎 macOS:** пока сборка не нотарифицирована Apple, при первом запуске может понадобиться:
|
||||
> ```bash
|
||||
@@ -108,6 +111,81 @@ Telegram → Настройки → **Продвинутые** → Тип сое
|
||||
|
||||
Удобно, если дома один комп всегда включён — он становится «домашним Telegram-роутером».
|
||||
|
||||
### 🖥 Без графического интерфейса: `tglock-cli`
|
||||
|
||||
Для сервера, виртуалки, контейнера и машины без монитора или без 3D-ускорения. Это отдельный бинарь, в котором **нет ни Tauri, ни системного WebView** — там, где окно просто не создаётся, CLI работает.
|
||||
|
||||
```bash
|
||||
tglock-cli # 127.0.0.1:1080, только для этого компьютера
|
||||
tglock-cli --lan # 0.0.0.0:1080, только адреса Telegram
|
||||
tglock-cli --bind 10.0.0.5 --port 1443 # свой адрес и порт
|
||||
tglock-cli --worker my-name.workers.dev # резервный маршрут через свой Cloudflare Worker
|
||||
tglock-cli --help # все флаги
|
||||
```
|
||||
|
||||
При запуске печатается готовая `tg://proxy`-ссылка — её можно открыть на любом устройстве в сети, чтобы Telegram настроился сам. Дальше в лог идёт по строке на каждое изменение состояния: сколько соединений, какой дата-центр, какой маршрут живой, сколько сбоев.
|
||||
|
||||
Прав администратора не нужно: TGLock не правит ни системный DNS, ни файл `hosts` — нужные адреса Telegram зашиты в маршрутах, а TLS SNI остаётся настоящим.
|
||||
|
||||
`--lan` и любой другой сетевой адрес пропускают **только** адреса Telegram. Обычным SOCKS5-прокси TGLock становится исключительно по явному `--allow-direct`, и на сетевом адресе это открытый прокси для всего интернета — включайте осознанно.
|
||||
|
||||
#### Юнит для systemd
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=TGLock — Telegram через WebSocket-туннель
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
ExecStart=/usr/local/bin/tglock-cli --lan --secret-file /var/lib/tglock/secret
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
StateDirectory=tglock
|
||||
DynamicUser=yes
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo install -m755 tglock-cli-x86_64-unknown-linux-gnu /usr/local/bin/tglock-cli
|
||||
sudo systemctl enable --now tglock
|
||||
journalctl -u tglock -f
|
||||
```
|
||||
|
||||
`--secret-file` здесь обязателен, и это не украшение: секрет — половина `tg://proxy`-ссылки. Без файла он генерируется заново при каждом старте, и после первого же `systemctl restart` все настроенные клиенты перестанут подключаться. `StateDirectory=tglock` создаёт `/var/lib/tglock` с нужными правами, а сам файл пишется с режимом `600`.
|
||||
|
||||
Остановка по `systemctl stop` приходит как `SIGTERM` — CLI закрывает туннели и выходит с нулевым кодом, а не умирает по `SIGKILL`.
|
||||
|
||||
#### Docker
|
||||
|
||||
```dockerfile
|
||||
FROM rust:1.88 AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN cargo build --release --locked --no-default-features --bin tglock-cli
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=build /src/target/release/tglock-cli /usr/local/bin/tglock-cli
|
||||
EXPOSE 1080
|
||||
ENTRYPOINT ["tglock-cli", "--lan", "--secret-file", "/data/secret"]
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -d --name tglock -p 1080:1080 -v tglock-data:/data tglock
|
||||
```
|
||||
|
||||
Образу не нужны ни Node.js, ни `libwebkit2gtk` — только `ca-certificates` для проверки сертификата Telegram.
|
||||
|
||||
---
|
||||
|
||||
## 🔬 Как это работает
|
||||
@@ -262,7 +340,17 @@ npm ci
|
||||
npm run tauri build
|
||||
```
|
||||
|
||||
Результат — `target/release/tglock` (или `tglock.exe` на Windows). Требуется Rust **stable 1.75+**.
|
||||
Результат — `target/release/tglock` (или `tglock.exe` на Windows).
|
||||
|
||||
Минимальная версия Rust — **1.88** (`rust-version` в `Cargo.toml`, проверяется отдельной задачей в CI). На более старых тулчейнах зависимости не соберутся: часть из них требует edition 2024.
|
||||
|
||||
### Только CLI, без графики
|
||||
|
||||
```bash
|
||||
cargo build --release --locked --no-default-features --bin tglock-cli
|
||||
```
|
||||
|
||||
Ни Node.js, ни фронтенда, ни `libwebkit2gtk` для этого не нужно — при выключенной фиче `gui` Tauri и системный WebView в сборку не попадают вообще. Именно так CLI собирается на голом сервере.
|
||||
|
||||
### Кросс-компиляция через GitHub Actions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user