mirror of
https://github.com/telemt/telemt.git
synced 2026-06-13 06:21:44 +03:00
Compare commits
16 Commits
3.3.38
...
715eec5386
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
715eec5386 | ||
|
|
b3f11624c9 | ||
|
|
2d15eb1f6d | ||
|
|
60a2edd6fe | ||
|
|
4d87a790cc | ||
|
|
07fed8f871 | ||
|
|
407d686d49 | ||
|
|
eac5cc81fb | ||
|
|
c51d16f403 | ||
|
|
b5146bba94 | ||
|
|
5ed525fa48 | ||
|
|
9f7c1693ce | ||
|
|
1524396e10 | ||
|
|
444a20672d | ||
|
|
d673935b6d | ||
|
|
363b5014f7 |
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -151,6 +151,14 @@ jobs:
|
||||
mkdir -p dist
|
||||
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
||||
|
||||
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then
|
||||
STRIP_BIN=aarch64-linux-gnu-strip
|
||||
else
|
||||
STRIP_BIN=strip
|
||||
fi
|
||||
|
||||
"${STRIP_BIN}" dist/telemt
|
||||
|
||||
cd dist
|
||||
tar -czf "${{ matrix.asset }}.tar.gz" \
|
||||
--owner=0 --group=0 --numeric-owner \
|
||||
@@ -279,6 +287,14 @@ jobs:
|
||||
mkdir -p dist
|
||||
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" dist/telemt
|
||||
|
||||
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
|
||||
STRIP_BIN=aarch64-linux-musl-strip
|
||||
else
|
||||
STRIP_BIN=strip
|
||||
fi
|
||||
|
||||
"${STRIP_BIN}" dist/telemt
|
||||
|
||||
cd dist
|
||||
tar -czf "${{ matrix.asset }}.tar.gz" \
|
||||
--owner=0 --group=0 --numeric-owner \
|
||||
|
||||
14
LICENSE
14
LICENSE
@@ -14,11 +14,15 @@ are preserved and complied with.
|
||||
The canonical version of this License is the English version.
|
||||
Official translations are provided for informational purposes only
|
||||
and for convenience, and do not have legal force. In case of any
|
||||
discrepancy, the English version of this License shall prevail.
|
||||
Available versions:
|
||||
- English in Markdown: docs/LICENSE/LICENSE.md
|
||||
- German: docs/LICENSE/LICENSE.de.md
|
||||
- Russian: docs/LICENSE/LICENSE.ru.md
|
||||
discrepancy, the English version of this License shall prevail
|
||||
|
||||
/----------------------------------------------------------\
|
||||
| Language | Location |
|
||||
|-------------|--------------------------------------------|
|
||||
| English | docs/LICENSE/TELEMT-PUBLIC-LICENSE-3.en.md |
|
||||
| German | docs/LICENSE/TELEMT-PUBLIC-LICENSE-3.de.md |
|
||||
| Russian | docs/LICENSE/TELEMT-PUBLIC-LICENSE-3.ru.md |
|
||||
\----------------------------------------------------------/
|
||||
|
||||
### License Versioning Policy
|
||||
|
||||
|
||||
191
README.md
191
README.md
@@ -2,189 +2,60 @@
|
||||
|
||||
***Löst Probleme, bevor andere überhaupt wissen, dass sie existieren*** / ***It solves problems before others even realize they exist***
|
||||
|
||||
### [**Telemt Chat in Telegram**](https://t.me/telemtrs)
|
||||
#### Fixed TLS ClientHello is now available in Telegram Desktop starting from version 6.7.2: to work with EE-MTProxy, please update your client;
|
||||
#### Fixed TLS ClientHello for Telegram Android Client is available in [our chat](https://t.me/telemtrs/30234/36441); official releases for Android and iOS are "work in progress";
|
||||
> [!NOTE]
|
||||
>
|
||||
> Fixed TLS ClientHello is now available in **Telegram Desktop** starting from version **6.7.2**: to work with EE-MTProxy, please update your client;
|
||||
>
|
||||
> Fixed TLS ClientHello for Telegram Android Client is available in [our chat](https://t.me/telemtrs/30234/36441); **official releases for Android and iOS are "work in progress"**;
|
||||
|
||||
<p align="center">
|
||||
<a href="https://t.me/telemtrs">
|
||||
<img src="docs/assets/telegram_button.png" alt="Join us in Telegram" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
**Telemt** is a fast, secure, and feature-rich server written in Rust: it fully implements the official Telegram proxy algo and adds many production-ready improvements such as:
|
||||
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md)
|
||||
- [Full-covered API w/ management](https://github.com/telemt/telemt/blob/main/docs/API.md)
|
||||
- Anti-Replay on Sliding Window
|
||||
- Prometheus-format Metrics
|
||||
- TLS-Fronting and TCP-Splicing for masking from "prying" eyes
|
||||
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md);
|
||||
- [Full-covered API w/ management](https://github.com/telemt/telemt/blob/main/docs/API.md);
|
||||
- Anti-Replay on Sliding Window;
|
||||
- Prometheus-format Metrics;
|
||||
- TLS-Fronting and TCP-Splicing for masking from "prying" eyes.
|
||||
|
||||

|
||||
|
||||
⚓ Our implementation of **TLS-fronting** is one of the most deeply debugged, focused, advanced and *almost* **"behaviorally consistent to real"**: we are confident we have it right - [see evidence on our validation and traces](#recognizability-for-dpi-and-crawler)
|
||||
|
||||
⚓ Our ***Middle-End Pool*** is fastest by design in standard scenarios, compared to other implementations of connecting to the Middle-End Proxy: non dramatically, but usual
|
||||
|
||||
- Full support for all official MTProto proxy modes:
|
||||
- Classic
|
||||
- Secure - with `dd` prefix
|
||||
- Fake TLS - with `ee` prefix + SNI fronting
|
||||
- Replay attack protection
|
||||
- Optional traffic masking: forward unrecognized connections to a real web server, e.g. GitHub 🤪
|
||||
- Configurable keepalives + timeouts + IPv6 and "Fast Mode"
|
||||
- Graceful shutdown on Ctrl+C
|
||||
- Extensive logging via `trace` and `debug` with `RUST_LOG` method
|
||||
- Classic;
|
||||
- Secure - with `dd` prefix;
|
||||
- Fake TLS - with `ee` prefix + SNI fronting;
|
||||
- Replay attack protection;
|
||||
- Optional traffic masking: forward unrecognized connections to a real web server, e.g. GitHub 🤪;
|
||||
- Configurable keepalives + timeouts + IPv6 and "Fast Mode";
|
||||
- Graceful shutdown on Ctrl+C;
|
||||
- Extensive logging via `trace` and `debug` with `RUST_LOG` method.
|
||||
|
||||
# GOTO
|
||||
- [Quick Start Guide](#quick-start-guide)
|
||||
- [FAQ](#faq)
|
||||
- [Recognizability for DPI and crawler](#recognizability-for-dpi-and-crawler)
|
||||
- [Client WITH secret-key accesses the MTProxy resource:](#client-with-secret-key-accesses-the-mtproxy-resource)
|
||||
- [Client WITHOUT secret-key gets transparent access to the specified resource:](#client-without-secret-key-gets-transparent-access-to-the-specified-resource)
|
||||
- [Telegram Calls via MTProxy](#telegram-calls-via-mtproxy)
|
||||
- [How does DPI see MTProxy TLS?](#how-does-dpi-see-mtproxy-tls)
|
||||
- [Whitelist on IP](#whitelist-on-ip)
|
||||
- [Too many open files](#too-many-open-files)
|
||||
- [Architecture](docs/Architecture)
|
||||
- [Quick Start Guide](#quick-start-guide)
|
||||
- [Config parameters](docs/Config_params)
|
||||
- [Build](#build)
|
||||
- [Why Rust?](#why-rust)
|
||||
- [Issues](#issues)
|
||||
- [Roadmap](#roadmap)
|
||||
|
||||
|
||||
## Quick Start Guide
|
||||
- [Quick Start Guide RU](docs/QUICK_START_GUIDE.ru.md)
|
||||
- [Quick Start Guide EN](docs/QUICK_START_GUIDE.en.md)
|
||||
- [Quick Start Guide RU](docs/Quick_start/QUICK_START_GUIDE.ru.md)
|
||||
- [Quick Start Guide EN](docs/Quick_start/QUICK_START_GUIDE.en.md)
|
||||
|
||||
## FAQ
|
||||
|
||||
- [FAQ RU](docs/FAQ.ru.md)
|
||||
- [FAQ EN](docs/FAQ.en.md)
|
||||
|
||||
### Recognizability for DPI and crawler
|
||||
|
||||
On April 1, 2026, we became aware of a method for detecting MTProxy Fake-TLS,
|
||||
based on the ECH extension and the ordering of cipher suites,
|
||||
as well as an overall unique JA3/JA4 fingerprint
|
||||
that does not occur in modern browsers:
|
||||
we have already submitted initial changes to the Telegram Desktop developers and are working on updates for other clients.
|
||||
|
||||
- We consider this a breakthrough aspect, which has no stable analogues today
|
||||
- Based on this: if `telemt` configured correctly, **TLS mode is completely identical to real-life handshake + communication** with a specified host
|
||||
- Here is our evidence:
|
||||
- 212.220.88.77 - "dummy" host, running `telemt`
|
||||
- `petrovich.ru` - `tls` + `masking` host, in HEX: `706574726f766963682e7275`
|
||||
- **No MITM + No Fake Certificates/Crypto** = pure transparent *TCP Splice* to "best" upstream: MTProxy or tls/mask-host:
|
||||
- DPI see legitimate HTTPS to `tls_host`, including *valid chain-of-trust* and entropy
|
||||
- Crawlers completely satisfied receiving responses from `mask_host`
|
||||
#### Client WITH secret-key accesses the MTProxy resource:
|
||||
|
||||
<img width="360" height="439" alt="telemt" src="https://github.com/user-attachments/assets/39352afb-4a11-4ecc-9d91-9e8cfb20607d" />
|
||||
|
||||
#### Client WITHOUT secret-key gets transparent access to the specified resource:
|
||||
- with trusted certificate
|
||||
- with original handshake
|
||||
- with full request-response way
|
||||
- with low-latency overhead
|
||||
```bash
|
||||
root@debian:~/telemt# curl -v -I --resolve petrovich.ru:443:212.220.88.77 https://petrovich.ru/
|
||||
* Added petrovich.ru:443:212.220.88.77 to DNS cache
|
||||
* Hostname petrovich.ru was found in DNS cache
|
||||
* Trying 212.220.88.77:443...
|
||||
* Connected to petrovich.ru (212.220.88.77) port 443 (#0)
|
||||
* ALPN: offers h2,http/1.1
|
||||
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
|
||||
* CAfile: /etc/ssl/certs/ca-certificates.crt
|
||||
* CApath: /etc/ssl/certs
|
||||
* TLSv1.3 (IN), TLS handshake, Server hello (2):
|
||||
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
|
||||
* TLSv1.3 (IN), TLS handshake, Certificate (11):
|
||||
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
|
||||
* TLSv1.3 (IN), TLS handshake, Finished (20):
|
||||
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
|
||||
* TLSv1.3 (OUT), TLS handshake, Finished (20):
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
* ALPN: server did not agree on a protocol. Uses default.
|
||||
* Server certificate:
|
||||
* subject: C=RU; ST=Saint Petersburg; L=Saint Petersburg; O=STD Petrovich; CN=*.petrovich.ru
|
||||
* start date: Jan 28 11:21:01 2025 GMT
|
||||
* expire date: Mar 1 11:21:00 2026 GMT
|
||||
* subjectAltName: host "petrovich.ru" matched cert's "petrovich.ru"
|
||||
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
|
||||
* SSL certificate verify ok.
|
||||
* using HTTP/1.x
|
||||
> HEAD / HTTP/1.1
|
||||
> Host: petrovich.ru
|
||||
> User-Agent: curl/7.88.1
|
||||
> Accept: */*
|
||||
>
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* old SSL session ID is stale, removing
|
||||
< HTTP/1.1 200 OK
|
||||
HTTP/1.1 200 OK
|
||||
< Server: Variti/0.9.3a
|
||||
Server: Variti/0.9.3a
|
||||
< Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Origin: *
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Cache-Control: no-store
|
||||
Cache-Control: no-store
|
||||
< Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Pragma: no-cache
|
||||
Pragma: no-cache
|
||||
< Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Content-Length: 31253
|
||||
Content-Length: 31253
|
||||
< Connection: keep-alive
|
||||
Connection: keep-alive
|
||||
< Keep-Alive: timeout=60
|
||||
Keep-Alive: timeout=60
|
||||
|
||||
<
|
||||
* Connection #0 to host petrovich.ru left intact
|
||||
|
||||
```
|
||||
- We challenged ourselves, we kept trying and we didn't only *beat the air*: now, we have something to show you
|
||||
- Do not just take our word for it? - This is great and we respect that: you can build your own `telemt` or download a build and check it right now
|
||||
### Telegram Calls via MTProxy
|
||||
- Telegram architecture **does NOT allow calls via MTProxy**, but only via SOCKS5, which cannot be obfuscated
|
||||
### How does DPI see MTProxy TLS?
|
||||
- DPI sees MTProxy in Fake TLS (ee) mode as TLS 1.3
|
||||
- the SNI you specify sends both the client and the server;
|
||||
- ALPN is similar to HTTP 1.1/2;
|
||||
- high entropy, which is normal for AES-encrypted traffic;
|
||||
### Whitelist on IP
|
||||
- MTProxy cannot work when there is:
|
||||
- no IP connectivity to the target host: Russian Whitelist on Mobile Networks - "Белый список"
|
||||
- OR all TCP traffic is blocked
|
||||
- OR high entropy/encrypted traffic is blocked: content filters at universities and critical infrastructure
|
||||
- OR all TLS traffic is blocked
|
||||
- OR specified port is blocked: use 443 to make it "like real"
|
||||
- OR provided SNI is blocked: use "officially approved"/innocuous name
|
||||
- like most protocols on the Internet;
|
||||
- these situations are observed:
|
||||
- in China behind the Great Firewall
|
||||
- in Russia on mobile networks, less in wired networks
|
||||
- in Iran during "activity"
|
||||
### Too many open files
|
||||
- On a fresh Linux install the default open file limit is low; under load `telemt` may fail with `Accept error: Too many open files`
|
||||
- **Systemd**: add `LimitNOFILE=65536` to the `[Service]` section (already included in the example above)
|
||||
- **Docker**: add `--ulimit nofile=65536:65536` to your `docker run` command, or in `docker-compose.yml`:
|
||||
```yaml
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
```
|
||||
- **System-wide** (optional): add to `/etc/security/limits.conf`:
|
||||
```
|
||||
* soft nofile 1048576
|
||||
* hard nofile 1048576
|
||||
root soft nofile 1048576
|
||||
root hard nofile 1048576
|
||||
```
|
||||
|
||||
|
||||
## Build
|
||||
```bash
|
||||
# Cloning repo
|
||||
@@ -207,7 +78,7 @@ telemt config.toml
|
||||
```
|
||||
|
||||
### OpenBSD
|
||||
- Build and service setup guide: [OpenBSD Guide (EN)](docs/OPENBSD.en.md)
|
||||
- Build and service setup guide: [OpenBSD Guide (EN)](docs/Quick_start/OPENBSD_QUICK_START_GUIDE.en.md)
|
||||
- Example rc.d script: [contrib/openbsd/telemt.rcd](contrib/openbsd/telemt.rcd)
|
||||
- Status: OpenBSD sandbox hardening with `pledge(2)` and `unveil(2)` is not implemented yet.
|
||||
|
||||
|
||||
123
README.ru.md
Normal file
123
README.ru.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Telemt — MTProxy на Rust + Tokio
|
||||
|
||||
***Решает проблемы раньше, чем другие узнают об их существовании***
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Исправленный TLS ClientHello доступен в **Telegram Desktop** начиная с версии **6.7.2**: для работы с EE-MTProxy обновите клиент.
|
||||
>
|
||||
> Исправленный TLS ClientHello для Telegram Android доступен в нашем чате; **официальные релизы для Android и iOS находятся в процессе разработки**.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://t.me/telemtrs">
|
||||
<img src="docs/assets/telegram_button.png" alt="Мы в Telegram" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
**Telemt** — это быстрый, безопасный и функциональный сервер, написанный на Rust. Он полностью реализует официальный алгоритм прокси Telegram и добавляет множество улучшений для продакшена:
|
||||
|
||||
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + жизненный цикл генераций](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md);
|
||||
- [Полноценный API с управлением](https://github.com/telemt/telemt/blob/main/docs/API.md);
|
||||
- Защита от повторных атак (Anti-Replay on Sliding Window);
|
||||
- Метрики в формате Prometheus;
|
||||
- TLS-fronting и TCP-splicing для маскировки от DPI.
|
||||
|
||||

|
||||
|
||||
## Особенности
|
||||
|
||||
⚓ Реализация **TLS-fronting** максимально приближена к поведению реального HTTPS-трафика.
|
||||
|
||||
⚓ ***Middle-End Pool*** оптимизирован для высокой производительности.
|
||||
|
||||
- Поддержка всех режимов MTProto proxy:
|
||||
- Classic;
|
||||
- Secure (префикс `dd`);
|
||||
- Fake TLS (префикс `ee` + SNI fronting);
|
||||
- Защита от replay-атак;
|
||||
- Маскировка трафика (перенаправление неизвестных подключений на реальные сайты);
|
||||
- Настраиваемые keepalive, таймауты, IPv6 и «быстрый режим»;
|
||||
- Корректное завершение работы (Ctrl+C);
|
||||
- Подробное логирование через `trace` и `debug`.
|
||||
|
||||
# Навигация
|
||||
- [FAQ](#faq)
|
||||
- [Архитектура](docs/Architecture)
|
||||
- [Быстрый старт](#quick-start-guide)
|
||||
- [Параметры конфигурационного файла](docs/Config_params)
|
||||
- [Сборка](#build)
|
||||
- [Почему Rust?](#why-rust)
|
||||
- [Известные проблемы](#issues)
|
||||
- [Планы](#roadmap)
|
||||
|
||||
## Быстрый старт
|
||||
- [Quick Start Guide RU](docs/Quick_start/QUICK_START_GUIDE.ru.md)
|
||||
- [Quick Start Guide EN](docs/Quick_start/QUICK_START_GUIDE.en.md)
|
||||
|
||||
## FAQ
|
||||
|
||||
- [FAQ RU](docs/FAQ.ru.md)
|
||||
- [FAQ EN](docs/FAQ.en.md)
|
||||
|
||||
## Сборка
|
||||
|
||||
```bash
|
||||
# Клонируйте репозиторий
|
||||
git clone https://github.com/telemt/telemt
|
||||
# Смените каталог на telemt
|
||||
cd telemt
|
||||
# Начните процесс сборки
|
||||
cargo build --release
|
||||
|
||||
# Устройства с небольшим объёмом оперативной памяти (1 ГБ, например NanoPi Neo3 / Raspberry Pi Zero 2):
|
||||
# используется параметр lto = «thin» для уменьшения пикового потребления памяти.
|
||||
# Если ваш пользовательский набор инструментов переопределяет профили, не используйте Fat LTO.
|
||||
|
||||
# Перейдите в каталог /bin
|
||||
mv ./target/release/telemt /bin
|
||||
# Сделайте файл исполняемым
|
||||
chmod +x /bin/telemt
|
||||
# Запустите!
|
||||
telemt config.toml
|
||||
```
|
||||
|
||||
### Устройства с малым объемом RAM
|
||||
Для устройств с ~1 ГБ RAM (например Raspberry Pi):
|
||||
- используется облегчённая оптимизация линковщика (thin LTO);
|
||||
- не рекомендуется включать fat LTO.
|
||||
|
||||
## OpenBSD
|
||||
|
||||
- Руководство по сборке и настройке на английском языке [OpenBSD Guide (EN)](docs/Quick_start/OPENBSD_QUICK_START_GUIDE.en.md);
|
||||
- Пример rc.d скрипта: [contrib/openbsd/telemt.rcd](contrib/openbsd/telemt.rcd);
|
||||
- Поддержка sandbox с `pledge(2)` и `unveil(2)` пока не реализована.
|
||||
|
||||
## Почему Rust?
|
||||
|
||||
- Надёжность для долгоживущих процессов;
|
||||
- Детерминированное управление ресурсами (RAII);
|
||||
- Отсутствие сборщика мусора;
|
||||
- Безопасность памяти;
|
||||
- Асинхронная архитектура Tokio.
|
||||
|
||||
## Известные проблемы
|
||||
|
||||
- ✅ [Поддержка SOCKS5 как upstream](https://github.com/telemt/telemt/issues/1) -> added Upstream Management;
|
||||
- ✅ [Проблема зависания загрузки медиа на iOS](https://github.com/telemt/telemt/issues/2).
|
||||
|
||||
## Планы
|
||||
|
||||
- Публичный IP в ссылках;
|
||||
- Перезагрузка конфигурации на лету;
|
||||
- Привязка к устройству или IP для входящих и исходящих соединений;
|
||||
- Поддержка рекламных тегов по SNI / секретному ключу;
|
||||
- Улучшенная обработка ошибок;
|
||||
- Zero-copy оптимизации;
|
||||
- Проверка состояния дата-центров;
|
||||
- Отсутствие глобального изменяемого состояния;
|
||||
- Изоляция клиентов и справедливое распределение трафика;
|
||||
- «Политика секретов» — маршрутизация по SNI / секрету;
|
||||
- Балансировщик с несколькими источниками и отработка отказов;
|
||||
- Строгие FSM для handshake;
|
||||
- Улучшенная защита от replay-атак;
|
||||
- Веб-интерфейс: статистика, состояние работоспособности, задержка, пользовательский опыт...
|
||||
@@ -130,7 +130,7 @@ mask_host:mask_port
|
||||
**Telemt работает как TCP-переключатель:**
|
||||
|
||||
1) принимает соединение
|
||||
2️) определяет тип клиента
|
||||
2) определяет тип клиента
|
||||
3) либо:
|
||||
|
||||
- обрабатывает MTProxy внутри
|
||||
|
Before Width: | Height: | Size: 650 KiB After Width: | Height: | Size: 650 KiB |
|
Before Width: | Height: | Size: 838 KiB After Width: | Height: | Size: 838 KiB |
@@ -14,10 +14,10 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`include`](#cfg-top-include) | `String` (special directive) | `null` |
|
||||
| [`include`](#cfg-top-include) | `String` (special directive) | — |
|
||||
| [`show_link`](#cfg-top-show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) |
|
||||
| [`dc_overrides`](#cfg-top-dc_overrides) | `Map<String, String or String[]>` | `{}` |
|
||||
| [`default_dc`](#cfg-top-default_dc) | `u8` or `null` | `null` (effective fallback: `2` in ME routing) |
|
||||
| [`default_dc`](#cfg-top-default_dc) | `u8` | — (effective fallback: `2` in ME routing) |
|
||||
|
||||
<a id="cfg-top-include"></a>
|
||||
- `include`
|
||||
@@ -68,17 +68,17 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`data_path`](#cfg-general-data_path) | `String` or `null` | `null` |
|
||||
| [`data_path`](#cfg-general-data_path) | `String` | — |
|
||||
| [`prefer_ipv6`](#cfg-general-prefer_ipv6) | `bool` | `false` |
|
||||
| [`fast_mode`](#cfg-general-fast_mode) | `bool` | `true` |
|
||||
| [`use_middle_proxy`](#cfg-general-use_middle_proxy) | `bool` | `true` |
|
||||
| [`proxy_secret_path`](#cfg-general-proxy_secret_path) | `String` or `null` | `"proxy-secret"` |
|
||||
| [`proxy_config_v4_cache_path`](#cfg-general-proxy_config_v4_cache_path) | `String` or `null` | `"cache/proxy-config-v4.txt"` |
|
||||
| [`proxy_config_v6_cache_path`](#cfg-general-proxy_config_v6_cache_path) | `String` or `null` | `"cache/proxy-config-v6.txt"` |
|
||||
| [`ad_tag`](#cfg-general-ad_tag) | `String` or `null` | `null` |
|
||||
| [`middle_proxy_nat_ip`](#cfg-general-middle_proxy_nat_ip) | `IpAddr` or `null` | `null` |
|
||||
| [`proxy_secret_path`](#cfg-general-proxy_secret_path) | `String` | `"proxy-secret"` |
|
||||
| [`proxy_config_v4_cache_path`](#cfg-general-proxy_config_v4_cache_path) | `String` | `"cache/proxy-config-v4.txt"` |
|
||||
| [`proxy_config_v6_cache_path`](#cfg-general-proxy_config_v6_cache_path) | `String` | `"cache/proxy-config-v6.txt"` |
|
||||
| [`ad_tag`](#cfg-general-ad_tag) | `String` | — |
|
||||
| [`middle_proxy_nat_ip`](#cfg-general-middle_proxy_nat_ip) | `IpAddr` | — |
|
||||
| [`middle_proxy_nat_probe`](#cfg-general-middle_proxy_nat_probe) | `bool` | `true` |
|
||||
| [`middle_proxy_nat_stun`](#cfg-general-middle_proxy_nat_stun) | `String` or `null` | `null` |
|
||||
| [`middle_proxy_nat_stun`](#cfg-general-middle_proxy_nat_stun) | `String` | — |
|
||||
| [`middle_proxy_nat_stun_servers`](#cfg-general-middle_proxy_nat_stun_servers) | `String[]` | `[]` |
|
||||
| [`stun_nat_probe_concurrency`](#cfg-general-stun_nat_probe_concurrency) | `usize` | `8` |
|
||||
| [`middle_proxy_pool_size`](#cfg-general-middle_proxy_pool_size) | `usize` | `8` |
|
||||
@@ -144,7 +144,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
| [`upstream_unhealthy_fail_threshold`](#cfg-general-upstream_unhealthy_fail_threshold) | `u32` | `5` |
|
||||
| [`upstream_connect_failfast_hard_errors`](#cfg-general-upstream_connect_failfast_hard_errors) | `bool` | `false` |
|
||||
| [`stun_iface_mismatch_ignore`](#cfg-general-stun_iface_mismatch_ignore) | `bool` | `false` |
|
||||
| [`unknown_dc_log_path`](#cfg-general-unknown_dc_log_path) | `String` or `null` | `"unknown-dc.txt"` |
|
||||
| [`unknown_dc_log_path`](#cfg-general-unknown_dc_log_path) | `String` | `"unknown-dc.txt"` |
|
||||
| [`unknown_dc_file_log_enabled`](#cfg-general-unknown_dc_file_log_enabled) | `bool` | `false` |
|
||||
| [`log_level`](#cfg-general-log_level) | `"debug"`, `"verbose"`, `"normal"`, or `"silent"` | `"normal"` |
|
||||
| [`disable_colors`](#cfg-general-disable_colors) | `bool` | `false` |
|
||||
@@ -163,7 +163,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
| [`me_route_inline_recovery_attempts`](#cfg-general-me_route_inline_recovery_attempts) | `u32` | `3` |
|
||||
| [`me_route_inline_recovery_wait_ms`](#cfg-general-me_route_inline_recovery_wait_ms) | `u64` | `3000` |
|
||||
| [`fast_mode_min_tls_record`](#cfg-general-fast_mode_min_tls_record) | `usize` | `0` |
|
||||
| [`update_every`](#cfg-general-update_every) | `u64` or `null` | `300` |
|
||||
| [`update_every`](#cfg-general-update_every) | `u64` | `300` |
|
||||
| [`me_reinit_every_secs`](#cfg-general-me_reinit_every_secs) | `u64` | `900` |
|
||||
| [`me_hardswap_warmup_delay_min_ms`](#cfg-general-me_hardswap_warmup_delay_min_ms) | `u64` | `1000` |
|
||||
| [`me_hardswap_warmup_delay_max_ms`](#cfg-general-me_hardswap_warmup_delay_max_ms) | `u64` | `2000` |
|
||||
@@ -205,7 +205,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
|
||||
<a id="cfg-general-data_path"></a>
|
||||
- `data_path`
|
||||
- **Constraints / validation**: `String` or `null`.
|
||||
- **Constraints / validation**: `String` (optional).
|
||||
- **Description**: Optional runtime data directory path.
|
||||
- **Example**:
|
||||
|
||||
@@ -245,7 +245,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-proxy_secret_path"></a>
|
||||
- `proxy_secret_path`
|
||||
- **Constraints / validation**: `String` or `null`. If `null`, the effective cache path is `"proxy-secret"`. Empty values are accepted but will likely fail at runtime (invalid file path).
|
||||
- **Constraints / validation**: `String`. When omitted, the default path is `"proxy-secret"`. Empty values are accepted by TOML/serde but will likely fail at runtime (invalid file path).
|
||||
- **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first, caches it to this path on success, and falls back to reading the cached file (any age) on download failure.
|
||||
- **Example**:
|
||||
|
||||
@@ -255,7 +255,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-proxy_config_v4_cache_path"></a>
|
||||
- `proxy_config_v4_cache_path`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must not be empty/whitespace-only.
|
||||
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||
- **Description**: Optional disk cache path for raw `getProxyConfig` (IPv4) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||
- **Example**:
|
||||
|
||||
@@ -265,7 +265,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-proxy_config_v6_cache_path"></a>
|
||||
- `proxy_config_v6_cache_path`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must not be empty/whitespace-only.
|
||||
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||
- **Description**: Optional disk cache path for raw `getProxyConfigV6` (IPv6) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||
- **Example**:
|
||||
|
||||
@@ -275,7 +275,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-ad_tag"></a>
|
||||
- `ad_tag`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must be exactly 32 hex characters; invalid values are disabled during config load.
|
||||
- **Constraints / validation**: `String` (optional). When set, must be exactly 32 hex characters; invalid values are disabled during config load.
|
||||
- **Description**: Global fallback sponsored-channel `ad_tag` (used when user has no override in `access.user_ad_tags`). An all-zero tag is accepted but has no effect (and is warned about) until replaced with a real tag from `@MTProxybot`.
|
||||
- **Example**:
|
||||
|
||||
@@ -285,7 +285,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-middle_proxy_nat_ip"></a>
|
||||
- `middle_proxy_nat_ip`
|
||||
- **Constraints / validation**: `IpAddr` or `null`.
|
||||
- **Constraints / validation**: `IpAddr` (optional).
|
||||
- **Description**: Manual public NAT IP override used as ME address material when set.
|
||||
- **Example**:
|
||||
|
||||
@@ -967,8 +967,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-unknown_dc_log_path"></a>
|
||||
- `unknown_dc_log_path`
|
||||
- **Constraints / validation**: `String` or `null`. Must be a safe path (no `..` components, parent directory must exist); unsafe paths are rejected at runtime.
|
||||
- **Description**: Log file path for unknown (non-standard) DC requests when `unknown_dc_file_log_enabled = true`. Set to `null` to disable file logging.
|
||||
- **Constraints / validation**: `String` (optional). Must be a safe path (no `..` components, parent directory must exist); unsafe paths are rejected at runtime.
|
||||
- **Description**: Log file path for unknown (non-standard) DC requests when `unknown_dc_file_log_enabled = true`. Omit this key to disable file logging.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
@@ -1157,7 +1157,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-update_every"></a>
|
||||
- `update_every`
|
||||
- **Constraints / validation**: `u64` (seconds) or `null`. If set, must be `> 0`. If `null`, legacy `proxy_secret_auto_reload_secs` and `proxy_config_auto_reload_secs` are used and their effective minimum must be `> 0`.
|
||||
- **Constraints / validation**: `u64` (seconds). If set, must be `> 0`. If this key is not explicitly set, legacy `proxy_secret_auto_reload_secs` and `proxy_config_auto_reload_secs` may be used (their effective minimum must be `> 0`).
|
||||
- **Description**: Unified refresh interval for ME updater tasks (`getProxyConfig`, `getProxyConfigV6`, `getProxySecret`). When set, it overrides legacy proxy reload intervals.
|
||||
- **Example**:
|
||||
|
||||
@@ -1450,7 +1450,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-proxy_secret_auto_reload_secs"></a>
|
||||
- `proxy_secret_auto_reload_secs`
|
||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is `null`, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is not explicitly set, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||
- **Description**: Deprecated legacy proxy-secret refresh interval. Used only when `general.update_every` is not set.
|
||||
- **Example**:
|
||||
|
||||
@@ -1463,7 +1463,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-proxy_config_auto_reload_secs"></a>
|
||||
- `proxy_config_auto_reload_secs`
|
||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is `null`, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||
- **Constraints / validation**: Deprecated. Use `general.update_every`. When `general.update_every` is not explicitly set, the effective legacy refresh interval is `min(proxy_secret_auto_reload_secs, proxy_config_auto_reload_secs)` and must be `> 0`.
|
||||
- **Description**: Deprecated legacy ME config refresh interval. Used only when `general.update_every` is not set.
|
||||
- **Example**:
|
||||
|
||||
@@ -1624,8 +1624,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`show`](#cfg-general-links-show) | `"*"` or `String[]` | `"*"` |
|
||||
| [`public_host`](#cfg-general-links-public_host) | `String` or `null` | `null` |
|
||||
| [`public_port`](#cfg-general-links-public_port) | `u16` or `null` | `null` |
|
||||
| [`public_host`](#cfg-general-links-public_host) | `String` | — |
|
||||
| [`public_port`](#cfg-general-links-public_port) | `u16` | — |
|
||||
|
||||
<a id="cfg-general-links-show"></a>
|
||||
- `show`
|
||||
@@ -1641,7 +1641,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-links-public_host"></a>
|
||||
- `public_host`
|
||||
- **Constraints / validation**: `String` or `null`.
|
||||
- **Constraints / validation**: `String` (optional).
|
||||
- **Description**: Public hostname/IP override used for generated `tg://` links (overrides detected IP).
|
||||
- **Example**:
|
||||
|
||||
@@ -1651,7 +1651,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-general-links-public_port"></a>
|
||||
- `public_port`
|
||||
- **Constraints / validation**: `u16` or `null`.
|
||||
- **Constraints / validation**: `u16` (optional).
|
||||
- **Description**: Public port override used for generated `tg://` links (overrides `server.port`).
|
||||
- **Example**:
|
||||
|
||||
@@ -1708,7 +1708,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`ipv4`](#cfg-network-ipv4) | `bool` | `true` |
|
||||
| [`ipv6`](#cfg-network-ipv6) | `bool` or `null` | `false` |
|
||||
| [`ipv6`](#cfg-network-ipv6) | `bool` | `false` |
|
||||
| [`prefer`](#cfg-network-prefer) | `u8` | `4` |
|
||||
| [`multipath`](#cfg-network-multipath) | `bool` | `false` |
|
||||
| [`stun_use`](#cfg-network-stun_use) | `bool` | `true` |
|
||||
@@ -1730,8 +1730,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-network-ipv6"></a>
|
||||
- `ipv6`
|
||||
- **Constraints / validation**: `bool` or `null`. `null` means "auto-detect IPv6 availability".
|
||||
- **Description**: Enables/disables IPv6 when explicitly set; when `null`, Telemt will auto-detect IPv6 availability at runtime.
|
||||
- **Constraints / validation**: `bool`.
|
||||
- **Description**: Enables/disables IPv6 networking. When omitted, defaults to `false`.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
@@ -1741,9 +1741,6 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
|
||||
# or: disable IPv6 explicitly
|
||||
# ipv6 = false
|
||||
|
||||
# or: let Telemt auto-detect
|
||||
# ipv6 = null
|
||||
```
|
||||
<a id="cfg-network-prefer"></a>
|
||||
- `prefer`
|
||||
@@ -1842,16 +1839,16 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`port`](#cfg-server-port) | `u16` | `443` |
|
||||
| [`listen_addr_ipv4`](#cfg-server-listen_addr_ipv4) | `String` or `null` | `"0.0.0.0"` |
|
||||
| [`listen_addr_ipv6`](#cfg-server-listen_addr_ipv6) | `String` or `null` | `"::"` |
|
||||
| [`listen_unix_sock`](#cfg-server-listen_unix_sock) | `String` or `null` | `null` |
|
||||
| [`listen_unix_sock_perm`](#cfg-server-listen_unix_sock_perm) | `String` or `null` | `null` |
|
||||
| [`listen_tcp`](#cfg-server-listen_tcp) | `bool` or `null` | `null` (auto) |
|
||||
| [`listen_addr_ipv4`](#cfg-server-listen_addr_ipv4) | `String` | `"0.0.0.0"` |
|
||||
| [`listen_addr_ipv6`](#cfg-server-listen_addr_ipv6) | `String` | `"::"` |
|
||||
| [`listen_unix_sock`](#cfg-server-listen_unix_sock) | `String` | — |
|
||||
| [`listen_unix_sock_perm`](#cfg-server-listen_unix_sock_perm) | `String` | — |
|
||||
| [`listen_tcp`](#cfg-server-listen_tcp) | `bool` | — (auto) |
|
||||
| [`proxy_protocol`](#cfg-server-proxy_protocol) | `bool` | `false` |
|
||||
| [`proxy_protocol_header_timeout_ms`](#cfg-server-proxy_protocol_header_timeout_ms) | `u64` | `500` |
|
||||
| [`proxy_protocol_trusted_cidrs`](#cfg-server-proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` |
|
||||
| [`metrics_port`](#cfg-server-metrics_port) | `u16` or `null` | `null` |
|
||||
| [`metrics_listen`](#cfg-server-metrics_listen) | `String` or `null` | `null` |
|
||||
| [`metrics_port`](#cfg-server-metrics_port) | `u16` | — |
|
||||
| [`metrics_listen`](#cfg-server-metrics_listen) | `String` | — |
|
||||
| [`metrics_whitelist`](#cfg-server-metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` |
|
||||
| [`max_connections`](#cfg-server-max_connections) | `u32` | `10000` |
|
||||
| [`accept_permit_timeout_ms`](#cfg-server-accept_permit_timeout_ms) | `u64` | `250` |
|
||||
@@ -1868,8 +1865,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-listen_addr_ipv4"></a>
|
||||
- `listen_addr_ipv4`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must be a valid IPv4 address string.
|
||||
- **Description**: IPv4 bind address for TCP listener (`null` disables IPv4 bind).
|
||||
- **Constraints / validation**: `String` (optional). When set, must be a valid IPv4 address string.
|
||||
- **Description**: IPv4 bind address for TCP listener (omit this key to disable IPv4 bind).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
@@ -1878,8 +1875,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-listen_addr_ipv6"></a>
|
||||
- `listen_addr_ipv6`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must be a valid IPv6 address string.
|
||||
- **Description**: IPv6 bind address for TCP listener (`null` disables IPv6 bind).
|
||||
- **Constraints / validation**: `String` (optional). When set, must be a valid IPv6 address string.
|
||||
- **Description**: IPv6 bind address for TCP listener (omit this key to disable IPv6 bind).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
@@ -1888,7 +1885,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-listen_unix_sock"></a>
|
||||
- `listen_unix_sock`
|
||||
- **Constraints / validation**: `String` or `null`. Must not be empty when set. Unix only.
|
||||
- **Constraints / validation**: `String` (optional). Must not be empty when set. Unix only.
|
||||
- **Description**: Unix socket path for listener. When set, `server.listen_tcp` defaults to `false` (unless explicitly overridden).
|
||||
- **Example**:
|
||||
|
||||
@@ -1898,8 +1895,8 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-listen_unix_sock_perm"></a>
|
||||
- `listen_unix_sock_perm`
|
||||
- **Constraints / validation**: `String` or `null`. When set, should be an octal permission string like `"0666"` or `"0777"`.
|
||||
- **Description**: Optional Unix socket file permissions applied after bind (chmod). `null` means "no change" (inherits umask).
|
||||
- **Constraints / validation**: `String` (optional). When set, should be an octal permission string like `"0666"` or `"0777"`.
|
||||
- **Description**: Optional Unix socket file permissions applied after bind (chmod). When omitted, permissions are not changed (inherits umask).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
@@ -1909,7 +1906,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-listen_tcp"></a>
|
||||
- `listen_tcp`
|
||||
- **Constraints / validation**: `bool` or `null`. `null` means auto:
|
||||
- **Constraints / validation**: `bool` (optional). When omitted, Telemt auto-detects:
|
||||
- `true` when `listen_unix_sock` is not set
|
||||
- `false` when `listen_unix_sock` is set
|
||||
- **Description**: Explicit TCP listener enable/disable override.
|
||||
@@ -1957,7 +1954,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-metrics_port"></a>
|
||||
- `metrics_port`
|
||||
- **Constraints / validation**: `u16` or `null`.
|
||||
- **Constraints / validation**: `u16` (optional).
|
||||
- **Description**: Prometheus-compatible metrics endpoint port. When set, enables the metrics listener (bind behavior can be overridden by `metrics_listen`).
|
||||
- **Example**:
|
||||
|
||||
@@ -1967,7 +1964,7 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
<a id="cfg-server-metrics_listen"></a>
|
||||
- `metrics_listen`
|
||||
- **Constraints / validation**: `String` or `null`. When set, must be in `IP:PORT` format.
|
||||
- **Constraints / validation**: `String` (optional). When set, must be in `IP:PORT` format.
|
||||
- **Description**: Full metrics bind address (`IP:PORT`), overrides `metrics_port` and binds on the specified address only.
|
||||
- **Example**:
|
||||
|
||||
@@ -2010,6 +2007,105 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
|
||||
Note: When `server.proxy_protocol` is enabled, incoming PROXY protocol headers are parsed from the first bytes of the connection and the client source address is replaced with `src_addr` from the header. For security, the peer source IP (the direct connection address) is verified against `server.proxy_protocol_trusted_cidrs`; if this list is empty, PROXY headers are rejected and the connection is considered untrusted.
|
||||
|
||||
## [server.conntrack_control]
|
||||
|
||||
Note: The conntrack-control worker runs **only on Linux**. On other operating systems it is not started; if `inline_conntrack_control` is `true`, a warning is logged. Effective operation also requires **CAP_NET_ADMIN** and a usable backend (`nft` or `iptables` / `ip6tables` on `PATH`). The `conntrack` utility is used for optional table entry deletes under pressure.
|
||||
|
||||
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`inline_conntrack_control`](#cfg-server-conntrack_control-inline_conntrack_control) | `bool` | `true` |
|
||||
| [`mode`](#cfg-server-conntrack_control-mode) | `String` | `"tracked"` |
|
||||
| [`backend`](#cfg-server-conntrack_control-backend) | `String` | `"auto"` |
|
||||
| [`profile`](#cfg-server-conntrack_control-profile) | `String` | `"balanced"` |
|
||||
| [`hybrid_listener_ips`](#cfg-server-conntrack_control-hybrid_listener_ips) | `IpAddr[]` | `[]` |
|
||||
| [`pressure_high_watermark_pct`](#cfg-server-conntrack_control-pressure_high_watermark_pct) | `u8` | `85` |
|
||||
| [`pressure_low_watermark_pct`](#cfg-server-conntrack_control-pressure_low_watermark_pct) | `u8` | `70` |
|
||||
| [`delete_budget_per_sec`](#cfg-server-conntrack_control-delete_budget_per_sec) | `u64` | `4096` |
|
||||
|
||||
<a id="cfg-server-conntrack_control-inline_conntrack_control"></a>
|
||||
- `inline_conntrack_control`
|
||||
- **Constraints / validation**: `bool`.
|
||||
- **Description**: Master switch for the runtime conntrack-control task: reconciles **raw/notrack** netfilter rules for listener ingress (see `mode`), samples load every second, and may run **`conntrack -D`** deletes for qualifying close events while **pressure mode** is active (see `delete_budget_per_sec`). When `false`, notrack rules are cleared and pressure-driven deletes are disabled.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
inline_conntrack_control = true
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-mode"></a>
|
||||
- `mode`
|
||||
- **Constraints / validation**: One of `tracked`, `notrack`, `hybrid` (case-insensitive; serialized lowercase).
|
||||
- **Description**: **`tracked`**: do not install telemt notrack rules (connections stay in conntrack). **`notrack`**: mark matching ingress TCP to `server.port` as notrack — targets are derived from `[[server.listeners]]` if any, otherwise from `server.listen_addr_ipv4` / `server.listen_addr_ipv6` (unspecified addresses mean “any” for that family). **`hybrid`**: notrack only for addresses listed in `hybrid_listener_ips` (must be non-empty; validated at load).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
mode = "notrack"
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-backend"></a>
|
||||
- `backend`
|
||||
- **Constraints / validation**: One of `auto`, `nftables`, `iptables` (case-insensitive; serialized lowercase).
|
||||
- **Description**: Which command set applies notrack rules. **`auto`**: use `nft` if present on `PATH`, else `iptables`/`ip6tables` if present. **`nftables`** / **`iptables`**: force that backend; missing binary means rules cannot be applied. The nft path uses table `inet telemt_conntrack` and a prerouting raw hook; iptables uses chain `TELEMT_NOTRACK` in the `raw` table.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
backend = "auto"
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-profile"></a>
|
||||
- `profile`
|
||||
- **Constraints / validation**: One of `conservative`, `balanced`, `aggressive` (case-insensitive; serialized lowercase).
|
||||
- **Description**: When **conntrack pressure mode** is active (`pressure_*` watermarks), caps idle and activity timeouts to reduce conntrack churn: e.g. **client first-byte idle** (`client.rs`), **direct relay activity timeout** (`direct_relay.rs`), and **middle-relay idle policy** caps (`middle_relay.rs` via `ConntrackPressureProfile::*_cap_secs` / `direct_activity_timeout_secs`). More aggressive profiles use shorter caps.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
profile = "balanced"
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-hybrid_listener_ips"></a>
|
||||
- `hybrid_listener_ips`
|
||||
- **Constraints / validation**: `IpAddr[]`. Required to be **non-empty** when `mode = "hybrid"`. Ignored for `tracked` / `notrack`.
|
||||
- **Description**: Explicit listener addresses that receive notrack rules in hybrid mode (split into IPv4 vs IPv6 rules by the implementation).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
mode = "hybrid"
|
||||
hybrid_listener_ips = ["203.0.113.10", "2001:db8::1"]
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-pressure_high_watermark_pct"></a>
|
||||
- `pressure_high_watermark_pct`
|
||||
- **Constraints / validation**: Must be within `[1, 100]`.
|
||||
- **Description**: Pressure mode **enters** when any of: connection fill vs `server.max_connections` (percentage, if `max_connections > 0`), **file-descriptor** usage vs process soft `RLIMIT_NOFILE`, **non-zero** `accept_permit_timeout` events in the last sample window, or **ME c2me send-full** counter delta. Entry compares relevant percentages against this high watermark (see `update_pressure_state` in `conntrack_control.rs`).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
pressure_high_watermark_pct = 85
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-pressure_low_watermark_pct"></a>
|
||||
- `pressure_low_watermark_pct`
|
||||
- **Constraints / validation**: Must be **strictly less than** `pressure_high_watermark_pct`.
|
||||
- **Description**: Pressure mode **clears** only after **three** consecutive one-second samples where all signals are at or below this low watermark and the accept-timeout / ME-queue deltas are zero (hysteresis).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
pressure_low_watermark_pct = 70
|
||||
```
|
||||
<a id="cfg-server-conntrack_control-delete_budget_per_sec"></a>
|
||||
- `delete_budget_per_sec`
|
||||
- **Constraints / validation**: Must be `> 0`.
|
||||
- **Description**: Maximum number of **`conntrack -D`** attempts **per second** while pressure mode is active (token bucket refilled each second). Deletes run only for close events with reasons **timeout**, **pressure**, or **reset**; each attempt consumes a token regardless of outcome.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[server.conntrack_control]
|
||||
delete_budget_per_sec = 4096
|
||||
```
|
||||
|
||||
|
||||
## [server.api]
|
||||
|
||||
Note: This section also accepts the legacy alias `[server.admin_api]` (same schema as `[server.api]`).
|
||||
@@ -2158,9 +2254,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
| Key | Type | Default |
|
||||
| --- | ---- | ------- |
|
||||
| [`ip`](#cfg-server-listeners-ip) | `IpAddr` | — |
|
||||
| [`announce`](#cfg-server-listeners-announce) | `String` or `null` | — |
|
||||
| [`announce_ip`](#cfg-server-listeners-announce_ip) | `IpAddr` or `null` | — |
|
||||
| [`proxy_protocol`](#cfg-server-listeners-proxy_protocol) | `bool` or `null` | `null` |
|
||||
| [`announce`](#cfg-server-listeners-announce) | `String` | — |
|
||||
| [`announce_ip`](#cfg-server-listeners-announce_ip) | `IpAddr` | — |
|
||||
| [`proxy_protocol`](#cfg-server-listeners-proxy_protocol) | `bool` | — |
|
||||
| [`reuse_allow`](#cfg-server-listeners-reuse_allow) | `bool` | `false` |
|
||||
|
||||
<a id="cfg-server-listeners-ip"></a>
|
||||
@@ -2175,7 +2271,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
```
|
||||
<a id="cfg-server-listeners-announce"></a>
|
||||
- `announce`
|
||||
- **Constraints / validation**: `String` or `null`. Must not be empty when set.
|
||||
- **Constraints / validation**: `String` (optional). Must not be empty when set.
|
||||
- **Description**: Public IP/domain announced in proxy links for this listener. Takes precedence over `announce_ip`.
|
||||
- **Example**:
|
||||
|
||||
@@ -2186,7 +2282,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
```
|
||||
<a id="cfg-server-listeners-announce_ip"></a>
|
||||
- `announce_ip`
|
||||
- **Constraints / validation**: `IpAddr` or `null`. Deprecated. Use `announce`.
|
||||
- **Constraints / validation**: `IpAddr` (optional). Deprecated. Use `announce`.
|
||||
- **Description**: Deprecated legacy announce IP. During config load it is migrated to `announce` when `announce` is not set.
|
||||
- **Example**:
|
||||
|
||||
@@ -2197,7 +2293,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
```
|
||||
<a id="cfg-server-listeners-proxy_protocol"></a>
|
||||
- `proxy_protocol`
|
||||
- **Constraints / validation**: `bool` or `null`. When set, overrides `server.proxy_protocol` for this listener.
|
||||
- **Constraints / validation**: `bool` (optional). When set, overrides `server.proxy_protocol` for this listener.
|
||||
- **Description**: Per-listener PROXY protocol override.
|
||||
- **Example**:
|
||||
|
||||
@@ -2351,9 +2447,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
| [`tls_fetch_scope`](#cfg-censorship-tls_fetch_scope) | `String` | `""` |
|
||||
| [`tls_fetch`](#cfg-censorship-tls_fetch) | `Table` | built-in defaults |
|
||||
| [`mask`](#cfg-censorship-mask) | `bool` | `true` |
|
||||
| [`mask_host`](#cfg-censorship-mask_host) | `String` or `null` | `null` |
|
||||
| [`mask_host`](#cfg-censorship-mask_host) | `String` | — |
|
||||
| [`mask_port`](#cfg-censorship-mask_port) | `u16` | `443` |
|
||||
| [`mask_unix_sock`](#cfg-censorship-mask_unix_sock) | `String` or `null` | `null` |
|
||||
| [`mask_unix_sock`](#cfg-censorship-mask_unix_sock) | `String` | — |
|
||||
| [`fake_cert_len`](#cfg-censorship-fake_cert_len) | `usize` | `2048` |
|
||||
| [`tls_emulation`](#cfg-censorship-tls_emulation) | `bool` | `true` |
|
||||
| [`tls_front_dir`](#cfg-censorship-tls_front_dir) | `String` | `"tlsfront"` |
|
||||
@@ -2440,8 +2536,8 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
```
|
||||
<a id="cfg-censorship-mask_host"></a>
|
||||
- `mask_host`
|
||||
- **Constraints / validation**: `String` or `null`.
|
||||
- If `mask_unix_sock` is set, `mask_host` must be `null` (mutually exclusive).
|
||||
- **Constraints / validation**: `String` (optional).
|
||||
- If `mask_unix_sock` is set, `mask_host` must be omitted (mutually exclusive).
|
||||
- If `mask_host` is not set and `mask_unix_sock` is not set, Telemt defaults `mask_host` to `tls_domain`.
|
||||
- **Description**: Upstream mask host for TLS fronting relay.
|
||||
- **Example**:
|
||||
@@ -2462,7 +2558,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
```
|
||||
<a id="cfg-censorship-mask_unix_sock"></a>
|
||||
- `mask_unix_sock`
|
||||
- **Constraints / validation**: `String` or `null`.
|
||||
- **Constraints / validation**: `String` (optional).
|
||||
- Must not be empty when set.
|
||||
- Unix only; rejected on non-Unix platforms.
|
||||
- On Unix, must be \(\le 107\) bytes (path length limit).
|
||||
@@ -2882,6 +2978,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
| [`users`](#cfg-access-users) | `Map<String, String>` | `{"default": "000…000"}` |
|
||||
| [`user_ad_tags`](#cfg-access-user_ad_tags) | `Map<String, String>` | `{}` |
|
||||
| [`user_max_tcp_conns`](#cfg-access-user_max_tcp_conns) | `Map<String, usize>` | `{}` |
|
||||
| [`user_max_tcp_conns_global_each`](#cfg-access-user_max_tcp_conns_global_each) | `usize` | `0` |
|
||||
| [`user_expirations`](#cfg-access-user_expirations) | `Map<String, DateTime<Utc>>` | `{}` |
|
||||
| [`user_data_quota`](#cfg-access-user_data_quota) | `Map<String, u64>` | `{}` |
|
||||
| [`user_max_unique_ips`](#cfg-access-user_max_unique_ips) | `Map<String, usize>` | `{}` |
|
||||
@@ -2926,6 +3023,20 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
[access.user_max_tcp_conns]
|
||||
alice = 500
|
||||
```
|
||||
<a id="cfg-access-user_max_tcp_conns_global_each"></a>
|
||||
- `user_max_tcp_conns_global_each`
|
||||
- **Constraints / validation**: `usize`. `0` disables the inherited limit.
|
||||
- **Description**: Global per-user maximum concurrent TCP connections, applied when a user has **no positive** entry in `[access.user_max_tcp_conns]` (a missing key, or a value of `0`, both fall through to this setting). Per-user limits greater than `0` in `user_max_tcp_conns` take precedence.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[access]
|
||||
user_max_tcp_conns_global_each = 200
|
||||
|
||||
[access.user_max_tcp_conns]
|
||||
alice = 500 # uses 500, not the global cap
|
||||
# bob has no entry → uses 200
|
||||
```
|
||||
<a id="cfg-access-user_expirations"></a>
|
||||
- `user_expirations`
|
||||
- **Constraints / validation**: `Map<String, DateTime<Utc>>`. Each value must be a valid RFC3339 / ISO-8601 datetime.
|
||||
@@ -3027,13 +3138,13 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
| [`weight`](#cfg-upstreams-weight) | `u16` | `1` |
|
||||
| [`enabled`](#cfg-upstreams-enabled) | `bool` | `true` |
|
||||
| [`scopes`](#cfg-upstreams-scopes) | `String` | `""` |
|
||||
| [`interface`](#cfg-upstreams-interface) | `String` or `null` | `null` |
|
||||
| [`bind_addresses`](#cfg-upstreams-bind_addresses) | `String[]` or `null` | `null` |
|
||||
| [`interface`](#cfg-upstreams-interface) | `String` | — |
|
||||
| [`bind_addresses`](#cfg-upstreams-bind_addresses) | `String[]` | — |
|
||||
| [`url`](#cfg-upstreams-url) | `String` | — |
|
||||
| [`address`](#cfg-upstreams-address) | `String` | — |
|
||||
| [`user_id`](#cfg-upstreams-user_id) | `String` or `null` | `null` |
|
||||
| [`username`](#cfg-upstreams-username) | `String` or `null` | `null` |
|
||||
| [`password`](#cfg-upstreams-password) | `String` or `null` | `null` |
|
||||
| [`user_id`](#cfg-upstreams-user_id) | `String` | — |
|
||||
| [`username`](#cfg-upstreams-username) | `String` | — |
|
||||
| [`password`](#cfg-upstreams-password) | `String` | — |
|
||||
|
||||
<a id="cfg-upstreams-type"></a>
|
||||
- `type`
|
||||
@@ -3090,7 +3201,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
```
|
||||
<a id="cfg-upstreams-interface"></a>
|
||||
- `interface`
|
||||
- **Constraints / validation**: `String` or `null`.
|
||||
- **Constraints / validation**: `String` (optional).
|
||||
- For `"direct"`: may be an IP address (used as explicit local bind) or an OS interface name (resolved to an IP at runtime; Unix only).
|
||||
- For `"socks4"`/`"socks5"`: supported only when `address` is an `IP:port` literal; when `address` is a hostname, interface binding is ignored.
|
||||
- For `"shadowsocks"`: passed to the shadowsocks connector as an optional outbound bind hint.
|
||||
@@ -3109,7 +3220,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
```
|
||||
<a id="cfg-upstreams-bind_addresses"></a>
|
||||
- `bind_addresses`
|
||||
- **Constraints / validation**: `String[]` or `null`. Applies only to `type = "direct"`.
|
||||
- **Constraints / validation**: `String[]` (optional). Applies only to `type = "direct"`.
|
||||
- Each entry should be an IP address string.
|
||||
- At runtime, Telemt selects an address that matches the target family (IPv4 vs IPv6). If `bind_addresses` is set and none match the target family, the connect attempt fails.
|
||||
- **Description**: Explicit local source addresses for outgoing direct TCP connects. When multiple addresses are provided, selection is round-robin.
|
||||
@@ -3150,7 +3261,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
```
|
||||
<a id="cfg-upstreams-user_id"></a>
|
||||
- `user_id`
|
||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks4"`.
|
||||
- **Constraints / validation**: `String` (optional). Only for `type = "socks4"`.
|
||||
- **Description**: SOCKS4 CONNECT user ID. Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||
- **Example**:
|
||||
|
||||
@@ -3162,7 +3273,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
```
|
||||
<a id="cfg-upstreams-username"></a>
|
||||
- `username`
|
||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks5"`.
|
||||
- **Constraints / validation**: `String` (optional). Only for `type = "socks5"`.
|
||||
- **Description**: SOCKS5 username (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||
- **Example**:
|
||||
|
||||
@@ -3174,7 +3285,7 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p
|
||||
```
|
||||
<a id="cfg-upstreams-password"></a>
|
||||
- `password`
|
||||
- **Constraints / validation**: `String` or `null`. Only for `type = "socks5"`.
|
||||
- **Constraints / validation**: `String` (optional). Only for `type = "socks5"`.
|
||||
- **Description**: SOCKS5 password (for username/password authentication). Note: when a request scope is selected, Telemt may override this with the selected scope value.
|
||||
- **Example**:
|
||||
|
||||
152
docs/FAQ.en.md
152
docs/FAQ.en.md
@@ -1,5 +1,4 @@
|
||||
## How to set up a "proxy sponsor" channel and statistics via the @MTProxybot
|
||||
|
||||
1. Go to the @MTProxybot.
|
||||
2. Enter the `/newproxy` command.
|
||||
3. Send your server's IP address and port. For example: `1.2.3.4:443`.
|
||||
@@ -32,13 +31,130 @@ use_middle_proxy = true
|
||||
hello = "ad_tag"
|
||||
hello2 = "ad_tag2"
|
||||
```
|
||||
## Recognizability for DPI and crawler
|
||||
|
||||
## Why do you need a middle proxy (ME)
|
||||
On April 1, 2026, we became aware of a method for detecting MTProxy Fake-TLS,
|
||||
based on the ECH extension and the ordering of cipher suites,
|
||||
as well as an overall unique JA3/JA4 fingerprint
|
||||
that does not occur in modern browsers:
|
||||
we have already submitted initial changes to the Telegram Desktop developers and are working on updates for other clients.
|
||||
|
||||
- We consider this a breakthrough aspect, which has no stable analogues today
|
||||
- Based on this: if `telemt` configured correctly, **TLS mode is completely identical to real-life handshake + communication** with a specified host
|
||||
- Here is our evidence:
|
||||
- 212.220.88.77 - "dummy" host, running `telemt`
|
||||
- `petrovich.ru` - `tls` + `masking` host, in HEX: `706574726f766963682e7275`
|
||||
- **No MITM + No Fake Certificates/Crypto** = pure transparent *TCP Splice* to "best" upstream: MTProxy or tls/mask-host:
|
||||
- DPI see legitimate HTTPS to `tls_host`, including *valid chain-of-trust* and entropy
|
||||
- Crawlers completely satisfied receiving responses from `mask_host`
|
||||
### Client WITH secret-key accesses the MTProxy resource:
|
||||
|
||||
<img width="360" height="439" alt="telemt" src="https://github.com/user-attachments/assets/39352afb-4a11-4ecc-9d91-9e8cfb20607d" />
|
||||
|
||||
### Client WITHOUT secret-key gets transparent access to the specified resource:
|
||||
- with trusted certificate
|
||||
- with original handshake
|
||||
- with full request-response way
|
||||
- with low-latency overhead
|
||||
```bash
|
||||
root@debian:~/telemt# curl -v -I --resolve petrovich.ru:443:212.220.88.77 https://petrovich.ru/
|
||||
* Added petrovich.ru:443:212.220.88.77 to DNS cache
|
||||
* Hostname petrovich.ru was found in DNS cache
|
||||
* Trying 212.220.88.77:443...
|
||||
* Connected to petrovich.ru (212.220.88.77) port 443 (#0)
|
||||
* ALPN: offers h2,http/1.1
|
||||
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
|
||||
* CAfile: /etc/ssl/certs/ca-certificates.crt
|
||||
* CApath: /etc/ssl/certs
|
||||
* TLSv1.3 (IN), TLS handshake, Server hello (2):
|
||||
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
|
||||
* TLSv1.3 (IN), TLS handshake, Certificate (11):
|
||||
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
|
||||
* TLSv1.3 (IN), TLS handshake, Finished (20):
|
||||
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
|
||||
* TLSv1.3 (OUT), TLS handshake, Finished (20):
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
* ALPN: server did not agree on a protocol. Uses default.
|
||||
* Server certificate:
|
||||
* subject: C=RU; ST=Saint Petersburg; L=Saint Petersburg; O=STD Petrovich; CN=*.petrovich.ru
|
||||
* start date: Jan 28 11:21:01 2025 GMT
|
||||
* expire date: Mar 1 11:21:00 2026 GMT
|
||||
* subjectAltName: host "petrovich.ru" matched cert's "petrovich.ru"
|
||||
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
|
||||
* SSL certificate verify ok.
|
||||
* using HTTP/1.x
|
||||
> HEAD / HTTP/1.1
|
||||
> Host: petrovich.ru
|
||||
> User-Agent: curl/7.88.1
|
||||
> Accept: */*
|
||||
>
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* old SSL session ID is stale, removing
|
||||
< HTTP/1.1 200 OK
|
||||
HTTP/1.1 200 OK
|
||||
< Server: Variti/0.9.3a
|
||||
Server: Variti/0.9.3a
|
||||
< Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Origin: *
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Cache-Control: no-store
|
||||
Cache-Control: no-store
|
||||
< Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Pragma: no-cache
|
||||
Pragma: no-cache
|
||||
< Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Content-Length: 31253
|
||||
Content-Length: 31253
|
||||
< Connection: keep-alive
|
||||
Connection: keep-alive
|
||||
< Keep-Alive: timeout=60
|
||||
Keep-Alive: timeout=60
|
||||
|
||||
<
|
||||
* Connection #0 to host petrovich.ru left intact
|
||||
|
||||
```
|
||||
- We challenged ourselves, we kept trying and we didn't only *beat the air*: now, we have something to show you
|
||||
- Do not just take our word for it? - This is great and we respect that: you can build your own `telemt` or download a build and check it right now
|
||||
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### Telegram Calls via MTProxy
|
||||
- Telegram architecture **does NOT allow calls via MTProxy**, but only via SOCKS5, which cannot be obfuscated
|
||||
|
||||
### How does DPI see MTProxy TLS?
|
||||
- DPI sees MTProxy in Fake TLS (ee) mode as TLS 1.3
|
||||
- the SNI you specify sends both the client and the server;
|
||||
- ALPN is similar to HTTP 1.1/2;
|
||||
- high entropy, which is normal for AES-encrypted traffic;
|
||||
|
||||
### Whitelist on IP
|
||||
- MTProxy cannot work when there is:
|
||||
- no IP connectivity to the target host: Russian Whitelist on Mobile Networks - "Белый список"
|
||||
- OR all TCP traffic is blocked
|
||||
- OR high entropy/encrypted traffic is blocked: content filters at universities and critical infrastructure
|
||||
- OR all TLS traffic is blocked
|
||||
- OR specified port is blocked: use 443 to make it "like real"
|
||||
- OR provided SNI is blocked: use "officially approved"/innocuous name
|
||||
- like most protocols on the Internet;
|
||||
- these situations are observed:
|
||||
- in China behind the Great Firewall
|
||||
- in Russia on mobile networks, less in wired networks
|
||||
- in Iran during "activity"
|
||||
|
||||
### Why do you need a middle proxy (ME)
|
||||
https://github.com/telemt/telemt/discussions/167
|
||||
|
||||
|
||||
## How many people can use one link
|
||||
|
||||
### How many people can use one link
|
||||
By default, an unlimited number of people can use a single link.
|
||||
However, you can limit the number of unique IP addresses for each user:
|
||||
```toml
|
||||
@@ -47,8 +163,7 @@ hello = 1
|
||||
```
|
||||
This parameter sets the maximum number of unique IP addresses from which a single link can be used simultaneously. If the first user disconnects, a second one can connect. At the same time, multiple users can connect from a single IP address simultaneously (for example, devices on the same Wi-Fi network).
|
||||
|
||||
## How to create multiple different links
|
||||
|
||||
### How to create multiple different links
|
||||
1. Generate the required number of secrets using the command: `openssl rand -hex 16`.
|
||||
2. Open the configuration file: `nano /etc/telemt/telemt.toml`.
|
||||
3. Add new users to the `[access.users]` section:
|
||||
@@ -64,7 +179,7 @@ user3 = "00000000000000000000000000000003"
|
||||
curl -s http://127.0.0.1:9091/v1/users | jq
|
||||
```
|
||||
|
||||
## "Unknown TLS SNI" error
|
||||
### "Unknown TLS SNI" error
|
||||
Usually, this error occurs if you have changed the `tls_domain` parameter, but users continue to connect using old links with the previous domain.
|
||||
|
||||
If you need to allow connections with any domains (ignoring SNI mismatches), add the following parameters:
|
||||
@@ -73,7 +188,7 @@ If you need to allow connections with any domains (ignoring SNI mismatches), add
|
||||
unknown_sni_action = "mask"
|
||||
```
|
||||
|
||||
## How to view metrics
|
||||
### How to view metrics
|
||||
|
||||
1. Open the configuration file: `nano /etc/telemt/telemt.toml`.
|
||||
2. Add the following parameters:
|
||||
@@ -87,6 +202,25 @@ metrics_whitelist = ["127.0.0.1/32", "::1/128", "0.0.0.0/0"]
|
||||
> [!WARNING]
|
||||
> The value `"0.0.0.0/0"` in `metrics_whitelist` opens access to metrics from any IP address. It is recommended to replace it with your personal IP, for example: `"1.2.3.4/32"`.
|
||||
|
||||
### Too many open files
|
||||
- On a fresh Linux install the default open file limit is low; under load `telemt` may fail with `Accept error: Too many open files`
|
||||
- **Systemd**: add `LimitNOFILE=65536` to the `[Service]` section (already included in the example above)
|
||||
- **Docker**: add `--ulimit nofile=65536:65536` to your `docker run` command, or in `docker-compose.yml`:
|
||||
```yaml
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
```
|
||||
- **System-wide** (optional): add to `/etc/security/limits.conf`:
|
||||
```
|
||||
* soft nofile 1048576
|
||||
* hard nofile 1048576
|
||||
root soft nofile 1048576
|
||||
root hard nofile 1048576
|
||||
```
|
||||
|
||||
|
||||
## Additional parameters
|
||||
|
||||
### Domain in the link instead of IP
|
||||
|
||||
134
docs/FAQ.ru.md
134
docs/FAQ.ru.md
@@ -32,6 +32,122 @@ use_middle_proxy = true
|
||||
hello = "ad_tag"
|
||||
hello2 = "ad_tag2"
|
||||
```
|
||||
## Распознаваемость для DPI и сканеров
|
||||
|
||||
1 апреля 2026 года нам стало известно о методе обнаружения MTProxy Fake-TLS, основанном на расширении ECH и порядке набора шифров,
|
||||
а также об общем уникальном отпечатке JA3/JA4, который не встречается в современных браузерах: мы уже отправили первоначальные изменения разработчикам Telegram Desktop и работаем над обновлениями для других клиентов.
|
||||
|
||||
- Мы считаем это прорывом, которому на сегодняшний день нет стабильных аналогов;
|
||||
- Исходя из этого: если `telemt` настроен правильно, **режим TLS полностью идентичен реальному «рукопожатию» + обмену данными** с указанным хостом;
|
||||
- Вот наши доказательства:
|
||||
- 212.220.88.77 — «фиктивный» хост, на котором запущен `telemt`;
|
||||
- `petrovich.ru` — хост с `tls` + `masking`, в HEX: `706574726f766963682e7275`;
|
||||
- **Без MITM + без поддельных сертификатов/шифрования** = чистое прозрачное *TCP Splice* к «лучшему» исходному серверу: MTProxy или tls/mask-host:
|
||||
- DPI видит легитимный HTTPS к `tls_host`, включая *достоверную цепочку доверия* и энтропию;
|
||||
- Краулеры полностью удовлетворены получением ответов от `mask_host`.
|
||||
|
||||
### Клиент С секретным ключом получает доступ к ресурсу MTProxy:
|
||||
|
||||
<img width="360" height="439" alt="telemt" src="https://github.com/user-attachments/assets/39352afb-4a11-4ecc-9d91-9e8cfb20607d" />
|
||||
|
||||
### Клиент БЕЗ секретного ключа получает прозрачный доступ к указанному ресурсу:
|
||||
- с доверенным сертификатом;
|
||||
- с исходным «рукопожатием»;
|
||||
- с полным циклом запрос-ответ;
|
||||
- с низкой задержкой.
|
||||
|
||||
```bash
|
||||
root@debian:~/telemt# curl -v -I --resolve petrovich.ru:443:212.220.88.77 https://petrovich.ru/
|
||||
* Added petrovich.ru:443:212.220.88.77 to DNS cache
|
||||
* Hostname petrovich.ru was found in DNS cache
|
||||
* Trying 212.220.88.77:443...
|
||||
* Connected to petrovich.ru (212.220.88.77) port 443 (#0)
|
||||
* ALPN: offers h2,http/1.1
|
||||
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
|
||||
* CAfile: /etc/ssl/certs/ca-certificates.crt
|
||||
* CApath: /etc/ssl/certs
|
||||
* TLSv1.3 (IN), TLS handshake, Server hello (2):
|
||||
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
|
||||
* TLSv1.3 (IN), TLS handshake, Certificate (11):
|
||||
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
|
||||
* TLSv1.3 (IN), TLS handshake, Finished (20):
|
||||
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
|
||||
* TLSv1.3 (OUT), TLS handshake, Finished (20):
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
* ALPN: server did not agree on a protocol. Uses default.
|
||||
* Server certificate:
|
||||
* subject: C=RU; ST=Saint Petersburg; L=Saint Petersburg; O=STD Petrovich; CN=*.petrovich.ru
|
||||
* start date: Jan 28 11:21:01 2025 GMT
|
||||
* expire date: Mar 1 11:21:00 2026 GMT
|
||||
* subjectAltName: host "petrovich.ru" matched cert's "petrovich.ru"
|
||||
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
|
||||
* SSL certificate verify ok.
|
||||
* using HTTP/1.x
|
||||
> HEAD / HTTP/1.1
|
||||
> Host: petrovich.ru
|
||||
> User-Agent: curl/7.88.1
|
||||
> Accept: */*
|
||||
>
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
|
||||
* old SSL session ID is stale, removing
|
||||
< HTTP/1.1 200 OK
|
||||
HTTP/1.1 200 OK
|
||||
< Server: Variti/0.9.3a
|
||||
Server: Variti/0.9.3a
|
||||
< Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Date: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Origin: *
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Cache-Control: no-store
|
||||
Cache-Control: no-store
|
||||
< Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
Expires: Thu, 01 Jan 2026 00:0000 GMT
|
||||
< Pragma: no-cache
|
||||
Pragma: no-cache
|
||||
< Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
Set-Cookie: ipp_uid=XXXXX/XXXXX/XXXXX==; Expires=Tue, 31 Dec 2040 23:59:59 GMT; Domain=.petrovich.ru; Path=/
|
||||
< Content-Type: text/html
|
||||
Content-Type: text/html
|
||||
< Content-Length: 31253
|
||||
Content-Length: 31253
|
||||
< Connection: keep-alive
|
||||
Connection: keep-alive
|
||||
< Keep-Alive: timeout=60
|
||||
Keep-Alive: timeout=60
|
||||
|
||||
<
|
||||
* Connection #0 to host petrovich.ru left intact
|
||||
|
||||
```
|
||||
- Мы поставили перед собой задачу, не сдавались и не просто «бились в пустоту»: теперь у нас есть что вам показать.
|
||||
- Не верите нам на слово? — Это прекрасно, и мы уважаем ваше решение: вы можете собрать свой собственный `telemt` или скачать готовую сборку и проверить её прямо сейчас.
|
||||
|
||||
### Звонки в Telegram через MTProxy
|
||||
- Архитектура Telegram **НЕ поддерживает звонки через MTProxy**, а только через SOCKS5, который невозможно замаскировать
|
||||
|
||||
### Как DPI распознает TLS-соединение MTProxy?
|
||||
- DPI распознает MTProxy в режиме Fake TLS (ee) как TLS 1.3
|
||||
- указанный вами SNI отправляется как клиентом, так и сервером;
|
||||
- ALPN аналогичен HTTP 1.1/2;
|
||||
- высокая энтропия, что нормально для трафика, зашифрованного AES;
|
||||
|
||||
### Белый список по IP
|
||||
- MTProxy не может работать, если:
|
||||
- отсутствует IP-связь с целевым хостом: российский белый список в мобильных сетях — «Белый список»;
|
||||
- ИЛИ весь TCP-трафик заблокирован;
|
||||
- ИЛИ трафик с высокой энтропией/зашифрованный трафик заблокирован: контент-фильтры в университетах и критически важной инфраструктуре;
|
||||
- ИЛИ весь TLS-трафик заблокирован;
|
||||
- ИЛИ заблокирован указанный порт: используйте 443, чтобы сделать его «как настоящий»;
|
||||
- ИЛИ заблокирован предоставленный SNI: используйте «официально одобренное»/безобидное имя;
|
||||
- как и большинство протоколов в Интернете;
|
||||
- такие ситуации наблюдаются:
|
||||
- в Китае за Великим файрволом;
|
||||
- в России в мобильных сетях, реже в проводных сетях;
|
||||
- в Иране во время «активности».
|
||||
|
||||
|
||||
## Зачем нужен middle proxy (ME)
|
||||
https://github.com/telemt/telemt/discussions/167
|
||||
@@ -104,7 +220,7 @@ max_connections = 10000 # 0 - без ограничений, 10000 - по у
|
||||
```
|
||||
|
||||
### Upstream Manager
|
||||
Для настройки исходящих подключений (апстримов) добавьте соответствующие параметры в секцию `[[upstreams]]` файла конфигурации:
|
||||
Для настройки исходящих подключений (Upstreams) добавьте соответствующие параметры в секцию `[[upstreams]]` файла конфигурации:
|
||||
|
||||
#### Привязка к исходящему IP-адресу
|
||||
```toml
|
||||
@@ -119,20 +235,20 @@ interface = "192.168.1.100" # Замените на ваш исходящий IP
|
||||
- Без авторизации:
|
||||
```toml
|
||||
[[upstreams]]
|
||||
type = "socks5" # Specify SOCKS4 or SOCKS5
|
||||
address = "1.2.3.4:1234" # SOCKS-server Address
|
||||
weight = 1 # Set Weight for Scenarios
|
||||
type = "socks5" # выбор типа SOCKS4 или SOCKS5
|
||||
address = "1.2.3.4:1234" # адрес сервера SOCKS
|
||||
weight = 1 # вес
|
||||
enabled = true
|
||||
```
|
||||
|
||||
- С авторизацией:
|
||||
```toml
|
||||
[[upstreams]]
|
||||
type = "socks5" # Specify SOCKS4 or SOCKS5
|
||||
address = "1.2.3.4:1234" # SOCKS-server Address
|
||||
username = "user" # Username for Auth on SOCKS-server
|
||||
password = "pass" # Password for Auth on SOCKS-server
|
||||
weight = 1 # Set Weight for Scenarios
|
||||
type = "socks5" # выбор типа SOCKS4 или SOCKS5
|
||||
address = "1.2.3.4:1234" # адрес сервера SOCKS
|
||||
username = "user" # имя пользователя
|
||||
password = "pass" # пароль
|
||||
weight = 1 # вес
|
||||
enabled = true
|
||||
```
|
||||
|
||||
|
||||
@@ -128,8 +128,8 @@ WorkingDirectory=/opt/telemt
|
||||
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
||||
Restart=on-failure
|
||||
LimitNOFILE=65536
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
@@ -128,8 +128,8 @@ WorkingDirectory=/opt/telemt
|
||||
ExecStart=/bin/telemt /etc/telemt/telemt.toml
|
||||
Restart=on-failure
|
||||
LimitNOFILE=65536
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
BIN
docs/assets/telegram_button.png
Normal file
BIN
docs/assets/telegram_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/assets/telemt.png
Normal file
BIN
docs/assets/telemt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
285
install.sh
285
install.sh
@@ -8,12 +8,20 @@ CONFIG_DIR="${CONFIG_DIR:-/etc/telemt}"
|
||||
CONFIG_FILE="${CONFIG_FILE:-${CONFIG_DIR}/telemt.toml}"
|
||||
WORK_DIR="${WORK_DIR:-/opt/telemt}"
|
||||
TLS_DOMAIN="${TLS_DOMAIN:-petrovich.ru}"
|
||||
SERVER_PORT="${SERVER_PORT:-443}"
|
||||
USER_SECRET=""
|
||||
AD_TAG=""
|
||||
SERVICE_NAME="telemt"
|
||||
TEMP_DIR=""
|
||||
SUDO=""
|
||||
CONFIG_PARENT_DIR=""
|
||||
SERVICE_START_FAILED=0
|
||||
|
||||
PORT_PROVIDED=0
|
||||
SECRET_PROVIDED=0
|
||||
AD_TAG_PROVIDED=0
|
||||
DOMAIN_PROVIDED=0
|
||||
|
||||
ACTION="install"
|
||||
TARGET_VERSION="${VERSION:-latest}"
|
||||
|
||||
@@ -25,8 +33,37 @@ while [ $# -gt 0 ]; do
|
||||
printf '[ERROR] %s requires a domain argument.\n' "$1" >&2
|
||||
exit 1
|
||||
fi
|
||||
TLS_DOMAIN="$2"
|
||||
shift 2 ;;
|
||||
TLS_DOMAIN="$2"; DOMAIN_PROVIDED=1; shift 2 ;;
|
||||
-p|--port)
|
||||
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||
printf '[ERROR] %s requires a port argument.\n' "$1" >&2; exit 1
|
||||
fi
|
||||
case "$2" in
|
||||
*[!0-9]*) printf '[ERROR] Port must be a valid number.\n' >&2; exit 1 ;;
|
||||
esac
|
||||
port_num="$(printf '%s\n' "$2" | sed 's/^0*//')"
|
||||
[ -z "$port_num" ] && port_num="0"
|
||||
if [ "${#port_num}" -gt 5 ] || [ "$port_num" -lt 1 ] || [ "$port_num" -gt 65535 ]; then
|
||||
printf '[ERROR] Port must be between 1 and 65535.\n' >&2; exit 1
|
||||
fi
|
||||
SERVER_PORT="$port_num"; PORT_PROVIDED=1; shift 2 ;;
|
||||
-s|--secret)
|
||||
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||
printf '[ERROR] %s requires a secret argument.\n' "$1" >&2; exit 1
|
||||
fi
|
||||
case "$2" in
|
||||
*[!0-9a-fA-F]*)
|
||||
printf '[ERROR] Secret must contain only hex characters.\n' >&2; exit 1 ;;
|
||||
esac
|
||||
if [ "${#2}" -ne 32 ]; then
|
||||
printf '[ERROR] Secret must be exactly 32 chars.\n' >&2; exit 1
|
||||
fi
|
||||
USER_SECRET="$2"; SECRET_PROVIDED=1; shift 2 ;;
|
||||
-a|--ad-tag|--ad_tag)
|
||||
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
||||
printf '[ERROR] %s requires an ad_tag argument.\n' "$1" >&2; exit 1
|
||||
fi
|
||||
AD_TAG="$2"; AD_TAG_PROVIDED=1; shift 2 ;;
|
||||
uninstall|--uninstall)
|
||||
if [ "$ACTION" != "purge" ]; then ACTION="uninstall"; fi
|
||||
shift ;;
|
||||
@@ -59,12 +96,17 @@ cleanup() {
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
show_help() {
|
||||
say "Usage: $0 [ <version> | install | uninstall | purge ] [ -d <domain> ] [ --help ]"
|
||||
say "Usage: $0 [ <version> | install | uninstall | purge ] [ options ]"
|
||||
say " <version> Install specific version (e.g. 3.3.15, default: latest)"
|
||||
say " install Install the latest version"
|
||||
say " uninstall Remove the binary and service (keeps config and user)"
|
||||
say " uninstall Remove the binary and service"
|
||||
say " purge Remove everything including configuration, data, and user"
|
||||
say ""
|
||||
say "Options:"
|
||||
say " -d, --domain Set TLS domain (default: petrovich.ru)"
|
||||
say " -p, --port Set server port (default: 443)"
|
||||
say " -s, --secret Set specific user secret (32 hex characters)"
|
||||
say " -a, --ad-tag Set ad_tag"
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -81,13 +123,13 @@ get_realpath() {
|
||||
path_in="$1"
|
||||
case "$path_in" in /*) ;; *) path_in="$(pwd)/$path_in" ;; esac
|
||||
|
||||
if command -v realpath >/dev/null 2>&1; then
|
||||
if command -v realpath >/dev/null 2>&1; then
|
||||
if realpath_out="$(realpath -m "$path_in" 2>/dev/null)"; then
|
||||
printf '%s\n' "$realpath_out"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if command -v readlink >/dev/null 2>&1; then
|
||||
resolved_path="$(readlink -f "$path_in" 2>/dev/null || true)"
|
||||
if [ -n "$resolved_path" ]; then
|
||||
@@ -120,6 +162,14 @@ get_svc_mgr() {
|
||||
else echo "none"; fi
|
||||
}
|
||||
|
||||
is_config_exists() {
|
||||
if [ -n "$SUDO" ]; then
|
||||
$SUDO sh -c '[ -f "$1" ]' _ "$CONFIG_FILE"
|
||||
else
|
||||
[ -f "$CONFIG_FILE" ]
|
||||
fi
|
||||
}
|
||||
|
||||
verify_common() {
|
||||
[ -n "$BIN_NAME" ] || die "BIN_NAME cannot be empty."
|
||||
[ -n "$INSTALL_DIR" ] || die "INSTALL_DIR cannot be empty."
|
||||
@@ -127,7 +177,7 @@ verify_common() {
|
||||
[ -n "$CONFIG_FILE" ] || die "CONFIG_FILE cannot be empty."
|
||||
|
||||
case "${INSTALL_DIR}${CONFIG_DIR}${WORK_DIR}${CONFIG_FILE}" in
|
||||
*[!a-zA-Z0-9_./-]*) die "Invalid characters in paths. Only alphanumeric, _, ., -, and / allowed." ;;
|
||||
*[!a-zA-Z0-9_./-]*) die "Invalid characters in paths." ;;
|
||||
esac
|
||||
|
||||
case "$TARGET_VERSION" in *[!a-zA-Z0-9_.-]*) die "Invalid characters in version." ;; esac
|
||||
@@ -145,11 +195,11 @@ verify_common() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
SUDO=""
|
||||
else
|
||||
command -v sudo >/dev/null 2>&1 || die "This script requires root or sudo. Neither found."
|
||||
command -v sudo >/dev/null 2>&1 || die "This script requires root or sudo."
|
||||
SUDO="sudo"
|
||||
if ! sudo -n true 2>/dev/null; then
|
||||
if ! [ -t 0 ]; then
|
||||
die "sudo requires a password, but no TTY detected. Aborting to prevent hang."
|
||||
die "sudo requires a password, but no TTY detected."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -162,21 +212,7 @@ verify_common() {
|
||||
die "Safety check failed: CONFIG_FILE '$CONFIG_FILE' is a directory."
|
||||
fi
|
||||
|
||||
for path in "$CONFIG_DIR" "$CONFIG_PARENT_DIR" "$WORK_DIR"; do
|
||||
check_path="$(get_realpath "$path")"
|
||||
case "$check_path" in
|
||||
/|/bin|/sbin|/usr|/usr/bin|/usr/sbin|/usr/local|/usr/local/bin|/usr/local/sbin|/usr/local/etc|/usr/local/share|/etc|/var|/var/lib|/var/log|/var/run|/home|/root|/tmp|/lib|/lib64|/opt|/run|/boot|/dev|/sys|/proc)
|
||||
die "Safety check failed: '$path' (resolved to '$check_path') is a critical system directory." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
check_install_dir="$(get_realpath "$INSTALL_DIR")"
|
||||
case "$check_install_dir" in
|
||||
/|/etc|/var|/home|/root|/tmp|/usr|/usr/local|/opt|/boot|/dev|/sys|/proc|/run)
|
||||
die "Safety check failed: INSTALL_DIR '$INSTALL_DIR' is a critical system directory." ;;
|
||||
esac
|
||||
|
||||
for cmd in id uname grep find rm chown chmod mv mktemp mkdir tr dd sed ps head sleep cat tar gzip rmdir; do
|
||||
for cmd in id uname awk grep find rm chown chmod mv mktemp mkdir tr dd sed ps head sleep cat tar gzip; do
|
||||
command -v "$cmd" >/dev/null 2>&1 || die "Required command not found: $cmd"
|
||||
done
|
||||
}
|
||||
@@ -185,14 +221,41 @@ verify_install_deps() {
|
||||
command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 || die "Neither curl nor wget is installed."
|
||||
command -v cp >/dev/null 2>&1 || command -v install >/dev/null 2>&1 || die "Need cp or install"
|
||||
|
||||
if ! command -v setcap >/dev/null 2>&1; then
|
||||
if ! command -v setcap >/dev/null 2>&1 || ! command -v conntrack >/dev/null 2>&1; then
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
$SUDO apk add --no-cache libcap-utils >/dev/null 2>&1 || $SUDO apk add --no-cache libcap >/dev/null 2>&1 || true
|
||||
$SUDO apk add --no-cache libcap-utils libcap conntrack-tools >/dev/null 2>&1 || true
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
$SUDO apt-get update -q >/dev/null 2>&1 || true
|
||||
$SUDO apt-get install -y -q libcap2-bin >/dev/null 2>&1 || true
|
||||
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y -q libcap >/dev/null 2>&1 || true
|
||||
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y -q libcap >/dev/null 2>&1 || true
|
||||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -q libcap2-bin conntrack >/dev/null 2>&1 || {
|
||||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get update -q >/dev/null 2>&1 || true
|
||||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -q libcap2-bin conntrack >/dev/null 2>&1 || true
|
||||
}
|
||||
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y -q libcap conntrack-tools >/dev/null 2>&1 || true
|
||||
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y -q libcap conntrack-tools >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_port_availability() {
|
||||
port_info=""
|
||||
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
port_info=$($SUDO ss -tulnp 2>/dev/null | grep -E ":${SERVER_PORT}([[:space:]]|$)" || true)
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
port_info=$($SUDO netstat -tulnp 2>/dev/null | grep -E ":${SERVER_PORT}([[:space:]]|$)" || true)
|
||||
elif command -v lsof >/dev/null 2>&1; then
|
||||
port_info=$($SUDO lsof -i :${SERVER_PORT} 2>/dev/null | grep LISTEN || true)
|
||||
else
|
||||
say "[WARNING] Network diagnostic tools (ss, netstat, lsof) not found. Skipping port check."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -n "$port_info" ]; then
|
||||
if printf '%s\n' "$port_info" | grep -q "${BIN_NAME}"; then
|
||||
say " -> Port ${SERVER_PORT} is in use by ${BIN_NAME}. Ignoring as it will be restarted."
|
||||
else
|
||||
say "[ERROR] Port ${SERVER_PORT} is already in use by another process:"
|
||||
printf ' %s\n' "$port_info"
|
||||
die "Please free the port ${SERVER_PORT} or change it and try again."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -250,10 +313,10 @@ ensure_user_group() {
|
||||
|
||||
setup_dirs() {
|
||||
$SUDO mkdir -p "$WORK_DIR" "$CONFIG_DIR" "$CONFIG_PARENT_DIR" || die "Failed to create directories"
|
||||
|
||||
|
||||
$SUDO chown telemt:telemt "$WORK_DIR" && $SUDO chmod 750 "$WORK_DIR"
|
||||
$SUDO chown root:telemt "$CONFIG_DIR" && $SUDO chmod 750 "$CONFIG_DIR"
|
||||
|
||||
|
||||
if [ "$CONFIG_PARENT_DIR" != "$CONFIG_DIR" ] && [ "$CONFIG_PARENT_DIR" != "." ] && [ "$CONFIG_PARENT_DIR" != "/" ]; then
|
||||
$SUDO chown root:telemt "$CONFIG_PARENT_DIR" && $SUDO chmod 750 "$CONFIG_PARENT_DIR"
|
||||
fi
|
||||
@@ -275,17 +338,19 @@ install_binary() {
|
||||
fi
|
||||
|
||||
$SUDO mkdir -p "$INSTALL_DIR" || die "Failed to create install directory"
|
||||
|
||||
$SUDO rm -f "$bin_dst" 2>/dev/null || true
|
||||
|
||||
if command -v install >/dev/null 2>&1; then
|
||||
$SUDO install -m 0755 "$bin_src" "$bin_dst" || die "Failed to install binary"
|
||||
else
|
||||
$SUDO rm -f "$bin_dst" 2>/dev/null || true
|
||||
$SUDO cp "$bin_src" "$bin_dst" && $SUDO chmod 0755 "$bin_dst" || die "Failed to copy binary"
|
||||
fi
|
||||
|
||||
$SUDO sh -c '[ -x "$1" ]' _ "$bin_dst" || die "Binary not executable: $bin_dst"
|
||||
|
||||
if command -v setcap >/dev/null 2>&1; then
|
||||
$SUDO setcap cap_net_bind_service=+ep "$bin_dst" 2>/dev/null || true
|
||||
$SUDO setcap cap_net_bind_service,cap_net_admin=+ep "$bin_dst" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -301,11 +366,20 @@ generate_secret() {
|
||||
}
|
||||
|
||||
generate_config_content() {
|
||||
conf_secret="$1"
|
||||
conf_tag="$2"
|
||||
escaped_tls_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
|
||||
cat <<EOF
|
||||
[general]
|
||||
use_middle_proxy = false
|
||||
use_middle_proxy = true
|
||||
EOF
|
||||
|
||||
if [ -n "$conf_tag" ]; then
|
||||
echo "ad_tag = \"${conf_tag}\""
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
[general.modes]
|
||||
classic = false
|
||||
@@ -313,7 +387,7 @@ secure = false
|
||||
tls = true
|
||||
|
||||
[server]
|
||||
port = 443
|
||||
port = ${SERVER_PORT}
|
||||
|
||||
[server.api]
|
||||
enabled = true
|
||||
@@ -324,28 +398,73 @@ whitelist = ["127.0.0.1/32"]
|
||||
tls_domain = "${escaped_tls_domain}"
|
||||
|
||||
[access.users]
|
||||
hello = "$1"
|
||||
hello = "${conf_secret}"
|
||||
EOF
|
||||
}
|
||||
|
||||
install_config() {
|
||||
if [ -n "$SUDO" ]; then
|
||||
if $SUDO sh -c '[ -f "$1" ]' _ "$CONFIG_FILE"; then
|
||||
say " -> Config already exists at $CONFIG_FILE. Skipping creation."
|
||||
return 0
|
||||
fi
|
||||
elif [ -f "$CONFIG_FILE" ]; then
|
||||
say " -> Config already exists at $CONFIG_FILE. Skipping creation."
|
||||
if is_config_exists; then
|
||||
say " -> Config already exists at $CONFIG_FILE. Updating parameters..."
|
||||
|
||||
tmp_conf="${TEMP_DIR}/config.tmp"
|
||||
$SUDO cat "$CONFIG_FILE" > "$tmp_conf"
|
||||
|
||||
escaped_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
|
||||
export AWK_PORT="$SERVER_PORT"
|
||||
export AWK_SECRET="$USER_SECRET"
|
||||
export AWK_DOMAIN="$escaped_domain"
|
||||
export AWK_AD_TAG="$AD_TAG"
|
||||
export AWK_FLAG_P="$PORT_PROVIDED"
|
||||
export AWK_FLAG_S="$SECRET_PROVIDED"
|
||||
export AWK_FLAG_D="$DOMAIN_PROVIDED"
|
||||
export AWK_FLAG_A="$AD_TAG_PROVIDED"
|
||||
|
||||
awk '
|
||||
BEGIN { ad_tag_handled = 0 }
|
||||
|
||||
ENVIRON["AWK_FLAG_P"] == "1" && /^[ \t]*port[ \t]*=/ { print "port = " ENVIRON["AWK_PORT"]; next }
|
||||
ENVIRON["AWK_FLAG_S"] == "1" && /^[ \t]*hello[ \t]*=/ { print "hello = \"" ENVIRON["AWK_SECRET"] "\""; next }
|
||||
ENVIRON["AWK_FLAG_D"] == "1" && /^[ \t]*tls_domain[ \t]*=/ { print "tls_domain = \"" ENVIRON["AWK_DOMAIN"] "\""; next }
|
||||
|
||||
ENVIRON["AWK_FLAG_A"] == "1" && /^[ \t]*ad_tag[ \t]*=/ {
|
||||
if (!ad_tag_handled) {
|
||||
print "ad_tag = \"" ENVIRON["AWK_AD_TAG"] "\"";
|
||||
ad_tag_handled = 1;
|
||||
}
|
||||
next
|
||||
}
|
||||
ENVIRON["AWK_FLAG_A"] == "1" && /^\[general\]/ {
|
||||
print;
|
||||
if (!ad_tag_handled) {
|
||||
print "ad_tag = \"" ENVIRON["AWK_AD_TAG"] "\"";
|
||||
ad_tag_handled = 1;
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
{ print }
|
||||
' "$tmp_conf" > "${tmp_conf}.new" && mv "${tmp_conf}.new" "$tmp_conf"
|
||||
|
||||
[ "$PORT_PROVIDED" -eq 1 ] && say " -> Updated port: $SERVER_PORT"
|
||||
[ "$SECRET_PROVIDED" -eq 1 ] && say " -> Updated secret for user 'hello'"
|
||||
[ "$DOMAIN_PROVIDED" -eq 1 ] && say " -> Updated tls_domain: $TLS_DOMAIN"
|
||||
[ "$AD_TAG_PROVIDED" -eq 1 ] && say " -> Updated ad_tag"
|
||||
|
||||
write_root "$CONFIG_FILE" < "$tmp_conf"
|
||||
rm -f "$tmp_conf"
|
||||
return 0
|
||||
fi
|
||||
|
||||
toml_secret="$(generate_secret)" || die "Failed to generate secret."
|
||||
if [ -z "$USER_SECRET" ]; then
|
||||
USER_SECRET="$(generate_secret)" || die "Failed to generate secret."
|
||||
fi
|
||||
|
||||
generate_config_content "$toml_secret" | write_root "$CONFIG_FILE" || die "Failed to install config"
|
||||
generate_config_content "$USER_SECRET" "$AD_TAG" | write_root "$CONFIG_FILE" || die "Failed to install config"
|
||||
$SUDO chown root:telemt "$CONFIG_FILE" && $SUDO chmod 640 "$CONFIG_FILE"
|
||||
|
||||
say " -> Config created successfully."
|
||||
say " -> Generated secret for default user 'hello': $toml_secret"
|
||||
say " -> Configured secret for user 'hello': $USER_SECRET"
|
||||
}
|
||||
|
||||
generate_systemd_content() {
|
||||
@@ -362,9 +481,10 @@ Group=telemt
|
||||
WorkingDirectory=$WORK_DIR
|
||||
ExecStart="${INSTALL_DIR}/${BIN_NAME}" "${CONFIG_FILE}"
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
LimitNOFILE=65536
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -395,7 +515,7 @@ install_service() {
|
||||
|
||||
$SUDO systemctl daemon-reload || true
|
||||
$SUDO systemctl enable "$SERVICE_NAME" || true
|
||||
|
||||
|
||||
if ! $SUDO systemctl start "$SERVICE_NAME"; then
|
||||
say "[WARNING] Failed to start service"
|
||||
SERVICE_START_FAILED=1
|
||||
@@ -405,16 +525,16 @@ install_service() {
|
||||
$SUDO chown root:root "/etc/init.d/${SERVICE_NAME}" && $SUDO chmod 0755 "/etc/init.d/${SERVICE_NAME}"
|
||||
|
||||
$SUDO rc-update add "$SERVICE_NAME" default 2>/dev/null || true
|
||||
|
||||
|
||||
if ! $SUDO rc-service "$SERVICE_NAME" start 2>/dev/null; then
|
||||
say "[WARNING] Failed to start service"
|
||||
SERVICE_START_FAILED=1
|
||||
fi
|
||||
else
|
||||
cmd="\"${INSTALL_DIR}/${BIN_NAME}\" \"${CONFIG_FILE}\""
|
||||
if [ -n "$SUDO" ]; then
|
||||
if [ -n "$SUDO" ]; then
|
||||
say " -> Service manager not found. Start manually: sudo -u telemt $cmd"
|
||||
else
|
||||
else
|
||||
say " -> Service manager not found. Start manually: su -s /bin/sh telemt -c '$cmd'"
|
||||
fi
|
||||
fi
|
||||
@@ -429,9 +549,10 @@ kill_user_procs() {
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
pids="$(pgrep -u telemt 2>/dev/null || true)"
|
||||
else
|
||||
pids="$(ps -u telemt -o pid= 2>/dev/null || true)"
|
||||
pids="$(ps -ef 2>/dev/null | awk '$1=="telemt"{print $2}' || true)"
|
||||
[ -z "$pids" ] && pids="$(ps 2>/dev/null | awk '$2=="telemt"{print $1}' || true)"
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$pids" ]; then
|
||||
for pid in $pids; do
|
||||
case "$pid" in ''|*[!0-9]*) continue ;; *) $SUDO kill "$pid" 2>/dev/null || true ;; esac
|
||||
@@ -471,15 +592,16 @@ uninstall() {
|
||||
say ">>> Stage 5: Purging configuration, data, and user"
|
||||
$SUDO rm -rf "$CONFIG_DIR" "$WORK_DIR"
|
||||
$SUDO rm -f "$CONFIG_FILE"
|
||||
if [ "$CONFIG_PARENT_DIR" != "$CONFIG_DIR" ] && [ "$CONFIG_PARENT_DIR" != "." ] && [ "$CONFIG_PARENT_DIR" != "/" ]; then
|
||||
$SUDO rmdir "$CONFIG_PARENT_DIR" 2>/dev/null || true
|
||||
fi
|
||||
sleep 1
|
||||
$SUDO userdel telemt 2>/dev/null || $SUDO deluser telemt 2>/dev/null || true
|
||||
$SUDO groupdel telemt 2>/dev/null || $SUDO delgroup telemt 2>/dev/null || true
|
||||
|
||||
if check_os_entity group telemt; then
|
||||
$SUDO groupdel telemt 2>/dev/null || $SUDO delgroup telemt 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
say "Note: Configuration and user kept. Run with 'purge' to remove completely."
|
||||
fi
|
||||
|
||||
|
||||
printf '\n====================================================================\n'
|
||||
printf ' UNINSTALLATION COMPLETE\n'
|
||||
printf '====================================================================\n\n'
|
||||
@@ -493,18 +615,28 @@ case "$ACTION" in
|
||||
say "Starting installation of $BIN_NAME (Version: $TARGET_VERSION)"
|
||||
|
||||
say ">>> Stage 1: Verifying environment and dependencies"
|
||||
verify_common; verify_install_deps
|
||||
verify_common
|
||||
verify_install_deps
|
||||
|
||||
if [ "$TARGET_VERSION" != "latest" ]; then
|
||||
if is_config_exists && [ "$PORT_PROVIDED" -eq 0 ]; then
|
||||
ext_port="$($SUDO awk -F'=' '/^[ \t]*port[ \t]*=/ {gsub(/[^0-9]/, "", $2); print $2; exit}' "$CONFIG_FILE" 2>/dev/null || true)"
|
||||
if [ -n "$ext_port" ]; then
|
||||
SERVER_PORT="$ext_port"
|
||||
fi
|
||||
fi
|
||||
|
||||
check_port_availability
|
||||
|
||||
if [ "$TARGET_VERSION" != "latest" ]; then
|
||||
TARGET_VERSION="${TARGET_VERSION#v}"
|
||||
fi
|
||||
|
||||
|
||||
ARCH="$(detect_arch)"; LIBC="$(detect_libc)"
|
||||
FILE_NAME="${BIN_NAME}-${ARCH}-linux-${LIBC}.tar.gz"
|
||||
|
||||
|
||||
if [ "$TARGET_VERSION" = "latest" ]; then
|
||||
DL_URL="https://github.com/${REPO}/releases/latest/download/${FILE_NAME}"
|
||||
else
|
||||
else
|
||||
DL_URL="https://github.com/${REPO}/releases/download/${TARGET_VERSION}/${FILE_NAME}"
|
||||
fi
|
||||
|
||||
@@ -521,7 +653,7 @@ case "$ACTION" in
|
||||
FILE_NAME="${BIN_NAME}-${ARCH}-linux-${LIBC}.tar.gz"
|
||||
if [ "$TARGET_VERSION" = "latest" ]; then
|
||||
DL_URL="https://github.com/${REPO}/releases/latest/download/${FILE_NAME}"
|
||||
else
|
||||
else
|
||||
DL_URL="https://github.com/${REPO}/releases/download/${TARGET_VERSION}/${FILE_NAME}"
|
||||
fi
|
||||
fetch_file "$DL_URL" "${TEMP_DIR}/${FILE_NAME}" || die "Download failed"
|
||||
@@ -540,13 +672,13 @@ case "$ACTION" in
|
||||
|
||||
say ">>> Stage 4: Setting up environment (User, Group, Directories)"
|
||||
ensure_user_group; setup_dirs; stop_service
|
||||
|
||||
|
||||
say ">>> Stage 5: Installing binary"
|
||||
install_binary "$EXTRACTED_BIN" "${INSTALL_DIR}/${BIN_NAME}"
|
||||
|
||||
say ">>> Stage 6: Generating configuration"
|
||||
|
||||
say ">>> Stage 6: Generating/Updating configuration"
|
||||
install_config
|
||||
|
||||
|
||||
say ">>> Stage 7: Installing and starting service"
|
||||
install_service
|
||||
|
||||
@@ -561,7 +693,7 @@ case "$ACTION" in
|
||||
printf ' INSTALLATION SUCCESS\n'
|
||||
printf '====================================================================\n\n'
|
||||
fi
|
||||
|
||||
|
||||
svc="$(get_svc_mgr)"
|
||||
if [ "$svc" = "systemd" ]; then
|
||||
printf 'To check the status of your proxy service, run:\n'
|
||||
@@ -570,15 +702,18 @@ case "$ACTION" in
|
||||
printf 'To check the status of your proxy service, run:\n'
|
||||
printf ' rc-service %s status\n\n' "$SERVICE_NAME"
|
||||
fi
|
||||
|
||||
|
||||
API_LISTEN="$($SUDO awk -F'"' '/^[ \t]*listen[ \t]*=/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null || true)"
|
||||
API_LISTEN="${API_LISTEN:-127.0.0.1:9091}"
|
||||
|
||||
printf 'To get your user connection links (for Telegram), run:\n'
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
printf ' curl -s http://127.0.0.1:9091/v1/users | jq -r '\''.data[] | "User: \\(.username)\\n\\(.links.tls[0] // empty)\\n"'\''\n'
|
||||
printf ' curl -s http://%s/v1/users | jq -r '\''.data[]? | "User: \\(.username)\\n\\(.links.tls[0] // empty)\\n"'\''\n' "$API_LISTEN"
|
||||
else
|
||||
printf ' curl -s http://127.0.0.1:9091/v1/users\n'
|
||||
printf ' curl -s http://%s/v1/users\n' "$API_LISTEN"
|
||||
printf ' (Tip: Install '\''jq'\'' for a much cleaner output)\n'
|
||||
fi
|
||||
|
||||
|
||||
printf '\n====================================================================\n'
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user