mirror of
https://github.com/telemt/telemt.git
synced 2026-09-05 18:16:06 +03:00
Docs for WEB
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -265,7 +265,7 @@ A sparse JSON object containing only the top-level config sections to modify. Ea
|
||||
|
||||
**Rejected keys:**
|
||||
- `access` → `400 access_not_editable` (users/secrets are managed via `POST/PATCH /v1/users`).
|
||||
- `network`, or any unknown top-level key → `400 section_not_editable`.
|
||||
- `network`, `web`, or any unknown top-level key → `400 section_not_editable`.
|
||||
- `server` with any key other than `listeners` (e.g. `port`, `api`, `admin_api`) → `400 field_not_editable`.
|
||||
- An object with no editable keys → `400 bad_request` (empty patch).
|
||||
|
||||
@@ -1418,7 +1418,7 @@ Applies a sparse patch to the editable config sections. The merged config is ful
|
||||
| Key | HTTP | `error.code` |
|
||||
| --- | --- | --- |
|
||||
| `access` | `400` | `access_not_editable` |
|
||||
| `network`, or any unknown top-level key | `400` | `section_not_editable` |
|
||||
| `network`, `web`, or any unknown top-level key | `400` | `section_not_editable` |
|
||||
| `server` with keys other than `listeners` | `400` | `field_not_editable` |
|
||||
| Object with no editable key | `400` | `bad_request` |
|
||||
|
||||
@@ -1522,6 +1522,29 @@ Reload preparation requires every configured TLS-front domain to have a non-defa
|
||||
|
||||
The revision is verified again after preparation. With `failure_policy=rollback`, a changed revision or revision read failure rolls the candidate back; with `failure_policy=keep_new`, the condition is reported in `warnings` and activation continues.
|
||||
|
||||
## WEB Proxy Management
|
||||
|
||||
The API provides partial operational control for WEB mode; it does not expose a dedicated `/v1/web` resource.
|
||||
|
||||
| Operation | Current contract |
|
||||
| --- | --- |
|
||||
| Read or patch `[web]`, vhosts, profiles, decoys, timeouts, or limits | Not exposed. `GET /v1/config` omits `[web]`; a `web` key in `PATCH /v1/config` returns `400 section_not_editable`. |
|
||||
| Persist `server.listeners` | Supported through `PATCH /v1/config`. Arrays replace wholesale. A changed WEB listener is process-owned and remains deferred until process restart. |
|
||||
| Apply an externally edited WEB config | Update the owning TOML source, call `POST /v1/system/reload`, then poll `GET /v1/system/reload/{id}`. |
|
||||
| Inspect restart requirements | Read `deferred_process_fields` from reload status. `server.listeners` and `web.limits` require process restart. |
|
||||
| Manage access users | Use `/v1/users`. Creating a user does not add it to `web.vhosts.profiles`; profile membership remains file-managed. |
|
||||
| Disable one user | `POST /v1/users/{username}/disable` updates admission immediately and cancels the user's active sessions. |
|
||||
| Rotate a profiled user's secret | Use `/v1/users/{username}/rotate-secret`; the config watcher rebuilds WEB capabilities from the new access snapshot. The API returns the secret, not a `tg://webproxy` link. |
|
||||
| Read WEB-specific runtime statistics | No WEB-specific endpoint exists in the current API surface. |
|
||||
|
||||
`web.enabled`, `web.timeouts`, vhosts, profiles, and decoy snapshots are runtime-generation fields. WEB listener inventory and trust policy, plus all `[web.limits]`, are process-owned. A successful reload can therefore activate the runtime-owned subset while reporting the process-owned subset as deferred.
|
||||
|
||||
Before deleting a user referenced by a WEB profile, remove and apply the profile first. User mutations validate the complete resulting configuration, so a dangling WEB profile is rejected rather than persisted.
|
||||
|
||||
The API whitelist is evaluated against the direct TCP peer and does not use the WEB listener's `X-Forwarded-For` policy. Keep the API on a separate loopback or private bind, use a narrow whitelist and a non-empty exact `auth_header`, and do not expose it through the public WEB vhost.
|
||||
|
||||
Deployment, TLS-terminator examples, links, and WEB-specific verification are documented in the [WEB proxy guide](../../WEB/WEB_PROXY.en.md).
|
||||
|
||||
## Mutation Semantics
|
||||
|
||||
| Endpoint | Notes |
|
||||
|
||||
@@ -24,6 +24,12 @@ Dieses Dokument listet alle Konfigurationsschlüssel auf, die `config.toml` akze
|
||||
- [server.conntrack_control](#serverconntrack_control)
|
||||
- [server.api](#serverapi)
|
||||
- [server.listeners](#serverlisteners)
|
||||
- [web](#web)
|
||||
- [web.limits](#weblimits)
|
||||
- [web.timeouts](#webtimeouts)
|
||||
- [web.vhosts](#webvhosts)
|
||||
- [web.vhosts.decoy](#webvhostsdecoy)
|
||||
- [web.vhosts.profiles](#webvhostsprofiles)
|
||||
- [timeouts](#timeouts)
|
||||
- [censorship](#censorship)
|
||||
- [censorship.tls_fetch](#censorshiptls_fetch)
|
||||
@@ -2324,6 +2330,9 @@ Hinweis: Dieser Abschnitt akzeptiert auch den Legacy-Alias `[server.admin_api]`
|
||||
| [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` |
|
||||
| [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` |
|
||||
| [`reuse_allow`](#reuse_allow) | `bool` | `false` | `✘` |
|
||||
| [`transport`](#transport-serverlisteners) | `"mtproxy"` oder `"web"` | `"mtproxy"` | `✘` |
|
||||
| [`web_client_ip_source`](#web_client_ip_source-serverlisteners) | `"x_forwarded_for"` | `"x_forwarded_for"` | `✘` |
|
||||
| [`web_trusted_proxy_cidrs`](#web_trusted_proxy_cidrs-serverlisteners) | `IpNetwork[]` | `[]` | `✘` |
|
||||
|
||||
## ip
|
||||
- **Einschränkungen / Validierung**: Erforderliches Feld. Muss ein `IpAddr` sein.
|
||||
@@ -2517,6 +2526,141 @@ Hinweis: Dieser Abschnitt akzeptiert auch den Legacy-Alias `[server.admin_api]`
|
||||
reuse_allow = false
|
||||
```
|
||||
|
||||
## transport (server.listeners)
|
||||
- **Einschränkungen / Validierung**: `"mtproxy"` oder `"web"`.
|
||||
- **Beschreibung**: Wählt das vom Listener akzeptierte Protokoll. Ein WEB-Listener empfängt unverschlüsseltes HTTP/1.1 von einem vertrauenswürdigen TLS-Terminator und erfordert einen Prozessneustart. Er muss `proxy_protocol = false` und `reuse_allow = false` verwenden; `client_mss`, `synlimit`, `announce` und `announce_ip` sind nicht zulässig.
|
||||
- **Beispiel**:
|
||||
|
||||
```toml
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
```
|
||||
|
||||
## web_client_ip_source (server.listeners)
|
||||
- **Einschränkungen / Validierung**: Die erste WEB-Implementierung unterstützt ausschließlich `"x_forwarded_for"`.
|
||||
- **Beschreibung**: Wählt die L7-Quelle der ursprünglichen Client-IP. Telemt akzeptiert genau eine kanonische `X-Forwarded-For`-Adresse und nur dann, wenn der direkte TCP-Peer zu `web_trusted_proxy_cidrs` gehört.
|
||||
|
||||
## web_trusted_proxy_cidrs (server.listeners)
|
||||
- **Einschränkungen / Validierung**: Nicht leeres CIDR-Array nur für WEB; ein `/0`-Netz wird abgelehnt. Für einen MTProxy-Listener ist das Feld ungültig.
|
||||
- **Beschreibung**: Vertrauensgrenze für den unmittelbar vorgeschalteten NGINX- oder HAProxy-Peer. Tragen Sie nur Adressen ein, die diesen Listener direkt erreichen können, und veröffentlichen Sie den unverschlüsselten Listener niemals in einem nicht vertrauenswürdigen Netz.
|
||||
|
||||
|
||||
# [web]
|
||||
|
||||
Der WEB-Modus transportiert MTProxy-Datenverkehr von Telegram Desktop über HTTPS, dessen TLS-Verbindung von einem externen NGINX oder HAProxy terminiert wird. Telemt empfängt unverschlüsseltes HTTP/1.1 auf einem privaten Listener mit `transport = "web"`. Lesen Sie vor der Aktivierung die [vollständige WEB-Bereitstellungsanleitung](../WEB/WEB_PROXY.de.md).
|
||||
|
||||
| Schlüssel | Typ | Default | Hot-Reload |
|
||||
| --- | --- | --- | --- |
|
||||
| `enabled` | `bool` | `false` | `✔` |
|
||||
| `limits` | Tabelle | begrenzte Defaults | `✘` |
|
||||
| `timeouts` | Tabelle | begrenzte Defaults | `✔` |
|
||||
| `vhosts` | Tabellen-Array | `[]` | `✔` |
|
||||
|
||||
`enabled = true` erfordert mindestens einen durch die Netzwerkrichtlinie zugelassenen WEB-Listener, einen vhost und mindestens ein Profil in jedem vhost. Das Deaktivieren von WEB beendet nach dem Reload die Ausgabe neuer Bridge- und Session-Zugangsdaten; zum Widerrufen aktiver Sitzungen eines einzelnen Benutzers verwenden Sie die Users-API.
|
||||
|
||||
# [web.limits]
|
||||
|
||||
Diese prozessweiten Obergrenzen begrenzen alle WEB-Register, Warteschlangen, Request-Bodys, statischen Snapshots und Admission-Pfade. Alle Werte werden gemeinsam validiert: Eigentümerbezogene Grenzen dürfen die globalen Grenzen nicht überschreiten, Queue-Reserven müssen den Fortschritt von Control Frames gewährleisten, Body-Reservierungen müssen in ihr globales Budget passen und alle deklarierten Byte-Grenzen müssen in `memory_envelope_bytes` passen. Jede Änderung in dieser Tabelle erfordert einen Prozessneustart.
|
||||
|
||||
| Schlüssel | Typ | Default | Beschreibung |
|
||||
| --- | --- | --- | --- |
|
||||
| `max_header_bytes` | `usize` | `16384` | Maximale Bytes in einem HTTP-Request-Head. |
|
||||
| `max_body_bytes` | `usize` | `2097152` | Maximale Größe eines gesammelten Carrier-Request-Bodys. |
|
||||
| `max_frame_payload_bytes` | `usize` | `1048576` | Maximale Nutzlast eines WEB-Frames. |
|
||||
| `carrier_batch_bytes` | `usize` | `2097152` | Maximale Größe eines kodierten Downlink-Batches. |
|
||||
| `max_frames_per_body` | `usize` | `4096` | Maximale Zahl geparster oder ausgegebener Frames pro Carrier-Body. |
|
||||
| `max_http_connections` | `usize` | `1024` | Prozessweit akzeptierte WEB-HTTP-Verbindungen. |
|
||||
| `max_http_handlers` | `usize` | `512` | Prozessweit gleichzeitig ausgeführte HTTP-Handler. |
|
||||
| `max_body_readers` | `usize` | `32` | Prozessweit gleichzeitig gesammelte Request-Bodys. |
|
||||
| `max_body_bytes_global` | `usize` | `67108864` | Globales Byte-Budget für gesammelte Bodys. |
|
||||
| `max_sessions_global` | `usize` | `128` | Prozessweit aktive WEB-Sitzungen. |
|
||||
| `max_sessions_per_ip` | `usize` | `16` | Aktive Sitzungen pro weitergeleiteter Client-IP. |
|
||||
| `max_streams_per_session` | `usize` | `128` | Standardgrenze aktiver logischer Streams pro Sitzung. |
|
||||
| `max_streams_global` | `usize` | `4096` | Prozessweit aktive logische Streams. |
|
||||
| `max_stream_handshakes` | `usize` | `256` | Gleichzeitig ausgeführte innere MTProxy-Handshakes. |
|
||||
| `max_tombstones_per_session` | `usize` | `4096` | Pro Sitzung gespeicherte IDs geschlossener Streams. |
|
||||
| `pending_bytes_per_session` | `usize` | `33554432` | Eingereihte Daten- und Steuerbytes pro Sitzung. |
|
||||
| `pending_bytes_global` | `usize` | `536870912` | Prozessweit eingereihte Daten- und Steuerbytes. |
|
||||
| `pending_items_per_session` | `usize` | `16384` | Eingereihte Daten- und Steuerelemente pro Sitzung. |
|
||||
| `pending_items_global` | `usize` | `262144` | Prozessweit eingereihte Daten- und Steuerelemente. |
|
||||
| `control_bytes_per_session` | `usize` | `262144` | Nur für Control Frames verfügbares Byte-Budget pro Sitzung. |
|
||||
| `control_bytes_global` | `usize` | `16777216` | Prozessweites, nur für Control Frames verfügbares Byte-Budget. |
|
||||
| `max_bootstraps_global` | `usize` | `512` | Prozessweit aktive Bootstrap-Zugangsdaten. |
|
||||
| `max_bootstraps_per_ip` | `usize` | `64` | Aktive Bootstrap-Zugangsdaten pro Client-IP. |
|
||||
| `max_vhosts` | `usize` | `8` | Konfigurierte virtuelle WEB-Hosts. |
|
||||
| `max_profiles` | `usize` | `32` | WEB-Profile über alle vhosts. |
|
||||
| `max_static_files` | `usize` | `4096` | Einträge statischer Snapshots über alle vhosts. |
|
||||
| `max_static_file_bytes` | `usize` | `8388608` | Maximale Größe einer statischen Datei. |
|
||||
| `max_static_bytes` | `usize` | `67108864` | Bytes statischer Snapshots über alle vhosts. |
|
||||
| `memory_envelope_bytes` | `usize` | `805306368` | Deklarierter Rahmen für HTTP-Heads, Bodys, Queues und statische Snapshots; maximal 4 GiB. |
|
||||
| `new_bootstraps_per_minute` | `u32` | `1200` | Nachhaltige prozessweite Ausgaberate für Bootstraps. |
|
||||
| `new_bootstraps_burst` | `u32` | `256` | Prozessweiter Burst für die Bootstrap-Ausgabe. |
|
||||
| `new_sessions_per_minute` | `u32` | `600` | Nachhaltige prozessweite Erstellungsrate für Sitzungen. |
|
||||
| `new_sessions_burst` | `u32` | `128` | Prozessweiter Burst für die Sitzungserstellung. |
|
||||
| `new_streams_per_minute` | `u32` | `6000` | Nachhaltige Erstellungsrate für logische Streams. |
|
||||
| `new_streams_burst` | `u32` | `512` | Prozessweiter Burst für die Stream-Erstellung. |
|
||||
|
||||
# [web.timeouts]
|
||||
|
||||
Alle Timeouts werden in Sekunden angegeben und müssen im Bereich `1..=3600` liegen. Die längste Request-Deadline muss kleiner als `http_idle_secs` sein.
|
||||
|
||||
| Schlüssel | Typ | Default | Hot-Reload | Beschreibung |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `header_secs` | `u64` | `10` | `✔` | Empfang eines vollständigen HTTP-Request-Heads. |
|
||||
| `body_secs` | `u64` | `30` | `✔` | Sammeln eines authentifizierten Carrier-Bodys. |
|
||||
| `stream_handshake_secs` | `u64` | `10` | `✔` | Abschluss eines inneren MTProxy-Handshakes. |
|
||||
| `long_poll_secs` | `u64` | `25` | `✔` | Maximale Dauer eines leeren Downlink-Long-Polls. |
|
||||
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Lebensdauer ungenutzter Bootstraps und geschlossener Token-Replay-Marker. |
|
||||
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Maximale Carrier-Inaktivität bis zum Schließen der Sitzung. |
|
||||
| `http_idle_secs` | `u64` | `75` | `✔` | Idle-Lebensdauer einer WEB-HTTP-Keep-Alive-Verbindung. |
|
||||
| `shutdown_secs` | `u64` | `15` | `✔` | Deadline für das kontrollierte Beenden von WEB. |
|
||||
| `decoy_header_secs` | `u64` | `30` | `✔` | Deadline für Verbindung und Response-Head eines HTTP-Decoys. |
|
||||
|
||||
# [[web.vhosts]]
|
||||
|
||||
| Schlüssel | Typ | Erforderlich | Hot-Reload | Beschreibung |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `host` | `String` | ja | `✔` | Eindeutiger, kanonischer ACE-FQDN in Kleinbuchstaben ohne Port, Pfad, Zugangsdaten oder abschließenden Punkt. |
|
||||
| `public_addr` | `SocketAddr` | ja | `✔` | Konkrete öffentliche IP auf Port `443`; wird im Ziel-Tupel des inneren Relays verwendet. |
|
||||
| `decoy` | Tabelle | ja | `✔` | Gewöhnlicher Site-Fallback für nicht authentifizierten oder ungültigen Datenverkehr. |
|
||||
| `profiles` | Tabellen-Array | bei aktiviertem WEB | `✔` | Explizite Benutzer und Client-Secret-Modi für diesen Hostnamen. |
|
||||
|
||||
Die weitergeleitete Client-Adresse und `public_addr` müssen dieselbe IP-Familie verwenden. Der Hostname wird bei der Validierung normalisiert und muss von Telegram Desktop akzeptiert werden.
|
||||
|
||||
# [web.vhosts.decoy]
|
||||
|
||||
Genau ein Decoy-Modus ist erforderlich:
|
||||
|
||||
| Modus | Erforderliche Schlüssel | Validierung |
|
||||
| --- | --- | --- |
|
||||
| `http_upstream` | `upstream` | Ein `http://`-Origin mit Loopback-, Link-Local- oder privater IP-Adresse als Literal; keine Zugangsdaten, kein Pfad, Query oder Fragment. |
|
||||
| `static_directory` | `directory`; optional `index = "index.html"` | Absolutes reales Verzeichnis und ein sicherer Index-Dateiname. Symlinks und Pfade außerhalb des Verzeichnisses werden abgelehnt; der unveränderliche Snapshot wird innerhalb von `[web.limits]` geladen. |
|
||||
|
||||
# [[web.vhosts.profiles]]
|
||||
|
||||
| Schlüssel | Typ | Erforderlich | Default | Beschreibung |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `user` | `String` | ja | — | Vorhandener Schlüssel aus `[access.users]`. |
|
||||
| `secret_mode` | `"plain"` oder `"dd"` | ja | — | Exakte Secret-Darstellung für Telegram Desktop. `ee` wird nicht unterstützt. |
|
||||
| `max_sessions` | `usize` | nein | `web.limits.max_sessions_global` | Aktive Sitzungen für dieses Profil. |
|
||||
| `max_streams` | `usize` | nein | `web.limits.max_streams_global` | Aktive logische Streams für dieses Profil. |
|
||||
| `max_streams_per_session` | `usize` | nein | `web.limits.max_streams_per_session` | Aktive logische Streams in einer Profilsitzung. |
|
||||
|
||||
Profilgrenzen müssen ungleich null sein und dürfen die zugehörigen globalen Grenzen nicht überschreiten. Doppelte `(user, secret_mode)`-Profile in einem vhost werden abgelehnt.
|
||||
|
||||
## WEB-Lebenszyklus und API-Verwaltung
|
||||
|
||||
- Config-Watcher und Generations-Reload wenden `web.enabled`, `web.timeouts`, vhosts, Profile und Decoy-Snapshots ohne Prozessneustart an. Bestehende Sitzungen behalten die bei ihrer Erstellung übernommenen Grenzen und Deadlines; neue Arbeit verwendet die aktive Generation.
|
||||
- Bestand und Vertrauensrichtlinie der WEB-Listener unter `server.listeners` sowie alle Werte in `web.limits` sind prozesseigen und erfordern einen Neustart.
|
||||
- Es gibt keinen eigenen Endpunkt `/v1/web`. `GET /v1/config` lässt `[web]` aus und `PATCH /v1/config` lehnt einen Schlüssel `web` mit `400 section_not_editable` ab.
|
||||
- Zum entfernten Anwenden einer WEB-Richtlinie ändern Sie die zuständige TOML-Datei und rufen `POST /v1/system/reload` auf. Prüfen Sie anschließend `GET /v1/system/reload/{id}` und dessen `deferred_process_fields`. Starten Sie Telemt neu, wenn das Feld `server.listeners` oder `web.limits` enthält.
|
||||
- Vorhandene Access-Benutzer können über `/v1/users` erstellt, geändert, rotiert, aktiviert, deaktiviert und gelöscht werden. Das Erstellen eines Benutzers fügt kein WEB-Profil hinzu. Das Deaktivieren aktualisiert die Admission sofort und beendet die aktiven Sitzungen dieses Benutzers.
|
||||
- `PATCH /v1/config` kann `server.listeners` einschließlich der WEB-Listener-Felder speichern; ein geänderter WEB-Listener wird jedoch erst nach einem Prozessneustart aktiv.
|
||||
|
||||
|
||||
# [timeouts]
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
- [server.conntrack_control](#serverconntrack_control)
|
||||
- [server.api](#serverapi)
|
||||
- [server.listeners](#serverlisteners)
|
||||
- [web](#web)
|
||||
- [web.limits](#weblimits)
|
||||
- [web.timeouts](#webtimeouts)
|
||||
- [web.vhosts](#webvhosts)
|
||||
- [web.vhosts.decoy](#webvhostsdecoy)
|
||||
- [web.vhosts.profiles](#webvhostsprofiles)
|
||||
- [timeouts](#timeouts)
|
||||
- [censorship](#censorship)
|
||||
- [censorship.tls_fetch](#censorshiptls_fetch)
|
||||
@@ -2324,6 +2330,9 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
| [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` |
|
||||
| [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` |
|
||||
| [`reuse_allow`](#reuse_allow) | `bool` | `false` | `✘` |
|
||||
| [`transport`](#transport-serverlisteners) | `"mtproxy"` or `"web"` | `"mtproxy"` | `✘` |
|
||||
| [`web_client_ip_source`](#web_client_ip_source-serverlisteners) | `"x_forwarded_for"` | `"x_forwarded_for"` | `✘` |
|
||||
| [`web_trusted_proxy_cidrs`](#web_trusted_proxy_cidrs-serverlisteners) | `IpNetwork[]` | `[]` | `✘` |
|
||||
|
||||
## ip
|
||||
- **Constraints / validation**: Required field. Must be an `IpAddr`.
|
||||
@@ -2517,6 +2526,141 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche
|
||||
reuse_allow = false
|
||||
```
|
||||
|
||||
## transport (server.listeners)
|
||||
- **Constraints / validation**: `"mtproxy"` or `"web"`.
|
||||
- **Description**: Selects the protocol accepted by this listener. A WEB listener receives plain HTTP/1.1 from a trusted TLS terminator and is restart-required. It must set `proxy_protocol = false`, `reuse_allow = false`, and cannot use `client_mss`, `synlimit`, `announce`, or `announce_ip`.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
```
|
||||
|
||||
## web_client_ip_source (server.listeners)
|
||||
- **Constraints / validation**: Only `"x_forwarded_for"` is supported by the initial WEB implementation.
|
||||
- **Description**: Chooses the L7 source of the original client IP. Telemt accepts exactly one canonical `X-Forwarded-For` address and only when the direct TCP peer belongs to `web_trusted_proxy_cidrs`.
|
||||
|
||||
## web_trusted_proxy_cidrs (server.listeners)
|
||||
- **Constraints / validation**: WEB-only non-empty CIDR array. A `/0` network is rejected. It is invalid on an MTProxy listener.
|
||||
- **Description**: Trust boundary for the immediate NGINX or HAProxy peer. List only addresses that can connect directly to this listener; never expose the plain listener to an untrusted network.
|
||||
|
||||
|
||||
# [web]
|
||||
|
||||
WEB mode carries Telegram Desktop MTProxy traffic through HTTPS terminated by an external NGINX or HAProxy. Telemt receives plain HTTP/1.1 on a private `transport = "web"` listener. See the [complete WEB deployment guide](../WEB/WEB_PROXY.en.md) before enabling this mode.
|
||||
|
||||
| Key | Type | Default | Hot-Reload |
|
||||
| --- | --- | --- | --- |
|
||||
| `enabled` | `bool` | `false` | `✔` |
|
||||
| `limits` | table | bounded defaults | `✘` |
|
||||
| `timeouts` | table | bounded defaults | `✔` |
|
||||
| `vhosts` | array of tables | `[]` | `✔` |
|
||||
|
||||
`enabled = true` requires at least one network-eligible WEB listener, at least one vhost, and at least one profile in every vhost. Disabling WEB stops issuance of new bridge and session credentials after reload; use the users API to revoke one user's active sessions.
|
||||
|
||||
# [web.limits]
|
||||
|
||||
These process-wide ceilings make every WEB registry, queue, request body, static snapshot, and admission path bounded. All values are validated together. Per-owner limits cannot exceed global limits, queue reserves must preserve control-frame progress, body reservations must fit their global budget, and all declared byte ceilings must fit `memory_envelope_bytes`. Changing any value in this table requires a process restart.
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `max_header_bytes` | `usize` | `16384` | Maximum bytes in one HTTP request head. |
|
||||
| `max_body_bytes` | `usize` | `2097152` | Maximum collected carrier request body. |
|
||||
| `max_frame_payload_bytes` | `usize` | `1048576` | Maximum payload in one WEB frame. |
|
||||
| `carrier_batch_bytes` | `usize` | `2097152` | Maximum encoded downlink batch. |
|
||||
| `max_frames_per_body` | `usize` | `4096` | Maximum frames parsed or emitted per carrier body. |
|
||||
| `max_http_connections` | `usize` | `1024` | Accepted WEB HTTP connections process-wide. |
|
||||
| `max_http_handlers` | `usize` | `512` | Concurrent HTTP handlers process-wide. |
|
||||
| `max_body_readers` | `usize` | `32` | Concurrent collected request bodies process-wide. |
|
||||
| `max_body_bytes_global` | `usize` | `67108864` | Global byte reservation for collected bodies. |
|
||||
| `max_sessions_global` | `usize` | `128` | Live WEB sessions process-wide. |
|
||||
| `max_sessions_per_ip` | `usize` | `16` | Live sessions for one forwarded client IP. |
|
||||
| `max_streams_per_session` | `usize` | `128` | Default live logical streams per session. |
|
||||
| `max_streams_global` | `usize` | `4096` | Live logical streams process-wide. |
|
||||
| `max_stream_handshakes` | `usize` | `256` | Concurrent inner MTProxy handshakes. |
|
||||
| `max_tombstones_per_session` | `usize` | `4096` | Closed stream IDs retained per session. |
|
||||
| `pending_bytes_per_session` | `usize` | `33554432` | Queued data and control bytes per session. |
|
||||
| `pending_bytes_global` | `usize` | `536870912` | Queued data and control bytes process-wide. |
|
||||
| `pending_items_per_session` | `usize` | `16384` | Queued data and control items per session. |
|
||||
| `pending_items_global` | `usize` | `262144` | Queued data and control items process-wide. |
|
||||
| `control_bytes_per_session` | `usize` | `262144` | Per-session byte reserve available only to control frames. |
|
||||
| `control_bytes_global` | `usize` | `16777216` | Process-wide byte reserve available only to control frames. |
|
||||
| `max_bootstraps_global` | `usize` | `512` | Live bootstrap credentials process-wide. |
|
||||
| `max_bootstraps_per_ip` | `usize` | `64` | Live bootstrap credentials per client IP. |
|
||||
| `max_vhosts` | `usize` | `8` | Configured WEB virtual hosts. |
|
||||
| `max_profiles` | `usize` | `32` | WEB profiles across all vhosts. |
|
||||
| `max_static_files` | `usize` | `4096` | Static snapshot entries across all vhosts. |
|
||||
| `max_static_file_bytes` | `usize` | `8388608` | Maximum bytes in one static file. |
|
||||
| `max_static_bytes` | `usize` | `67108864` | Static snapshot bytes across all vhosts. |
|
||||
| `memory_envelope_bytes` | `usize` | `805306368` | Declared envelope for HTTP heads, bodies, queues, and static snapshots; maximum 4 GiB. |
|
||||
| `new_bootstraps_per_minute` | `u32` | `1200` | Sustained process-wide bootstrap issuance rate. |
|
||||
| `new_bootstraps_burst` | `u32` | `256` | Process-wide bootstrap issuance burst. |
|
||||
| `new_sessions_per_minute` | `u32` | `600` | Sustained process-wide session creation rate. |
|
||||
| `new_sessions_burst` | `u32` | `128` | Process-wide session creation burst. |
|
||||
| `new_streams_per_minute` | `u32` | `6000` | Sustained logical-stream creation rate. |
|
||||
| `new_streams_burst` | `u32` | `512` | Process-wide logical-stream creation burst. |
|
||||
|
||||
# [web.timeouts]
|
||||
|
||||
Every timeout is measured in seconds and must be within `1..=3600`. The longest request deadline must be lower than `http_idle_secs`.
|
||||
|
||||
| Key | Type | Default | Hot-Reload | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `header_secs` | `u64` | `10` | `✔` | Receive one complete HTTP request head. |
|
||||
| `body_secs` | `u64` | `30` | `✔` | Collect one authenticated carrier body. |
|
||||
| `stream_handshake_secs` | `u64` | `10` | `✔` | Complete one inner MTProxy handshake. |
|
||||
| `long_poll_secs` | `u64` | `25` | `✔` | Maximum empty downlink long poll. |
|
||||
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Unused bootstrap and closed-token replay lifetime. |
|
||||
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Maximum carrier inactivity before session closure. |
|
||||
| `http_idle_secs` | `u64` | `75` | `✔` | WEB HTTP keep-alive idle lifetime. |
|
||||
| `shutdown_secs` | `u64` | `15` | `✔` | Graceful WEB shutdown deadline. |
|
||||
| `decoy_header_secs` | `u64` | `30` | `✔` | Connect and response-head deadline for an HTTP decoy. |
|
||||
|
||||
# [[web.vhosts]]
|
||||
|
||||
| Key | Type | Required | Hot-Reload | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `host` | `String` | yes | `✔` | Unique, canonical lowercase ACE FQDN without port, path, credentials, or trailing dot. |
|
||||
| `public_addr` | `SocketAddr` | yes | `✔` | Concrete public IP on port `443`; used in the inner relay destination tuple. |
|
||||
| `decoy` | table | yes | `✔` | Ordinary-site fallback for unauthenticated or invalid traffic. |
|
||||
| `profiles` | array of tables | when enabled | `✔` | Explicit users and client secret modes exposed by this hostname. |
|
||||
|
||||
The forwarded client address and `public_addr` must use the same IP family. The hostname must be accepted by Telegram Desktop and is normalized during validation.
|
||||
|
||||
# [web.vhosts.decoy]
|
||||
|
||||
Exactly one decoy mode is required:
|
||||
|
||||
| Mode | Required keys | Validation |
|
||||
| --- | --- | --- |
|
||||
| `http_upstream` | `upstream` | An `http://` origin using a loopback, link-local, or private IP literal; no credentials, path, query, or fragment. |
|
||||
| `static_directory` | `directory`; optional `index = "index.html"` | Absolute real directory and one safe index file name. Symlinks and escaping paths are rejected; the immutable snapshot is loaded under `[web.limits]`. |
|
||||
|
||||
# [[web.vhosts.profiles]]
|
||||
|
||||
| Key | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `user` | `String` | yes | — | Existing key from `[access.users]`. |
|
||||
| `secret_mode` | `"plain"` or `"dd"` | yes | — | Exact Telegram Desktop secret representation. `ee` is not supported. |
|
||||
| `max_sessions` | `usize` | no | `web.limits.max_sessions_global` | Live sessions for this profile. |
|
||||
| `max_streams` | `usize` | no | `web.limits.max_streams_global` | Live logical streams for this profile. |
|
||||
| `max_streams_per_session` | `usize` | no | `web.limits.max_streams_per_session` | Live logical streams in one profile session. |
|
||||
|
||||
Profile limits must be non-zero and no greater than their corresponding global limits. Duplicate `(user, secret_mode)` profiles in one vhost are rejected.
|
||||
|
||||
## WEB lifecycle and API management
|
||||
|
||||
- The config watcher and generation reload apply `web.enabled`, `web.timeouts`, vhosts, profiles, and decoy snapshots without a process restart. Existing sessions keep their acquisition-time limits and deadlines; new work uses the active generation.
|
||||
- WEB listener inventory and trust policy under `server.listeners`, and every `web.limits` value, are process-owned and restart-required.
|
||||
- There is no dedicated `/v1/web` endpoint. `GET /v1/config` omits `[web]`, and `PATCH /v1/config` rejects a `web` key with `400 section_not_editable`.
|
||||
- To manage WEB policy remotely, update the owned TOML file and call `POST /v1/system/reload`; inspect `GET /v1/system/reload/{id}` and its `deferred_process_fields`. Restart Telemt when it contains `server.listeners` or `web.limits`.
|
||||
- Existing access users can be created, changed, rotated, enabled, disabled, and deleted through `/v1/users`. Creating a user does not add a WEB profile. Disabling a user immediately updates admission and cancels that user's active sessions.
|
||||
- `PATCH /v1/config` can persist `server.listeners`, including WEB listener fields, but a changed WEB listener does not become active until process restart.
|
||||
|
||||
|
||||
# [timeouts]
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
- [server.conntrack_control](#serverconntrack_control)
|
||||
- [server.api](#serverapi)
|
||||
- [server.listeners](#serverlisteners)
|
||||
- [web](#web)
|
||||
- [web.limits](#weblimits)
|
||||
- [web.timeouts](#webtimeouts)
|
||||
- [web.vhosts](#webvhosts)
|
||||
- [web.vhosts.decoy](#webvhostsdecoy)
|
||||
- [web.vhosts.profiles](#webvhostsprofiles)
|
||||
- [timeouts](#timeouts)
|
||||
- [censorship](#censorship)
|
||||
- [censorship.tls_fetch](#censorshiptls_fetch)
|
||||
@@ -2250,6 +2256,9 @@
|
||||
| [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` |
|
||||
| [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` |
|
||||
| [`reuse_allow`](#reuse_allow) | `bool` | `false` | `✘` |
|
||||
| [`transport`](#transport-serverlisteners) | `"mtproxy"` или `"web"` | `"mtproxy"` | `✘` |
|
||||
| [`web_client_ip_source`](#web_client_ip_source-serverlisteners) | `"x_forwarded_for"` | `"x_forwarded_for"` | `✘` |
|
||||
| [`web_trusted_proxy_cidrs`](#web_trusted_proxy_cidrs-serverlisteners) | `IpNetwork[]` | `[]` | `✘` |
|
||||
|
||||
## ip
|
||||
- **Ограничения / валидация**: Обязательный параметр. Значение должно содержать IP-адрес в формате строки.
|
||||
@@ -2443,6 +2452,141 @@
|
||||
reuse_allow = false
|
||||
```
|
||||
|
||||
## transport (server.listeners)
|
||||
- **Ограничения / валидация**: `"mtproxy"` или `"web"`.
|
||||
- **Описание**: Выбирает протокол listener’а. WEB-listener принимает обычный HTTP/1.1 от доверенного TLS-терминатора и требует перезапуска процесса. Для него обязательны `proxy_protocol = false` и `reuse_allow = false`; параметры `client_mss`, `synlimit`, `announce` и `announce_ip` запрещены.
|
||||
- **Пример**:
|
||||
|
||||
```toml
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
```
|
||||
|
||||
## web_client_ip_source (server.listeners)
|
||||
- **Ограничения / валидация**: Первая реализация WEB поддерживает только `"x_forwarded_for"`.
|
||||
- **Описание**: Выбирает L7-источник исходного IP клиента. Telemt принимает ровно один канонический адрес `X-Forwarded-For`, только если прямой TCP peer входит в `web_trusted_proxy_cidrs`.
|
||||
|
||||
## web_trusted_proxy_cidrs (server.listeners)
|
||||
- **Ограничения / валидация**: Непустой массив CIDR только для WEB. Сеть `/0` запрещена. Параметр недопустим для MTProxy-listener’а.
|
||||
- **Описание**: Граница доверия для непосредственного NGINX или HAProxy. Указывайте только адреса, которые могут напрямую подключаться к этому listener’у; не публикуйте plain HTTP listener в недоверенной сети.
|
||||
|
||||
|
||||
# [web]
|
||||
|
||||
WEB-режим переносит MTProxy-трафик Telegram Desktop внутри HTTPS, который терминирует внешний NGINX или HAProxy. Telemt принимает обычный HTTP/1.1 на приватном listener’е с `transport = "web"`. Перед включением режима прочитайте [полное руководство по развёртыванию WEB](../WEB/WEB_PROXY.ru.md).
|
||||
|
||||
| Ключ | Тип | По умолчанию | Hot-Reload |
|
||||
| --- | --- | --- | --- |
|
||||
| `enabled` | `bool` | `false` | `✔` |
|
||||
| `limits` | таблица | ограниченные defaults | `✘` |
|
||||
| `timeouts` | таблица | ограниченные defaults | `✔` |
|
||||
| `vhosts` | массив таблиц | `[]` | `✔` |
|
||||
|
||||
Для `enabled = true` нужен как минимум один доступный по сетевой политике WEB-listener, один vhost и один профиль в каждом vhost. Отключение WEB после reload прекращает выдачу новых bridge- и session-credentials; для отзыва активных сессий отдельного пользователя используйте users API.
|
||||
|
||||
# [web.limits]
|
||||
|
||||
Эти process-wide границы ограничивают все WEB-реестры, очереди, тела запросов, статические snapshots и admission-пути. Значения проверяются совместно: per-owner лимиты не могут превышать глобальные, резервы очередей должны сохранять прогресс control frames, body-резервы должны помещаться в общий бюджет, а все заявленные байтовые границы — в `memory_envelope_bytes`. Изменение любого значения этой таблицы требует перезапуска процесса.
|
||||
|
||||
| Ключ | Тип | По умолчанию | Описание |
|
||||
| --- | --- | --- | --- |
|
||||
| `max_header_bytes` | `usize` | `16384` | Максимальный размер заголовка одного HTTP-запроса. |
|
||||
| `max_body_bytes` | `usize` | `2097152` | Максимальный размер собранного carrier body. |
|
||||
| `max_frame_payload_bytes` | `usize` | `1048576` | Максимальный payload одного WEB frame. |
|
||||
| `carrier_batch_bytes` | `usize` | `2097152` | Максимальный закодированный downlink batch. |
|
||||
| `max_frames_per_body` | `usize` | `4096` | Максимальное число frames в одном carrier body. |
|
||||
| `max_http_connections` | `usize` | `1024` | Принятые WEB HTTP connections на весь процесс. |
|
||||
| `max_http_handlers` | `usize` | `512` | Одновременно выполняемые HTTP handlers на весь процесс. |
|
||||
| `max_body_readers` | `usize` | `32` | Одновременно собираемые request bodies на весь процесс. |
|
||||
| `max_body_bytes_global` | `usize` | `67108864` | Глобальный байтовый резерв для собранных bodies. |
|
||||
| `max_sessions_global` | `usize` | `128` | Активные WEB-сессии на весь процесс. |
|
||||
| `max_sessions_per_ip` | `usize` | `16` | Активные сессии одного forwarded client IP. |
|
||||
| `max_streams_per_session` | `usize` | `128` | Default активных logical streams на сессию. |
|
||||
| `max_streams_global` | `usize` | `4096` | Активные logical streams на весь процесс. |
|
||||
| `max_stream_handshakes` | `usize` | `256` | Одновременные внутренние MTProxy handshakes. |
|
||||
| `max_tombstones_per_session` | `usize` | `4096` | Закрытые stream IDs, сохраняемые одной сессией. |
|
||||
| `pending_bytes_per_session` | `usize` | `33554432` | Байты данных и управления в очередях одной сессии. |
|
||||
| `pending_bytes_global` | `usize` | `536870912` | Байты данных и управления в очередях всего процесса. |
|
||||
| `pending_items_per_session` | `usize` | `16384` | Элементы данных и управления в очередях одной сессии. |
|
||||
| `pending_items_global` | `usize` | `262144` | Элементы данных и управления в очередях всего процесса. |
|
||||
| `control_bytes_per_session` | `usize` | `262144` | Резерв одной сессии только для control frames. |
|
||||
| `control_bytes_global` | `usize` | `16777216` | Process-wide резерв только для control frames. |
|
||||
| `max_bootstraps_global` | `usize` | `512` | Активные bootstrap credentials на весь процесс. |
|
||||
| `max_bootstraps_per_ip` | `usize` | `64` | Активные bootstrap credentials на один client IP. |
|
||||
| `max_vhosts` | `usize` | `8` | Настроенные WEB virtual hosts. |
|
||||
| `max_profiles` | `usize` | `32` | WEB-профили всех vhosts. |
|
||||
| `max_static_files` | `usize` | `4096` | Элементы static snapshot всех vhosts. |
|
||||
| `max_static_file_bytes` | `usize` | `8388608` | Максимальный размер одного статического файла. |
|
||||
| `max_static_bytes` | `usize` | `67108864` | Размер static snapshots всех vhosts. |
|
||||
| `memory_envelope_bytes` | `usize` | `805306368` | Заявленный envelope для HTTP heads, bodies, очередей и static snapshots; максимум 4 GiB. |
|
||||
| `new_bootstraps_per_minute` | `u32` | `1200` | Устойчивая process-wide скорость выдачи bootstrap. |
|
||||
| `new_bootstraps_burst` | `u32` | `256` | Process-wide burst выдачи bootstrap. |
|
||||
| `new_sessions_per_minute` | `u32` | `600` | Устойчивая process-wide скорость создания сессий. |
|
||||
| `new_sessions_burst` | `u32` | `128` | Process-wide burst создания сессий. |
|
||||
| `new_streams_per_minute` | `u32` | `6000` | Устойчивая скорость создания logical streams. |
|
||||
| `new_streams_burst` | `u32` | `512` | Process-wide burst создания logical streams. |
|
||||
|
||||
# [web.timeouts]
|
||||
|
||||
Все таймауты задаются в секундах и должны входить в диапазон `1..=3600`. Самый длинный request deadline должен быть меньше `http_idle_secs`.
|
||||
|
||||
| Ключ | Тип | По умолчанию | Hot-Reload | Описание |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `header_secs` | `u64` | `10` | `✔` | Получение полного заголовка HTTP-запроса. |
|
||||
| `body_secs` | `u64` | `30` | `✔` | Сбор одного аутентифицированного carrier body. |
|
||||
| `stream_handshake_secs` | `u64` | `10` | `✔` | Выполнение внутреннего MTProxy handshake. |
|
||||
| `long_poll_secs` | `u64` | `25` | `✔` | Максимальная длительность пустого downlink long poll. |
|
||||
| `bootstrap_lifetime_secs` | `u64` | `120` | `✔` | Срок неиспользованного bootstrap и replay-marker закрытого token. |
|
||||
| `reconnect_grace_secs` | `u64` | `120` | `✔` | Максимальная неактивность carrier до закрытия сессии. |
|
||||
| `http_idle_secs` | `u64` | `75` | `✔` | Idle lifetime WEB HTTP keep-alive connection. |
|
||||
| `shutdown_secs` | `u64` | `15` | `✔` | Deadline корректного завершения WEB. |
|
||||
| `decoy_header_secs` | `u64` | `30` | `✔` | Deadline подключения и получения response head от HTTP decoy. |
|
||||
|
||||
# [[web.vhosts]]
|
||||
|
||||
| Ключ | Тип | Обязательный | Hot-Reload | Описание |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `host` | `String` | да | `✔` | Уникальный канонический lowercase ACE FQDN без порта, пути, credentials и завершающей точки. |
|
||||
| `public_addr` | `SocketAddr` | да | `✔` | Конкретный публичный IP на порту `443`, используемый во внутреннем destination tuple relay. |
|
||||
| `decoy` | таблица | да | `✔` | Обычный сайт для неаутентифицированного или некорректного трафика. |
|
||||
| `profiles` | массив таблиц | при включённом WEB | `✔` | Явные пользователи и client secret modes для этого hostname. |
|
||||
|
||||
Forwarded client address и `public_addr` должны относиться к одному семейству IP. Hostname нормализуется при валидации и должен приниматься Telegram Desktop.
|
||||
|
||||
# [web.vhosts.decoy]
|
||||
|
||||
Обязателен ровно один decoy mode:
|
||||
|
||||
| Mode | Обязательные ключи | Валидация |
|
||||
| --- | --- | --- |
|
||||
| `http_upstream` | `upstream` | `http://` origin с loopback, link-local или private IP literal; без credentials, path, query и fragment. |
|
||||
| `static_directory` | `directory`; необязательный `index = "index.html"` | Абсолютный реальный каталог и одно безопасное имя index-файла. Symlinks и выход за пределы каталога запрещены; immutable snapshot загружается в пределах `[web.limits]`. |
|
||||
|
||||
# [[web.vhosts.profiles]]
|
||||
|
||||
| Ключ | Тип | Обязательный | По умолчанию | Описание |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `user` | `String` | да | — | Существующий ключ из `[access.users]`. |
|
||||
| `secret_mode` | `"plain"` или `"dd"` | да | — | Точное представление секрета для Telegram Desktop. `ee` не поддерживается. |
|
||||
| `max_sessions` | `usize` | нет | `web.limits.max_sessions_global` | Активные сессии этого профиля. |
|
||||
| `max_streams` | `usize` | нет | `web.limits.max_streams_global` | Активные logical streams этого профиля. |
|
||||
| `max_streams_per_session` | `usize` | нет | `web.limits.max_streams_per_session` | Активные logical streams в одной сессии профиля. |
|
||||
|
||||
Лимиты профиля должны быть ненулевыми и не превышать соответствующие глобальные границы. Повторяющиеся профили `(user, secret_mode)` в одном vhost запрещены.
|
||||
|
||||
## Lifecycle WEB и управление через API
|
||||
|
||||
- Config watcher и generation reload применяют `web.enabled`, `web.timeouts`, vhosts, profiles и decoy snapshots без перезапуска процесса. Существующие сессии сохраняют лимиты и deadlines своего момента создания; новая работа использует активное поколение.
|
||||
- Состав WEB-listeners и их trust policy в `server.listeners`, а также все значения `web.limits` принадлежат процессу и требуют перезапуска.
|
||||
- Отдельного endpoint `/v1/web` нет. `GET /v1/config` не возвращает `[web]`, а `PATCH /v1/config` отклоняет ключ `web` с `400 section_not_editable`.
|
||||
- Для удалённого применения WEB policy измените соответствующий TOML-файл и вызовите `POST /v1/system/reload`; проверьте `GET /v1/system/reload/{id}` и поле `deferred_process_fields`. Если оно содержит `server.listeners` или `web.limits`, перезапустите Telemt.
|
||||
- Существующих access users можно создавать, изменять, ротировать, включать, выключать и удалять через `/v1/users`. Создание пользователя не добавляет WEB-профиль. Отключение пользователя немедленно обновляет admission и завершает его активные сессии.
|
||||
- `PATCH /v1/config` может сохранить `server.listeners`, включая поля WEB-listener’а, но изменённый WEB-listener активируется только после перезапуска процесса.
|
||||
|
||||
|
||||
# [timeouts]
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
# WEB-Proxy-Modus
|
||||
|
||||
[English](WEB_PROXY.en.md) | [Русский](WEB_PROXY.ru.md) | [Deutsch](WEB_PROXY.de.md)
|
||||
|
||||
Der WEB-Modus transportiert gewöhnliche MTProxy-Streams über einen begrenzten HTTPS-Long-Poll-Transport, der mit dem Proxy-Typ `WEB` von Telegram Desktop kompatibel ist. In der ersten Implementierung terminiert Telemt TLS nicht selbst: NGINX oder HAProxy verwaltet das öffentliche Zertifikat und leitet unverschlüsseltes HTTP/1.1 an einen privaten Telemt-Listener weiter.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> Der WEB-Modus ist im aktuellen Quellcode implementiert und konfigurierbar. Für die erste Bereitstellung sind ein Binary aus einer Revision mit dieser Implementierung und ein Neustart des Telemt-Prozesses erforderlich. Veröffentlichte Pakete dürfen erst verwendet werden, nachdem geprüft wurde, dass sie dieselbe Revision enthalten. Die Ende-zu-Ende-Prüfung mit dem vorgesehenen Telegram-Desktop-Build und dem realen öffentlichen TLS-Endpunkt bleibt ein Abnahmeschritt des Betreibers.
|
||||
|
||||
## Datenpfad
|
||||
|
||||
```text
|
||||
Telegram Desktop
|
||||
| HTTPS :443
|
||||
v
|
||||
NGINX oder HAProxy (TLS-Terminierung, kanonische Werte für Host und X-Forwarded-For)
|
||||
| unverschlüsseltes HTTP/1.1 in einem privaten Netz
|
||||
v
|
||||
Telemt-WEB-Listener
|
||||
|-- authentifizierter Carrier --> begrenzte logische MTProxy-Relays --> Telegram
|
||||
`-- gewöhnlicher oder ungültiger Request --> konfigurierte Decoy-Site
|
||||
```
|
||||
|
||||
Leiten Sie den vollständigen öffentlichen vhost an Telemt weiter. Wenn der TLS-Terminator nur bekannte Carrier-Pfade trennt, unterscheiden sich gewöhnliches und authentifiziertes Verhalten beobachtbar und Telemt kann seine Decoy-Richtlinie nicht durchsetzen.
|
||||
|
||||
## Unterstützter Client-Vertrag
|
||||
|
||||
- Der öffentliche Endpunkt ist immer `https://HOST:443`.
|
||||
- Unterstützt werden 16-Byte-MTProxy-Secrets in den Modi `plain` und `dd`. FakeTLS-Secrets mit `ee` werden im WEB-Modus nicht unterstützt.
|
||||
- Der erste Carrier verwendet serialisierte HTTPS-Uplink-Requests und HTTPS-Long-Polling. WebSocket- und Lane-Carrier werden nicht angeboten.
|
||||
- Capability-, Bootstrap- und Session-Zugangsdaten sind getrennte Werte mit begrenzter Lebensdauer. Carrier-Zugangsdaten sind geheim und dürfen nicht in Access-Logs erscheinen.
|
||||
- Die innere MTProxy-Authentifizierung ist auf den Benutzer und Secret-Modus des vhost-Profils beschränkt. Ein ungültiger innerer Handshake schließt nur seinen logischen Stream und gelangt niemals in den TCP-Masking-Pfad.
|
||||
|
||||
Telegram-Desktop-WEB-Links enthalten keinen Port, da der Client Port 443 voraussetzt:
|
||||
|
||||
```text
|
||||
tg://webproxy?server=proxy.example.com&secret=0123456789abcdef0123456789abcdef
|
||||
tg://webproxy?server=proxy.example.com&secret=dd0123456789abcdef0123456789abcdef
|
||||
```
|
||||
|
||||
Telemt gibt Links für die durch `[general.links].show` ausgewählten WEB-Profile über das vorhandene Log-Target `telemt::links` aus.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Ein eigener öffentlicher FQDN und ein gültiges TLS-Zertifikat auf NGINX oder HAProxy.
|
||||
- Eine stabile öffentliche IP für diesen Hostnamen. `public_addr` muss genau diese konkrete IP auf Port 443 enthalten, da die Adresse Teil des Ziel-Tupels des inneren Relays ist.
|
||||
- Ein privater oder lokaler HTTP-Pfad vom TLS-Terminator zu Telemt.
|
||||
- Eine gewöhnliche Decoy-Site als privater HTTP-Origin oder unveränderlicher Snapshot eines lokalen Verzeichnisses.
|
||||
- Ein kompatibler Telegram-Desktop-Build mit dem Proxy-Typ `WEB`.
|
||||
|
||||
Wenn ein Hostname sowohl über IPv4 als auch IPv6 bedient wird, verwenden Sie in dieser ersten Implementierung getrennte Hostnamen oder Telemt-Instanzen. Die weitergeleitete Client-Adresse und `public_addr` müssen dieselbe IP-Familie verwenden.
|
||||
|
||||
## Minimale Telemt-Konfiguration
|
||||
|
||||
Das Beispiel bindet den WEB-Listener an Loopback und verwendet einen privaten HTTP-Decoy-Origin:
|
||||
|
||||
```toml
|
||||
[general.links]
|
||||
show = ["web-user"]
|
||||
|
||||
[access.users]
|
||||
web-user = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_client_ip_source = "x_forwarded_for"
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
|
||||
[web]
|
||||
enabled = true
|
||||
|
||||
[[web.vhosts]]
|
||||
host = "proxy.example.com"
|
||||
public_addr = "203.0.113.10:443"
|
||||
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://127.0.0.1:18081"
|
||||
|
||||
[[web.vhosts.profiles]]
|
||||
user = "web-user"
|
||||
secret_mode = "dd"
|
||||
max_sessions = 8
|
||||
max_streams = 512
|
||||
max_streams_per_session = 64
|
||||
```
|
||||
|
||||
Der WEB-Listener muss `proxy_protocol = false` und `reuse_allow = false` verwenden. `client_mss`, `synlimit`, `announce` und `announce_ip` sind nicht zulässig. `web_trusted_proxy_cidrs` muss nicht leer sein und darf nur die unmittelbar vorgeschalteten NGINX- oder HAProxy-Peers enthalten; `/0`-Netze werden abgelehnt.
|
||||
|
||||
Der HTTP-Decoy-Origin muss eine Loopback-, Link-Local- oder private IP-Adresse als Literal verwenden. Telemt bewahrt bei gewöhnlichen Requests Methode, Pfad, Query, Header, gestreamten Body, Response-Status, Header und Body und entfernt Hop-by-Hop-Header. Vor dem Fallback auf den Decoy entfernt Telemt Carrier-Zugangsdaten und Bodys aus fehlerhaften Carrier-Requests.
|
||||
|
||||
Alternativ kann ein unveränderlicher Snapshot einer statischen Site verwendet werden:
|
||||
|
||||
```toml
|
||||
[web.vhosts.decoy]
|
||||
mode = "static_directory"
|
||||
directory = "/var/lib/telemt/public"
|
||||
index = "index.html"
|
||||
```
|
||||
|
||||
Statische Dateien werden beim Start und bei einem erfolgreichen Konfigurations-Reload gelesen. Eintragszahl, Dateigröße und Gesamtgröße des Snapshots werden durch `[web.limits]` begrenzt. Symlinks und Pfade außerhalb des konfigurierten Verzeichnisses werden abgelehnt. Ändern Sie das Verzeichnis nicht gleichzeitig, während Telemt einen Snapshot erstellt.
|
||||
|
||||
Alle WEB-Schlüssel und Defaults sind in der [Konfigurationsreferenz](../Config_params/CONFIG_PARAMS.de.md#web) aufgeführt.
|
||||
|
||||
## TLS-Terminierung mit NGINX
|
||||
|
||||
```nginx
|
||||
upstream telemt_web {
|
||||
server 127.0.0.1:18080;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name proxy.example.com;
|
||||
access_log off;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;
|
||||
|
||||
client_max_body_size 2m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://telemt_web;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 35s;
|
||||
proxy_read_timeout 35s;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_next_upstream off;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`client_max_body_size` muss mindestens `web.limits.max_body_bytes` entsprechen. `proxy_read_timeout` und `proxy_send_timeout` müssen größer als `web.timeouts.long_poll_secs` sein, dessen Default 25 Sekunden beträgt. Überschreiben Sie `X-Forwarded-For`, statt einen Wert anzuhängen. Aktivieren Sie keine Upstream-Wiederholungen: Der Bridge-Transport führt byte-identische Wiederholungen über sein eigenes Sequenzprotokoll aus.
|
||||
|
||||
## TLS-Terminierung mit HAProxy
|
||||
|
||||
```haproxy
|
||||
frontend public_https
|
||||
mode http
|
||||
no log
|
||||
bind :443 ssl crt /etc/haproxy/certs/proxy.example.com.pem alpn h2,http/1.1
|
||||
acl telemt_web_host hdr(host) -i proxy.example.com proxy.example.com:443
|
||||
use_backend telemt_web if telemt_web_host
|
||||
|
||||
backend telemt_web
|
||||
mode http
|
||||
option http-keep-alive
|
||||
retries 0
|
||||
timeout connect 5s
|
||||
timeout server 35s
|
||||
http-request set-header Host proxy.example.com
|
||||
http-request del-header X-Forwarded-For
|
||||
http-request set-header X-Forwarded-For %[src]
|
||||
server telemt_web_1 127.0.0.1:18080 check
|
||||
```
|
||||
|
||||
Im Frontend oder im Abschnitt `defaults` muss auch `timeout client` oberhalb der Long-Poll-Deadline liegen. Pfad, Raw Query, Body sowie die Carrier-Header `Authorization`, `Content-Type`, `X-Up-Seq` und `X-Down-Cursor` dürfen nicht umgeschrieben werden.
|
||||
|
||||
## Lebenszyklus und Reload-Verhalten
|
||||
|
||||
| Konfiguration | Runtime-Verhalten |
|
||||
| --- | --- |
|
||||
| Bestand der WEB-Listener, Bind-Adresse und Vertrauensrichtlinie | Prozesseigen; Telemt neu starten. |
|
||||
| Jeder Wert in `[web.limits]` | Prozesseigener Speicher- und Ressourcenvertrag; Telemt neu starten. |
|
||||
| `web.enabled`, Timeouts, vhosts, Profile und Decoys | Werden vom Config-Watcher oder durch einen Runtime-Generations-Reload angewendet. |
|
||||
| Bestehende HTTP-Verbindungen und WEB-Sitzungen | Behalten die bei ihrer Erstellung übernommenen Grenzen und Deadlines; neue logische Streams verwenden die aktive Runtime-Generation. |
|
||||
| Beenden des Prozesses | Verwendet den zuletzt geladenen Wert von `web.timeouts.shutdown_secs`. |
|
||||
|
||||
Jeder logische Stream behält die Client-IP seiner Sitzung und besitzt während der gesamten Relay-Lebensdauer einen prozessweit eindeutigen, von null verschiedenen synthetischen Quellport. Damit bleibt für Direct- und Middle-End-KDF-Routing ein stabiles, kollisionsfreies Quell-/Ziel-Tupel erhalten.
|
||||
|
||||
## Verwaltung über die API
|
||||
|
||||
API-Verwaltung ist verfügbar, aber absichtlich eingeschränkt. Es gibt weder einen eigenen Endpunkt `/v1/web` noch einen WEB-spezifischen Runtime-Statistik-Endpunkt.
|
||||
|
||||
| Operation | API-Unterstützung |
|
||||
| --- | --- |
|
||||
| `[web]`, vhosts, Profile, Decoys, Timeouts oder Limits lesen oder ändern | Nein. `GET /v1/config` lässt `[web]` aus; `PATCH /v1/config` antwortet für `web` mit `400 section_not_editable`. |
|
||||
| `server.listeners` speichern | Ja, über `PATCH /v1/config`; ein geänderter WEB-Listener bleibt jedoch bis zum Prozessneustart zurückgestellt. |
|
||||
| Außerhalb der API geänderte WEB-Konfiguration anwenden | Ja, über `POST /v1/system/reload` und anschließende Abfrage des Vorgangsstatus. |
|
||||
| `[access.users]` verwalten | Ja, über `/v1/users`. Das Erstellen eines Benutzers erzeugt kein WEB-Profil. |
|
||||
| Einen Benutzer widerrufen | Ja. `/v1/users/{username}/disable` aktualisiert die Admission sofort und beendet die aktiven Sitzungen dieses Benutzers. |
|
||||
|
||||
Binden Sie die API an Loopback, halten Sie die Whitelist direkter Peers eng, konfigurieren Sie einen exakten Authorization-Header und verwenden Sie `read_only = false` nur dort, wo Mutationen erforderlich sind:
|
||||
|
||||
```toml
|
||||
[server.api]
|
||||
enabled = true
|
||||
listen = "127.0.0.1:9091"
|
||||
whitelist = ["127.0.0.0/8"]
|
||||
auth_header = "Bearer replace-with-a-random-control-token"
|
||||
read_only = false
|
||||
```
|
||||
|
||||
Die API-Whitelist prüft den direkten TCP-Peer und vertraut `X-Forwarded-For` nicht. Änderungen an `[server.api]` selbst erfordern einen Prozessneustart.
|
||||
|
||||
Nachdem ein Administrator oder Konfigurationssystem die TOML-Datei atomar aktualisiert hat, setzen Sie `TELEMT_API_AUTH` auf den exakten Wert von `auth_header` und starten Sie einen beobachtbaren Generations-Reload:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/system/reload \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"mode":"drain","timeout_secs":30,"failure_policy":"rollback"}'
|
||||
|
||||
# Use data.reload_id from the response.
|
||||
curl -sS http://127.0.0.1:9091/v1/system/reload/RELOAD_ID \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
```
|
||||
|
||||
Der terminale Status `succeeded` bestätigt die Runtime-Aktivierung. Enthält `deferred_process_fields` den Wert `server.listeners` oder `web.limits`, ist die Datei gültig und gespeichert, diese Einstellungen erfordern aber weiterhin einen Telemt-Neustart.
|
||||
|
||||
Operationen für Access-Benutzer verwenden die vorhandenen Endpunkte, zum Beispiel:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/disable \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/rotate-secret \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
Nach einer Secret-Rotation erstellt der Config-Watcher die WEB-Capabilities neu. Die Users-API liefert das Secret, aber keine `tg://webproxy`-URL. Erstellen Sie den Link mit dem konfigurierten Hostnamen und der `plain`- oder `dd`-Darstellung des Profils. Entfernen und aktivieren Sie vor dem Löschen eines Benutzers zuerst das WEB-Profil, das auf ihn verweist, damit die resultierende Konfiguration gültig bleibt.
|
||||
|
||||
Der vollständige Vertrag für Requests, Revisionen, Fehler und alle Benutzer-Endpunkte steht in der [Dokumentation der Control API](../Architecture/API/API.md).
|
||||
|
||||
## Bereitstellungsinvarianten
|
||||
|
||||
- Veröffentlichen Sie den unverschlüsselten HTTP-WEB-Listener niemals in einem nicht vertrauenswürdigen Netz. Erzwingen Sie diese Einschränkung auch bei einer Loopback-Bindung mit Host-Firewall-Regeln.
|
||||
- Deaktivieren Sie am TLS-Terminator die Protokollierung von Request-Target und Authorization oder verwenden Sie ein geprüftes, redigiertes Format. Raw Queries enthalten Bridge-Capabilities und `Authorization` enthält Bootstrap- oder Session-Bearer-Zugangsdaten.
|
||||
- Verwenden Sie pro vhost eine stabile öffentliche Adresse. Wenn DNS mehrere Ingress-Adressen liefert, muss jede Bereitstellung die Adresse ihres externen Pfads verwenden.
|
||||
- Bootstrap- und Session-Register sind prozesslokal. Ein Multi-Prozess- oder Multi-Host-Upstream-Pool benötigt Affinität für den vollständigen vhost: Bridge-GET, Sitzungserstellung, Uplink, Downlink und DELETE. Ein einzelner Telemt-Prozess benötigt keine zusätzliche Affinität.
|
||||
- Der Decoy gehört zum Anti-Probing-Vertrag. Prüfen Sie sein gewöhnliches 404-Verhalten und die Antwortzeiten über den öffentlichen TLS-Endpunkt, bevor Sie Links verteilen.
|
||||
|
||||
## Erstprüfung
|
||||
|
||||
1. Starten Sie das neu erstellte Telemt-Binary mit der WEB-Konfiguration und prüfen Sie, dass der private Listener gebunden ist.
|
||||
2. Prüfen Sie über den öffentlichen TLS-Endpunkt, dass `GET /`, ein unbekannter Pfad und eine ungültige `bridge`-Query die konfigurierte Decoy-Site zurückgeben.
|
||||
3. Prüfen Sie, dass Telemt genau eine kanonische `X-Forwarded-For`-Adresse und `Host: proxy.example.com` oder `Host: proxy.example.com:443` erhält.
|
||||
4. Importieren Sie den ausgegebenen `tg://webproxy`-Link in den vorgesehenen Telegram-Desktop-Build und stellen Sie eine Proxy-Verbindung her.
|
||||
5. Testen Sie einen Reconnect und mindestens einen Long Poll über 25 Sekunden, um sicherzustellen, dass Frontend-Timeouts den Carrier nicht abbrechen.
|
||||
6. Prüfen Sie Benutzer- und logische MTProxy-Verbindungslimits anhand der Logical-Stream-Zähler und nicht anhand der Zahl der HTTP-Verbindungen.
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
| Symptom | Prüfung |
|
||||
| --- | --- |
|
||||
| WEB-Konfiguration ist auf dem Datenträger gültig, aber das Listener-Verhalten hat sich nicht geändert | Prüfen Sie `deferred_process_fields`; Listener- und `[web.limits]`-Änderungen erfordern einen Neustart. |
|
||||
| Carrier-Requests erreichen den Decoy | Prüfen Sie den exakten vhost, den Secret-Modus des Links, das CIDR des direkten Proxys und genau einen kanonischen `X-Forwarded-For`-Wert. |
|
||||
| Long Polls werden nach einem festen Intervall getrennt | Setzen Sie Client-, Server-, Sende- und Lese-Timeouts von NGINX/HAProxy über `web.timeouts.long_poll_secs`. |
|
||||
| Telegram Desktop lehnt den Link ab | Lassen Sie den Port weg und verwenden Sie einen gültigen FQDN, extern Port 443 sowie ausschließlich `plain` oder `dd`. |
|
||||
| Ein Knoten funktioniert, ein Load-Balancing-Pool aber nur sporadisch | Konfigurieren Sie Affinität für den gesamten vhost; WEB-Zugangsdatenregister sind prozesslokal. |
|
||||
@@ -0,0 +1,263 @@
|
||||
# WEB proxy mode
|
||||
|
||||
[English](WEB_PROXY.en.md) | [Русский](WEB_PROXY.ru.md) | [Deutsch](WEB_PROXY.de.md)
|
||||
|
||||
WEB mode carries ordinary MTProxy streams through a bounded HTTPS long-poll transport compatible with Telegram Desktop's `WEB` proxy type. In the first implementation, Telemt does not terminate TLS: NGINX or HAProxy owns the public certificate and forwards plain HTTP/1.1 to a private Telemt listener.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> WEB mode is implemented and configurable in the current source tree. The first deployment requires a binary built from a revision containing this implementation and a Telemt process restart. Published packages can be used only after verifying that they contain the same revision. End-to-end validation with the intended Telegram Desktop build and the real public TLS endpoint remains an operator acceptance step.
|
||||
|
||||
## Traffic path
|
||||
|
||||
```text
|
||||
Telegram Desktop
|
||||
| HTTPS :443
|
||||
v
|
||||
NGINX or HAProxy (TLS termination, canonical Host and X-Forwarded-For)
|
||||
| plain HTTP/1.1 on a private network
|
||||
v
|
||||
Telemt WEB listener
|
||||
|-- authenticated carrier --> bounded logical MTProxy relays --> Telegram
|
||||
`-- ordinary or invalid request --> configured decoy site
|
||||
```
|
||||
|
||||
Route the complete public vhost to Telemt. Splitting only recognized carrier paths at the TLS terminator would make ordinary and authenticated behavior observably different and would bypass Telemt's decoy policy.
|
||||
|
||||
## Supported client contract
|
||||
|
||||
- The public endpoint is always `https://HOST:443`.
|
||||
- `plain` and `dd` 16-byte MTProxy secrets are supported. `ee` FakeTLS secrets are not supported by WEB mode.
|
||||
- The initial carrier uses serialized HTTPS uplink requests and HTTPS long polling. WebSocket and lane carriers are not advertised.
|
||||
- Capability, bootstrap, and session credentials are separate bounded-lifetime values. Carrier credentials must be treated as secrets and must not appear in access logs.
|
||||
- Inner MTProxy authentication is restricted to the user and secret mode selected by the vhost profile. Invalid inner handshakes close only their logical stream and never enter the TCP masking path.
|
||||
|
||||
Telegram Desktop WEB links omit a port because the client requires port 443:
|
||||
|
||||
```text
|
||||
tg://webproxy?server=proxy.example.com&secret=0123456789abcdef0123456789abcdef
|
||||
tg://webproxy?server=proxy.example.com&secret=dd0123456789abcdef0123456789abcdef
|
||||
```
|
||||
|
||||
Telemt prints links for WEB profiles selected by `[general.links].show` through the existing `telemt::links` log target.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A dedicated public FQDN and valid TLS certificate on NGINX or HAProxy.
|
||||
- A stable public IP for that hostname. `public_addr` must be that concrete IP on port 443 because it participates in the inner relay destination tuple.
|
||||
- A private or loopback HTTP path from the TLS terminator to Telemt.
|
||||
- A normal decoy site, either a private HTTP origin or an immutable local directory snapshot.
|
||||
- A compatible Telegram Desktop build with the `WEB` proxy type.
|
||||
|
||||
If one hostname is served through both IPv4 and IPv6, use separate Telemt deployments or separate hostnames in this first implementation. The forwarded client address and `public_addr` must use the same IP family.
|
||||
|
||||
## Minimal Telemt configuration
|
||||
|
||||
The example keeps the WEB listener on loopback and uses a private HTTP decoy origin:
|
||||
|
||||
```toml
|
||||
[general.links]
|
||||
show = ["web-user"]
|
||||
|
||||
[access.users]
|
||||
web-user = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_client_ip_source = "x_forwarded_for"
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
|
||||
[web]
|
||||
enabled = true
|
||||
|
||||
[[web.vhosts]]
|
||||
host = "proxy.example.com"
|
||||
public_addr = "203.0.113.10:443"
|
||||
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://127.0.0.1:18081"
|
||||
|
||||
[[web.vhosts.profiles]]
|
||||
user = "web-user"
|
||||
secret_mode = "dd"
|
||||
max_sessions = 8
|
||||
max_streams = 512
|
||||
max_streams_per_session = 64
|
||||
```
|
||||
|
||||
The WEB listener must use `proxy_protocol = false` and `reuse_allow = false`. It cannot use `client_mss`, `synlimit`, `announce`, or `announce_ip`. `web_trusted_proxy_cidrs` must be non-empty and must contain only the immediate NGINX or HAProxy peers; `/0` networks are rejected.
|
||||
|
||||
The HTTP decoy origin must be a loopback, link-local, or private IP literal. Telemt preserves ordinary request method, path, query, headers, streamed body, response status, headers, and body while removing hop-by-hop headers. Malformed carrier requests have carrier credentials and bodies removed before falling back to the decoy.
|
||||
|
||||
An immutable static-site snapshot can be used instead:
|
||||
|
||||
```toml
|
||||
[web.vhosts.decoy]
|
||||
mode = "static_directory"
|
||||
directory = "/var/lib/telemt/public"
|
||||
index = "index.html"
|
||||
```
|
||||
|
||||
Static files are read at startup and successful configuration reload. Entry count, per-file size, and total snapshot size are bounded by `[web.limits]`. Symlinks and paths escaping the configured directory are rejected. Do not mutate the directory concurrently while Telemt builds a snapshot.
|
||||
|
||||
All WEB keys and defaults are listed in the [configuration reference](../Config_params/CONFIG_PARAMS.en.md#web).
|
||||
|
||||
## NGINX TLS termination
|
||||
|
||||
```nginx
|
||||
upstream telemt_web {
|
||||
server 127.0.0.1:18080;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name proxy.example.com;
|
||||
access_log off;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;
|
||||
|
||||
client_max_body_size 2m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://telemt_web;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 35s;
|
||||
proxy_read_timeout 35s;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_next_upstream off;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`client_max_body_size` must be at least `web.limits.max_body_bytes`. `proxy_read_timeout` and `proxy_send_timeout` must exceed `web.timeouts.long_poll_secs`, which defaults to 25 seconds. Overwrite, rather than append to, `X-Forwarded-For`. Do not enable upstream retries: the bridge performs byte-identical retries through its own sequence protocol.
|
||||
|
||||
## HAProxy TLS termination
|
||||
|
||||
```haproxy
|
||||
frontend public_https
|
||||
mode http
|
||||
no log
|
||||
bind :443 ssl crt /etc/haproxy/certs/proxy.example.com.pem alpn h2,http/1.1
|
||||
acl telemt_web_host hdr(host) -i proxy.example.com proxy.example.com:443
|
||||
use_backend telemt_web if telemt_web_host
|
||||
|
||||
backend telemt_web
|
||||
mode http
|
||||
option http-keep-alive
|
||||
retries 0
|
||||
timeout connect 5s
|
||||
timeout server 35s
|
||||
http-request set-header Host proxy.example.com
|
||||
http-request del-header X-Forwarded-For
|
||||
http-request set-header X-Forwarded-For %[src]
|
||||
server telemt_web_1 127.0.0.1:18080 check
|
||||
```
|
||||
|
||||
The frontend or `defaults` section must also set `timeout client` above the long-poll deadline. Do not rewrite the path, raw query, body, or the `Authorization`, `Content-Type`, `X-Up-Seq`, and `X-Down-Cursor` carrier headers.
|
||||
|
||||
## Lifecycle and reload behavior
|
||||
|
||||
| Configuration | Runtime behavior |
|
||||
| --- | --- |
|
||||
| WEB listener inventory, bind address, and trust policy | Process-owned; restart Telemt. |
|
||||
| Any `[web.limits]` value | Process-owned memory/resource contract; restart Telemt. |
|
||||
| `web.enabled`, timeouts, vhosts, profiles, and decoys | Applied by the config watcher or a runtime generation reload. |
|
||||
| Existing HTTP connections and WEB sessions | Keep their acquisition-time limits and deadlines; new logical streams use the active runtime generation. |
|
||||
| Process shutdown | Uses the latest reloaded `web.timeouts.shutdown_secs`. |
|
||||
|
||||
Each logical stream keeps its session's creation-time client IP and owns a process-unique, non-zero synthetic source port for the complete relay lifetime. This preserves one stable, non-colliding source/destination tuple for Direct and Middle-End KDF routing.
|
||||
|
||||
## API management
|
||||
|
||||
API management is available, but it is intentionally partial. There is no dedicated `/v1/web` endpoint and no WEB-specific runtime statistics endpoint.
|
||||
|
||||
| Operation | API support |
|
||||
| --- | --- |
|
||||
| Read or patch `[web]`, vhosts, profiles, decoys, timeouts, or limits | No. `GET /v1/config` omits `[web]`; `PATCH /v1/config` returns `400 section_not_editable` for `web`. |
|
||||
| Persist `server.listeners` | Yes, through `PATCH /v1/config`, but a changed WEB listener remains deferred until process restart. |
|
||||
| Apply an externally edited WEB configuration | Yes, through `POST /v1/system/reload`, then inspect the operation status. |
|
||||
| Manage `[access.users]` | Yes, through `/v1/users`. User creation does not create a WEB profile. |
|
||||
| Revoke one user | Yes. `/v1/users/{username}/disable` updates admission immediately and cancels that user's active sessions. |
|
||||
|
||||
Bind the API to loopback, keep its direct-peer whitelist narrow, configure an exact authorization header, and leave `read_only = false` only when mutation is required:
|
||||
|
||||
```toml
|
||||
[server.api]
|
||||
enabled = true
|
||||
listen = "127.0.0.1:9091"
|
||||
whitelist = ["127.0.0.0/8"]
|
||||
auth_header = "Bearer replace-with-a-random-control-token"
|
||||
read_only = false
|
||||
```
|
||||
|
||||
The API whitelist checks the direct TCP peer and does not trust `X-Forwarded-For`. Changes to `[server.api]` itself require a process restart.
|
||||
|
||||
After an administrator or configuration system atomically updates the TOML file, set `TELEMT_API_AUTH` to the exact value configured in `auth_header` and submit an observable generation reload:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/system/reload \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"mode":"drain","timeout_secs":30,"failure_policy":"rollback"}'
|
||||
|
||||
# Use data.reload_id from the response.
|
||||
curl -sS http://127.0.0.1:9091/v1/system/reload/RELOAD_ID \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
```
|
||||
|
||||
A terminal `succeeded` status confirms runtime activation. If `deferred_process_fields` contains `server.listeners` or `web.limits`, the file is valid and persisted but those settings still require a Telemt restart.
|
||||
|
||||
Access-user operations use the existing endpoints, for example:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/disable \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/rotate-secret \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
The config watcher rebuilds WEB capabilities after a secret rotation. The users API returns the secret, not a `tg://webproxy` URL; construct the link with the configured hostname and the profile's `plain` or `dd` representation. Before deleting a user referenced by a WEB profile, remove and apply that profile first so the resulting configuration remains valid.
|
||||
|
||||
See the complete [Control API contract](../Architecture/API/API.md) for request envelopes, revisions, failure modes, and all user endpoints.
|
||||
|
||||
## Deployment invariants
|
||||
|
||||
- Never expose the plain HTTP WEB listener to an untrusted network. Enforce the restriction with host firewall rules even when it binds to loopback.
|
||||
- Disable request-target and authorization logging at the TLS terminator, or use a verified redacted format. Raw queries contain bridge capabilities and `Authorization` contains bootstrap or session bearer credentials.
|
||||
- Keep one stable public address per vhost. If DNS returns several ingress addresses, each deployment must use the address matching its external path.
|
||||
- Bootstrap and session registries are process-local. A multi-process or multi-host upstream pool requires affinity for the complete vhost: bridge GET, session creation, uplink, downlink, and DELETE. A single Telemt process needs no extra affinity.
|
||||
- The decoy is part of the anti-probing contract. Verify its ordinary 404 behavior and response timing through the public TLS endpoint before distributing links.
|
||||
|
||||
## Initial verification
|
||||
|
||||
1. Start the rebuilt Telemt binary with the WEB configuration and confirm that the private listener is bound.
|
||||
2. Confirm through the public TLS endpoint that `GET /`, an unknown path, and an invalid `bridge` query return the configured decoy site.
|
||||
3. Confirm that Telemt receives one canonical `X-Forwarded-For` address and `Host: proxy.example.com` or `Host: proxy.example.com:443`.
|
||||
4. Import the printed `tg://webproxy` link in the intended Telegram Desktop build and establish a proxy connection.
|
||||
5. Exercise reconnect and at least one long poll beyond 25 seconds to prove the frontend timeouts do not truncate the carrier.
|
||||
6. Verify user and logical MTProxy connection limits using logical-stream counters, not the number of HTTP connections.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Check |
|
||||
| --- | --- |
|
||||
| WEB configuration is valid on disk but listener behavior did not change | Inspect reload `deferred_process_fields`; listener and `[web.limits]` changes require restart. |
|
||||
| Carrier requests reach the decoy | Verify exact vhost, link secret mode, direct proxy CIDR, and one canonical `X-Forwarded-For` value. |
|
||||
| Long polls disconnect near a fixed interval | Raise NGINX/HAProxy client, server, send, and read timeouts above `web.timeouts.long_poll_secs`. |
|
||||
| Telegram Desktop rejects the link | Omit the port, use a valid FQDN, port 443 externally, and only `plain` or `dd` secret mode. |
|
||||
| One node works but a load-balanced pool is intermittent | Add complete-vhost affinity; WEB credential registries are process-local. |
|
||||
@@ -0,0 +1,263 @@
|
||||
# WEB-режим прокси
|
||||
|
||||
[English](WEB_PROXY.en.md) | [Русский](WEB_PROXY.ru.md) | [Deutsch](WEB_PROXY.de.md)
|
||||
|
||||
WEB-режим переносит обычные MTProxy-потоки через ограниченный HTTPS long-poll transport, совместимый с типом прокси `WEB` в Telegram Desktop. В первой реализации Telemt не терминирует TLS: публичный сертификат обслуживает NGINX или HAProxy, который передаёт обычный HTTP/1.1 на приватный listener Telemt.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> WEB-режим реализован и настраивается в текущем дереве исходного кода. Для первого развёртывания нужен бинарный файл, собранный из ревизии с этой реализацией, и перезапуск процесса Telemt. Готовый пакет можно использовать только после проверки, что он содержит эту ревизию. Сквозная проверка с целевой сборкой Telegram Desktop и реальным публичным TLS endpoint остаётся обязательным приёмочным шагом оператора.
|
||||
|
||||
## Путь трафика
|
||||
|
||||
```text
|
||||
Telegram Desktop
|
||||
| HTTPS :443
|
||||
v
|
||||
NGINX или HAProxy (TLS termination, канонические Host и X-Forwarded-For)
|
||||
| обычный HTTP/1.1 в приватной сети
|
||||
v
|
||||
WEB-listener Telemt
|
||||
|-- аутентифицированный carrier --> bounded logical MTProxy relays --> Telegram
|
||||
`-- обычный или некорректный запрос --> настроенный decoy site
|
||||
```
|
||||
|
||||
Направляйте в Telemt весь публичный vhost. Если TLS-терминатор будет выделять только известные carrier paths, поведение обычных и аутентифицированных запросов станет наблюдаемо различным, а decoy policy Telemt будет обойдена.
|
||||
|
||||
## Поддерживаемый контракт клиента
|
||||
|
||||
- Публичный endpoint всегда имеет вид `https://HOST:443`.
|
||||
- Поддерживаются 16-байтовые MTProxy-секреты `plain` и `dd`. FakeTLS-секреты `ee` в WEB-режиме не поддерживаются.
|
||||
- Первая версия carrier использует сериализованные HTTPS uplink-запросы и HTTPS long polling. WebSocket- и lane-carriers не анонсируются.
|
||||
- Capability, bootstrap и session credentials — отдельные значения с ограниченным сроком жизни. Carrier credentials считаются секретами и не должны попадать в access logs.
|
||||
- Внутренняя MTProxy-аутентификация ограничена пользователем и режимом секрета, выбранными профилем vhost. Некорректный внутренний handshake закрывает только свой logical stream и никогда не попадает в TCP masking path.
|
||||
|
||||
В WEB-ссылках Telegram Desktop нет порта, потому что клиент требует порт 443:
|
||||
|
||||
```text
|
||||
tg://webproxy?server=proxy.example.com&secret=0123456789abcdef0123456789abcdef
|
||||
tg://webproxy?server=proxy.example.com&secret=dd0123456789abcdef0123456789abcdef
|
||||
```
|
||||
|
||||
Telemt печатает ссылки для WEB-профилей, выбранных в `[general.links].show`, через существующий log target `telemt::links`.
|
||||
|
||||
## Предварительные требования
|
||||
|
||||
- Отдельный публичный FQDN и действующий TLS-сертификат на NGINX или HAProxy.
|
||||
- Стабильный публичный IP этого hostname. В `public_addr` должен быть указан именно этот конкретный IP с портом 443, поскольку адрес участвует во внутреннем destination tuple relay.
|
||||
- Приватный или loopback HTTP-путь от TLS-терминатора до Telemt.
|
||||
- Обычный decoy site: приватный HTTP origin либо immutable snapshot локального каталога.
|
||||
- Совместимая сборка Telegram Desktop с типом прокси `WEB`.
|
||||
|
||||
Если один hostname обслуживается одновременно по IPv4 и IPv6, в первой реализации используйте отдельные hostname или отдельные экземпляры Telemt. Forwarded client address и `public_addr` должны принадлежать одному семейству IP.
|
||||
|
||||
## Минимальная конфигурация Telemt
|
||||
|
||||
В примере WEB-listener остаётся на loopback, а decoy использует приватный HTTP origin:
|
||||
|
||||
```toml
|
||||
[general.links]
|
||||
show = ["web-user"]
|
||||
|
||||
[access.users]
|
||||
web-user = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
proxy_protocol = false
|
||||
web_client_ip_source = "x_forwarded_for"
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
|
||||
[web]
|
||||
enabled = true
|
||||
|
||||
[[web.vhosts]]
|
||||
host = "proxy.example.com"
|
||||
public_addr = "203.0.113.10:443"
|
||||
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://127.0.0.1:18081"
|
||||
|
||||
[[web.vhosts.profiles]]
|
||||
user = "web-user"
|
||||
secret_mode = "dd"
|
||||
max_sessions = 8
|
||||
max_streams = 512
|
||||
max_streams_per_session = 64
|
||||
```
|
||||
|
||||
Для WEB-listener обязательны `proxy_protocol = false` и `reuse_allow = false`. В нём нельзя использовать `client_mss`, `synlimit`, `announce` и `announce_ip`. Массив `web_trusted_proxy_cidrs` должен быть непустым и содержать только непосредственные адреса NGINX или HAProxy; сети `/0` запрещены.
|
||||
|
||||
HTTP decoy origin должен быть loopback, link-local или private IP literal. Для обычных запросов Telemt сохраняет method, path, query, headers, streamed body, response status, headers и body, удаляя hop-by-hop headers. Перед отправкой некорректного carrier-запроса в decoy Telemt удаляет из него carrier credentials и body.
|
||||
|
||||
Вместо origin можно использовать immutable snapshot статического сайта:
|
||||
|
||||
```toml
|
||||
[web.vhosts.decoy]
|
||||
mode = "static_directory"
|
||||
directory = "/var/lib/telemt/public"
|
||||
index = "index.html"
|
||||
```
|
||||
|
||||
Статические файлы читаются при запуске и успешном reload конфигурации. Число элементов, размер одного файла и общий размер snapshot ограничены `[web.limits]`. Symlinks и пути с выходом из настроенного каталога запрещены. Не изменяйте каталог одновременно с построением snapshot в Telemt.
|
||||
|
||||
Все WEB-ключи и defaults перечислены в [справочнике конфигурации](../Config_params/CONFIG_PARAMS.ru.md#web).
|
||||
|
||||
## Терминация TLS на NGINX
|
||||
|
||||
```nginx
|
||||
upstream telemt_web {
|
||||
server 127.0.0.1:18080;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name proxy.example.com;
|
||||
access_log off;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;
|
||||
|
||||
client_max_body_size 2m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://telemt_web;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 35s;
|
||||
proxy_read_timeout 35s;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_next_upstream off;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`client_max_body_size` должен быть не меньше `web.limits.max_body_bytes`. Значения `proxy_read_timeout` и `proxy_send_timeout` должны превышать `web.timeouts.long_poll_secs`, по умолчанию равный 25 секундам. Перезаписывайте `X-Forwarded-For`, а не дополняйте его. Не включайте upstream retries: byte-identical retry выполняет сам bridge по своему sequence protocol.
|
||||
|
||||
## Терминация TLS на HAProxy
|
||||
|
||||
```haproxy
|
||||
frontend public_https
|
||||
mode http
|
||||
no log
|
||||
bind :443 ssl crt /etc/haproxy/certs/proxy.example.com.pem alpn h2,http/1.1
|
||||
acl telemt_web_host hdr(host) -i proxy.example.com proxy.example.com:443
|
||||
use_backend telemt_web if telemt_web_host
|
||||
|
||||
backend telemt_web
|
||||
mode http
|
||||
option http-keep-alive
|
||||
retries 0
|
||||
timeout connect 5s
|
||||
timeout server 35s
|
||||
http-request set-header Host proxy.example.com
|
||||
http-request del-header X-Forwarded-For
|
||||
http-request set-header X-Forwarded-For %[src]
|
||||
server telemt_web_1 127.0.0.1:18080 check
|
||||
```
|
||||
|
||||
Во frontend или секции `defaults` также задайте `timeout client` выше long-poll deadline. Не переписывайте path, raw query, body и carrier headers `Authorization`, `Content-Type`, `X-Up-Seq`, `X-Down-Cursor`.
|
||||
|
||||
## Lifecycle и reload
|
||||
|
||||
| Конфигурация | Поведение runtime |
|
||||
| --- | --- |
|
||||
| Состав WEB-listeners, bind address и trust policy | Принадлежат процессу; перезапустите Telemt. |
|
||||
| Любое значение `[web.limits]` | Process-owned контракт памяти и ресурсов; перезапустите Telemt. |
|
||||
| `web.enabled`, timeouts, vhosts, profiles и decoys | Применяются config watcher или runtime generation reload. |
|
||||
| Существующие HTTP connections и WEB sessions | Сохраняют лимиты и deadlines своего момента создания; новые logical streams используют активное runtime generation. |
|
||||
| Завершение процесса | Использует последнее применённое значение `web.timeouts.shutdown_secs`. |
|
||||
|
||||
Каждый logical stream сохраняет client IP своей сессии и владеет уникальным в пределах процесса ненулевым synthetic source port до завершения relay. Это сохраняет один стабильный непересекающийся source/destination tuple для Direct и Middle-End KDF routing.
|
||||
|
||||
## Управление через API
|
||||
|
||||
Управление через API доступно, но намеренно ограничено. Отдельных endpoint `/v1/web` и WEB-specific runtime statistics endpoint сейчас нет.
|
||||
|
||||
| Операция | Поддержка API |
|
||||
| --- | --- |
|
||||
| Чтение или изменение `[web]`, vhosts, profiles, decoys, timeouts или limits | Нет. `GET /v1/config` не возвращает `[web]`; `PATCH /v1/config` отвечает `400 section_not_editable` на ключ `web`. |
|
||||
| Сохранение `server.listeners` | Да, через `PATCH /v1/config`, но изменённый WEB-listener остаётся deferred до перезапуска процесса. |
|
||||
| Применение WEB-конфигурации, изменённой вне API | Да, через `POST /v1/system/reload` с последующей проверкой статуса операции. |
|
||||
| Управление `[access.users]` | Да, через `/v1/users`. Создание пользователя не создаёт WEB-профиль. |
|
||||
| Отзыв отдельного пользователя | Да. `/v1/users/{username}/disable` немедленно обновляет admission и завершает активные сессии пользователя. |
|
||||
|
||||
Привяжите API к loopback, оставьте узким whitelist непосредственных peers, настройте точное значение authorization header и используйте `read_only = false` только там, где нужны мутации:
|
||||
|
||||
```toml
|
||||
[server.api]
|
||||
enabled = true
|
||||
listen = "127.0.0.1:9091"
|
||||
whitelist = ["127.0.0.0/8"]
|
||||
auth_header = "Bearer replace-with-a-random-control-token"
|
||||
read_only = false
|
||||
```
|
||||
|
||||
API whitelist проверяет непосредственный TCP peer и не доверяет `X-Forwarded-For`. Изменения самой секции `[server.api]` требуют перезапуска процесса.
|
||||
|
||||
После атомарного изменения TOML-файла администратором или системой управления конфигурацией задайте в `TELEMT_API_AUTH` точное значение `auth_header` и отправьте наблюдаемый generation reload:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/system/reload \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"mode":"drain","timeout_secs":30,"failure_policy":"rollback"}'
|
||||
|
||||
# Use data.reload_id from the response.
|
||||
curl -sS http://127.0.0.1:9091/v1/system/reload/RELOAD_ID \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
```
|
||||
|
||||
Терминальный статус `succeeded` подтверждает активацию runtime. Если `deferred_process_fields` содержит `server.listeners` или `web.limits`, файл валиден и сохранён, но эти настройки всё ещё требуют перезапуска Telemt.
|
||||
|
||||
Операции с access users используют существующие endpoints, например:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/disable \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}"
|
||||
|
||||
curl -sS -X POST http://127.0.0.1:9091/v1/users/web-user/rotate-secret \
|
||||
-H "Authorization: ${TELEMT_API_AUTH}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
После ротации секрета config watcher перестраивает WEB capabilities. Users API возвращает секрет, но не URL `tg://webproxy`; соберите ссылку из настроенного hostname и представления `plain` или `dd` соответствующего профиля. Перед удалением пользователя, на которого ссылается WEB-профиль, сначала удалите и примените этот профиль, чтобы итоговая конфигурация оставалась валидной.
|
||||
|
||||
Полный контракт запросов, revisions, ошибок и всех user endpoints приведён в [документации Control API](../Architecture/API/API.md).
|
||||
|
||||
## Инварианты развёртывания
|
||||
|
||||
- Никогда не публикуйте plain HTTP WEB-listener в недоверенной сети. Закрепите это host firewall rules, даже если listener использует loopback.
|
||||
- Отключите логирование request target и authorization на TLS-терминаторе либо используйте проверенный формат с редактированием. Raw queries содержат bridge capabilities, а `Authorization` — bootstrap или session bearer credentials.
|
||||
- Сохраняйте один стабильный публичный адрес на vhost. Если DNS возвращает несколько ingress addresses, каждый deployment должен использовать адрес своего внешнего пути.
|
||||
- Bootstrap- и session-registries локальны для процесса. Для multi-process или multi-host upstream pool нужна affinity всего vhost: bridge GET, создание сессии, uplink, downlink и DELETE. Одному процессу Telemt дополнительная affinity не нужна.
|
||||
- Decoy входит в anti-probing contract. До распространения ссылок проверьте через публичный TLS endpoint его обычный ответ 404 и response timing.
|
||||
|
||||
## Первичная проверка
|
||||
|
||||
1. Запустите пересобранный Telemt с WEB-конфигурацией и убедитесь, что приватный listener привязан.
|
||||
2. Через публичный TLS endpoint проверьте, что `GET /`, неизвестный path и некорректный query `bridge` возвращают настроенный decoy site.
|
||||
3. Убедитесь, что Telemt получает один канонический адрес `X-Forwarded-For` и `Host: proxy.example.com` либо `Host: proxy.example.com:443`.
|
||||
4. Импортируйте напечатанную ссылку `tg://webproxy` в целевую сборку Telegram Desktop и установите соединение через прокси.
|
||||
5. Проверьте reconnect и как минимум один long poll длительнее 25 секунд, чтобы frontend timeouts не обрывали carrier.
|
||||
6. Проверяйте лимиты пользователя и logical MTProxy connections по logical-stream counters, а не по числу HTTP connections.
|
||||
|
||||
## Диагностика
|
||||
|
||||
| Симптом | Что проверить |
|
||||
| --- | --- |
|
||||
| WEB-конфигурация валидна на диске, но поведение listener’а не изменилось | Проверьте `deferred_process_fields`; listener и `[web.limits]` требуют перезапуска. |
|
||||
| Carrier-запросы попадают в decoy | Проверьте точный vhost, secret mode ссылки, CIDR непосредственного proxy и единственное каноническое значение `X-Forwarded-For`. |
|
||||
| Long polls разрываются через фиксированный интервал | Поднимите client, server, send и read timeouts NGINX/HAProxy выше `web.timeouts.long_poll_secs`. |
|
||||
| Telegram Desktop отклоняет ссылку | Не указывайте порт, используйте валидный FQDN, внешний порт 443 и только `plain` или `dd`. |
|
||||
| Один узел работает, но load-balanced pool нестабилен | Настройте affinity всего vhost: WEB credential registries локальны для процесса. |
|
||||
Reference in New Issue
Block a user