Skip to content

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 14:14
· 117 commits to main since this release

Telegram's WEB proxy transport, and the two secret forms that reach it

The headline is a new connection type. Telegram Desktop 7.1 added a WEB proxy: the client loads a page from a hostname you control, inside its own WebView, and that page carries MTProto over a WebSocket. A network in between sees a browser visiting a website over genuine TLS — there is no obfuscated handshake to fingerprint and no FakeTLS to distinguish, because the TLS is real.

Reaching it needed the rest of this release. A WEB-capable client refuses an ee secret before it opens a socket, so serving one meant teaching the proxy the two pre-FakeTLS secret forms (dd and plain) and giving them their own listening sockets.

WEB proxy transport

  • mtg-proxy web-relay <config> runs the relay as a second process of the same binary, reading the same config file. --check validates the [web] table and exits.
  • mtg-proxy generate-web-key <path> writes its 32-byte token signing key.
  • Three-tier recognition on the public origin: a client holding a capability the relay minted gets the bridge page; an authentic-but-unusable one gets a local uncacheable 404 that is never forwarded; everything else gets your cover site, on every path — including the API ones. Nothing ships as a default cover page, because a starter page many operators installed would be a better signature than anything the protocol leaks.
  • Flow control is bounded in both directions, per stream and per session, with a global backstop across the process.
  • mtg-proxy access prints a web object per secret, and /stats advertises web-relay in features so a panel can tell whether the binary supports it.
  • contrib/web-relay/ carries the deployment: the Caddy vhost, the four hops that have to carry the client's address, and an acceptance checklist.

The three secret forms

A secret is sixteen key bytes. The ee / dd / bare-hex prefix only tells the client which door to knock on, so an existing user keeps one name, one quota bucket and one set of counters whichever form they use — nothing has to be re-issued.

  • New [[listener]] config table declares extra obfuscated2 sockets, each with its own accept set, public acknowledgement, proxy-protocol and unknown-secret disposal policy.
  • bind-to is, and stays, FakeTLS-only. One port never serves both entry paths and there is no sniffing.
  • /stats reports what each listener accepts, and per-secret modes.

Reload

The relay re-reads [secrets] on SIGHUP and swaps its profile set atomically. Only the secret set is reloadable — any other [web] change is refused whole with restart required rather than half-applied, and a reload that cannot read or validate the file keeps the running set.

Fixes worth knowing about

  • FakeTLS: an unauthenticated client hello was buffered before its length was bounded.
  • Config: byte sizes truncated on 32-bit builds.
  • API: neither HTTP server had a header read deadline.
  • Obfuscation: bytes returned alongside a read error were dropped instead of decrypted; the handshake plaintext prefix was not kept on the wire.
  • Proxy: a PROXY-protocol header that would not format panicked.
  • Doppelganger: noise size could exceed what a TLS record can carry.
  • Middle proxy: padding was declared unconditionally; padded-intermediate padding is 0-15, not 0-3.

Supply chain

Every release now publishes a checksums file, an SPDX SBOM of the module graph, and a signed build provenance attestation covering all of it — including the checksums file itself. Verify with:

gh attestation verify <file> --repo Arman2122/mtg-proxy --signer-workflow Arman2122/mtg-proxy/.github/workflows/go.yml

Upgrading

Additive only. No config key was renamed or removed, release asset names are unchanged, and the default accept-modes is still ["ee"] — an existing deployment behaves exactly as it did on v1.0.1 until you add a [[listener]] or a [web] table.


Full Changelog: v1.0.1...v1.1.0