Bỏ qua

Changelog

4.15.0

  • ClientHello JA4 fingerprint distribution on /stats and /metrics (#102, follow-up to #39 / #101). When TSPU updates a detection signature, the only operationally interesting traffic is the probe that fails secret validation — so the proxy now hashes every well-formed ClientHello (per the foxio JA4 spec) before the HMAC check and rolls it into a top-32 counter. Surfaces as ja4_seen<TAB><hash><TAB><N> tab-separated lines in /stats and teleproxy_ja4_seen{hash="..."} samples in /metrics. Always on, no configuration. Optional [stats] ja4_log = true TOML key (also --ja4-log CLI / JA4_LOG=true env) prints ja4=<hash> sni=<name> per connection at verbose level 2 for one-off investigations. See the new ClientHello JA4 Fingerprint Distribution section. Downstream goal: the observatory project can now chart fingerprint distribution over time and correlate with block rate instead of relying on user reports.

4.14.1

  • Fix macOS backend-forward path. Backend forwarding (the camouflage fallback for invalid handshakes — wrong secret, stale timestamp, unknown SNI, duplicate client_random, plain HTTPS probes from a browser) silently dropped every request on macOS native builds: writev() to a freshly opened outbound socket whose connect() was still in EINPROGRESS returns ENOTCONN on BSD/Darwin where Linux returns EAGAIN, and the proxy treated anything other than EAGAIN/EINTR as fatal — tearing down the connection before the SYN-ACK arrived. ENOTCONN is now a retry condition too. Five fake-TLS E2E tests that were previously silently passing in Linux Docker but failing on the macOS dev build (test_wrong_secret_rejected, test_stale_timestamp_rejected, test_unknown_sni_falls_back, test_duplicate_client_random_rejected, test_browser_tls_sees_real_backend) now pass on both targets. The bug never manifested in production because production runs Linux.
  • Disable knob for the v4.14.0 MSS clamp. New mss_clamp = false TOML key, --no-mss-clamp CLI flag, and MSS_CLAMP=false env var (start.sh) turn off the automatic ClientHello fragmentation shipped in v4.14.0. Default stays on. Useful escape hatch for bandwidth-bound operators willing to take the JA4 detection risk in exchange for ~5× lower packet count on the proxy listener. See Forced ClientHello fragmentation for what's being turned off.

4.14.0

  • Automatic ClientHello fragmentation against TSPU JA4 fingerprinting (#39). Russian DPI (TSPU/ASBI) classifies traffic to fake-TLS MTProxies by JA4-hashing the client's ClientHello — a fingerprint teleproxy itself can't change because the bytes are produced by the Telegram client. But teleproxy can change how those bytes hit the wire: the public listening socket now announces a 256-byte TCP MSS in the SYN-ACK, and the client kernel respects it, splitting the outgoing ClientHello (~500-700 bytes) across 2-3 TCP segments. Critically, ALPN and signature_algorithms — both required inputs to the JA4 hash — typically land in segment 2 or 3 of a real Telegram ClientHello, so a DPI that fingerprints from the first segment alone computes the wrong hash and the connection slips past the signature. The trick is automatic — no flag, no config, no upgrade-time decisions for the operator — and is scoped to the user-facing proxy port only; the HTTP /stats / /metrics listener keeps full system MSS. Pairs with the existing ServerHello segmentation so both sides of the handshake become unfriendly to single-packet DPI. Genuinely novel — neither mtg nor telemt does this. Trade-off: Linux applies the listening-socket MSS to both directions of each accepted connection, so server→client segments are also capped at 256 bytes — packet count rises ~5× and per-byte TCP/IP header overhead grows from ~3% to ~15% on the proxy listener. Modern hardware with TSO/GSO handles the CPU side, and the existing 20MB MTProto E2E test still completes well inside its timeout. See the new Forced ClientHello fragmentation section for the mechanism and rationale.

4.13.0

  • Wildcard certificate support in fake-TLS (#44). Operators with a wildcard cert like *.example.com served from a concrete vhost (e.g. proxy.example.com) couldn't make fake-TLS work — SNI lookup did literal string compare against *.example.com, every real ClientHello missed the match, and the connection landed on nginx's default vhost serving an unrelated cert (the failure mode also reported against mtg as 9seconds/mtg#394). Configure -D '*.example.com:proxy.example.com:443' (or EE_DOMAIN=*.example.com, EE_BACKEND=proxy.example.com:443) and teleproxy now matches any single-label subdomain against the pattern per RFC 6125: proxy.example.com matches, example.com (apex) and a.b.example.com (multi-label) do not. Startup fingerprinting probes the backend hostname, so the captured ServerHello parameters reflect the real cert vhost. Covered by make test-wildcard-cert.
  • Document direct-mode limitations (#79). The Direct-to-DC Mode page now spells out what --direct trades away by skipping Telegram's middle-end: media on non-Premium accounts may not load, sponsored channels aren't delivered, and Telegram voice/video calls aren't carried by any MTProto proxy regardless of mode. Answers the recurring symptom in #60.
  • CONFIG_DOWNLOAD_PROXY env var for the proxy-multi.conf download (#61). Hosts that can't reach core.telegram.org directly can now route the config refresh through an outbound HTTP or SOCKS proxy. Accepts any URL curl -x understands (http://, https://, socks5://, socks5h://, with optional user:pass@). Falls back to SOCKS5_PROXY when unset, so users who already proxy DC traffic don't need to set anything new. Applies to the initial fetch in start.sh and the 6-hour cron refresh.
  • Split SNI domain from camouflage backend (#62). The fake-TLS feature used to require EE_DOMAIN to be both the SNI hostname and the backend connect target — operators worked around it by editing /etc/hosts. New EE_BACKEND env var separates the two: EE_DOMAIN keeps a clean public SNI name like cloudflare.com, while EE_BACKEND points at the actual backend (127.0.0.1:8443, [::1]:8443, or unix:/run/nginx.sock). Reality-style. Available as TOML too: domain = [{ name = "cloudflare.com", backend = "127.0.0.1:8443" }]. The legacy EE_DOMAIN=host:port and domain = "..." strings still work unchanged.
  • Fix pre-handshake socket accumulation (#63). Scanners and probes that opened a TCP connection but never sent the obfs2 header used to sit on the proxy until OS-level TCP keepalive killed them roughly two hours later, inflating total_connections (Prometheus teleproxy_active_connections) far above the authenticated user count. The proxy already schedules a 10-second handshake alarm, but the handler used to no-op when fake-TLS wasn't configured. It now drops the unhandshaked socket. Most visible under random-padding (dd) mode, where there's no SNI gate to filter scanners early. New make test-handshake-timeout regression test asserts the counter returns to baseline within 15 seconds.
  • Memory-handling robustness and exhaustive cppcheck in CI (#84, #85, #82, #78). Tightened allocation/free paths in src/common/common-stats.c and a few buffer sites that could leak or double-free under memory pressure. make lint now runs cppcheck --check-level=exhaustive against a source-built cppcheck 2.20 (Ubuntu's 2.13 silently misses OOM checks), and CI fails on new findings — so future OOM-path regressions surface before merge instead of after.

4.12.2

Build hygiene release. No runtime changes.

  • Clang x86_64 build support (#68). make CC=clang now compiles cleanly on Linux x86_64 — src/common/crc32.c previously relied on GCC-only __builtin_ia32_* names for SSE/PCLMUL ops; clang gets _mm_* intrinsic shims for the missing ones. The shim block is gated to __x86_64__/__i386__ so Apple Silicon clang isn't affected.
  • Clang x86_64 CI job (#72). New build-clang job in the test matrix runs make CC=clang so future regressions in the clang build path surface in CI instead of in third-party packaging.

4.12.1

Hotfix for log spam introduced by the 4.12.0 unique-IPs counter rework.

  • Fix WARNING: IP tracking table full for secret 0 flooding docker logs (#71). The 4.12.0 fix for #70 made every secret populate a fixed-size 256-entry per-IP table; on a busy public proxy with no max_ips or rate_limit configured, the table fills as soon as ~256 distinct source IPs have connected and every subsequent unique client logs a warning. Plain secrets now bypass the precise table entirely and feed teleproxy_secret_unique_ips from a per-secret Bloom filter — bounded memory, no overflow, ~0.5% FPR at 10K IPs.
  • Throttle the table-full warning to once per minute per slot for limit-bearing secrets where max_ips legitimately exceeds the table size. The message now also includes the secret label and a hint to raise max_ips or check the configuration.
  • The macro SECRET_MAX_TRACKED_IPS is now overridable at build time (make EXTRA_CFLAGS="-DSECRET_MAX_TRACKED_IPS=N"), used by the new make test-table-full regression test.

4.12.0

Bug fixes for Docker deployments and per-secret metrics.

  • Fix teleproxy_secret_unique_ips always reporting 0 (#70). The counter only ever incremented for secrets that had max_ips or rate_limit configured — a stale guard inside ip_track_connect() that broke the metric for every plain secret. Removed the guard so the IP-tracking table populates for all secrets. Also clarified the metric type: teleproxy_secret_unique_ips is counter (cumulative count of distinct source IPs since startup), not gauge.
  • Clarified teleproxy_secret_bytes_received_total / _sent_total HELP text (#70). The labels are technically correct ("received from clients" = uploads, "sent to clients" = downloads) but easy to misread from a client perspective. The HELP lines now explicitly call out the direction. Note: these counters increment in direct mode only — relay-mode aggregation is a separate gap, tracked for a follow-up.
  • Fix Docker SECRET=hex:label,hex:label writing the entire string as the TOML key (#67). The numbered-secret path (SECRET_LABEL_N) was already correct; the comma-separated path now mirrors it and writes a separate label = ... line.
  • New EXTERNAL_PORT env var (#66) for advertising a different port in the connection link than the internal listen port — needed when Docker maps -p 4443:443. Also added a matching external_port TOML option, consumed by both the operator-printed connection URL and the /link HTML page (QR codes).

4.11.0

SOCKS5 upstream support in the check command (#57) and a new Cloudflare Spectrum deployment guide (#55).

  • teleproxy check now routes DC probes through the configured SOCKS5 proxy — new --socks5 URL CLI flag, also reads from the socks5 field in TOML config. Useful for environments where the proxy itself sits behind a SOCKS5 upstream.
  • Handle buffer allocation failures gracefully instead of crashing (#58). Under memory pressure, rwm_alloc and related functions could return NULL and trigger a segfault. Now they fail the connection cleanly.
  • Fix PROXY protocol metrics always reporting 0 in multi-worker mode (#53). teleproxy_proxy_protocol_connections_total and teleproxy_proxy_protocol_errors_total were not aggregated from worker processes — the master read its own zero copy. Both the text stats and Prometheus endpoints now report correct values.
  • New deployment guide for running behind Cloudflare Spectrum.

4.10.0

Graceful connection draining on secret removal (#45) and a brand-new signed RPM repository (#21).

  • Zero-downtime secret rotation — removing a secret from config.toml and sending SIGHUP no longer drops the in-flight connections that were authenticated under it. The slot enters a "draining" state: new connections matching that secret are rejected, but existing ones keep working until they close on their own or drain_timeout_secs (default 300, 0 = infinite) elapses. Re-adding a draining secret revives the same slot — counters and IP tracking carry over.
  • New TOML option drain_timeout_secs (reloadable). Pinned -S CLI secrets are immutable across SIGHUP and never drain.
  • New stats: secret_<lbl>_draining, secret_<lbl>_drain_age_seconds, secret_<lbl>_rejected_draining, secret_<lbl>_drain_forced. Same fields exposed in Prometheus as teleproxy_secret_draining, teleproxy_secret_drain_age_seconds, teleproxy_secret_rejected_draining_total, teleproxy_secret_drain_forced_total.
  • Slot capacity expanded internally: 16 active secrets at a time as before, plus up to 16 additional draining slots.
  • Fixes a latent bug where the per-secret connection counter could go negative if a TLS connection was closed between the TLS handshake and the obfs2 init.
  • Signed dnf repository at https://teleproxy.github.io/repo/ serving RHEL 9, RHEL 10, AlmaLinux, Rocky Linux, and Fedora 41/42 on x86_64 and aarch64. Install on any of these distros with dnf install https://teleproxy.github.io/repo/teleproxy-release-latest.noarch.rpm && dnf install teleproxy && systemctl enable --now teleproxy. Packages are signed with an RSA 4096 / SHA-512 key (RHEL 9 rpm-sequoia compatible). The first install generates a random secret in /etc/teleproxy/config.toml; subsequent dnf upgrade runs swap the binary and never touch user-edited config; dnf remove leaves the config file in place. Built automatically on each tag from the existing static linux binaries via nfpm, driven by repository_dispatch from the release workflow into the new teleproxy/repo repository.

4.9.0

PROXY protocol v1/v2 listener support (#50) and per-IP top-N Prometheus metrics (#46).

  • PROXY protocol — accept HAProxy PROXY protocol v1 (text) and v2 (binary) headers on client listeners. Required when running behind a load balancer (HAProxy, nginx, AWS NLB) that injects client IP. Enable with --proxy-protocol (CLI), proxy_protocol = true (TOML), or PROXY_PROTOCOL=true (Docker).
  • Auto-detects v1 and v2 headers, extracts real client IP, re-checks IP ACLs against the real address.
  • v2 LOCAL command accepted for load balancer health check probes.
  • New Prometheus metrics: teleproxy_proxy_protocol_connections_total, teleproxy_proxy_protocol_errors_total.
  • Per-IP top-N metrics — opt-in top_ips_per_secret = N (TOML) or TOP_IPS_PER_SECRET=N (Docker) exposes the top-N heaviest client IPs per secret in /metrics. Three new families: teleproxy_secret_ip_connections, teleproxy_secret_ip_bytes_received_total, teleproxy_secret_ip_bytes_sent_total. Sorted by total bytes; cap at 32 per secret to keep Prometheus cardinality bounded. Default 0 (disabled, zero overhead). Useful for diagnosing the "proxy works for 5 minutes then stops" complaint pattern alongside the Grafana dashboard.
  • Fix auto-generated secret not written to TOML config — start.sh now correctly stores the generated secret in the TOML config.
  • Documentation: complete SEO overhaul with per-page meta descriptions, OpenGraph tags, JSON-LD structured data, and robots.txt.
  • Translations: Russian documentation now at 100% coverage, Farsi and Vietnamese expanded to 38%.
  • TON wallet added as a donation option alongside Tribute.

4.8.0

DC health probes (#47).

  • DC latency probes - periodic TCP handshake measurement to all 5 Telegram DCs, exposed as Prometheus histograms (teleproxy_dc_latency_seconds), failure counters, and last-latency gauges. Helps operators diagnose slow downloads and pick optimal DC routing.
  • Disabled by default. Enable with --dc-probe-interval 30 (CLI), dc_probe_interval = 30 (TOML), or DC_PROBE_INTERVAL=30 (Docker env).
  • Probes run in the master process only. Completion is tracked via non-blocking poll to preserve sub-millisecond accuracy.
  • Text stats endpoint includes per-DC latency, average, count, and failure fields.

4.7.0

Per-secret quotas, unique-IP limits, and expiration (#26).

  • Data quota — cap total bytes transferred per secret; active connections are closed and new ones rejected when exhausted. Configurable in bytes or human-readable sizes (quota = "10G")
  • Unique IP limit — cap how many distinct client IPs can use a secret simultaneously (max_ips = 5). Additional connections from an already-connected IP are always allowed
  • Secret expiration — auto-disable a secret after a timestamp (expires = 2025-12-31T23:59:59Z). Existing connections continue; only new ones are rejected
  • Per-reason rejection counters in Prometheus and plain-text stats (rejected_quota, rejected_ips, rejected_expired)
  • Docker env vars: SECRET_QUOTA_N, SECRET_MAX_IPS_N, SECRET_EXPIRES_N
  • SOCKS5 upstream proxy support (#22)
  • One-click cloud deploy page
  • Documentation: install/upgrade instructions, SOCKS5 docs, Observatory link

4.6.0

DPI resistance and operational improvements.

  • ServerHello size variation widened from ±1 to ±32 bytes, mimicking the natural variation in certificate chain and session ticket sizes seen from real TLS servers
  • ServerHello fragmentation: ServerHello and CCS+AppData are now sent as separate TCP segments, defeating DPI that pattern-matches the full handshake response in a single packet
  • Docker healthcheck respects custom STATS_PORT — previously hardcoded to 8888, now uses ${STATS_PORT:-8888} (#38)
  • install.sh supports multiple secrets via comma-separated SECRET or numbered SECRET_N variables
  • /link endpoint serves connection links as HTML pages with scannable QR codes

New documentation: DPI Resistance — covers server-side mitigations, recommended setup, and client-side bypass tools.

Client-side detection

The primary detection vector for MTProxy fake-TLS in Russia is the Telegram client's TLS fingerprint, which cannot be fixed server-side. Telegram Desktop fixed several fingerprint artifacts; keep clients updated. For affected networks, client-side bypass tools like zapret and GoodbyeDPI can help.

4.5.0

QR codes for connection links.

  • teleproxy link subcommand prints a proxy URL and renders a scannable QR code in the terminal using UTF-8 half-block characters
  • Docker start.sh and install.sh now display QR codes automatically at startup — point a phone camera at the screen to connect
  • Vendored nayuki/QR-Code-generator (MIT) for zero-dependency QR rendering on any platform
  • E2E tests decode the rendered QR output with pyzbar and verify it matches the expected URL
  • Documentation: new "Connection Links" page (en + ru)

4.4.0

  • teleproxy check diagnostic subcommand — validates configuration and tests connectivity before accepting clients. Checks DC reachability, NTP clock drift, fake-TLS domain probe, and SNI/DNS mismatch. Exit 0/1/2 for pass/fail/bad-args.

4.3.0

Direct mode connection resilience.

  • IPv6 auto-detection: probe at startup, enable without -6 if reachable
  • Multiple addresses per DC with synchronous failover on connect failure
  • Connection retry with exponential backoff (200ms–800ms, 3 attempts)
  • --dc-override dc_id:host:port to add or replace DC addresses (repeatable). Docker: DC_OVERRIDE=2:1.2.3.4:443,2:5.6.7.8:443
  • New stat: direct_dc_retries / teleproxy_direct_dc_retries_total

4.2.1

  • Fix aarch64 build: remove unused x86-only sys/io.h include
  • Add native ARM64 glibc build to CI (catches platform-specific issues masked by Alpine/musl)

4.2.0

  • --stats-allow-net CIDR flag to extend stats endpoint access beyond RFC1918 ranges (repeatable). Docker: STATS_ALLOW_NET=100.64.0.0/10,fd00::/8

4.1.0

MTProto transport protocol compliance improvements.

  • Detect and log transport error codes (-404, -429, etc.) from DCs in direct mode
  • Detect transport error codes in medium mode client parse path
  • Track quick ACK packets with teleproxy_quickack_packets_total counter
  • Track transport errors with teleproxy_transport_errors_total counter

4.0.0

Rebrand to Teleproxy. Binary renamed from mtproto-proxy to teleproxy.

  • Binary name: teleproxy (was mtproto-proxy)
  • Prometheus metrics prefix: teleproxy_ (was mtproxy_)
  • Docker user/paths: /opt/teleproxy/ (was /opt/mtproxy/)
  • Environment variables: TELEPROXY_* (old MTPROXY_* still accepted with deprecation warning)
  • Docker image includes backward-compat symlink mtproto-proxy -> teleproxy
  • CLI flags and behavior unchanged