Skip to content

Commit 6c8a236

Browse files
committed
1 parent f6ce49a commit 6c8a236

8 files changed

Lines changed: 114 additions & 5 deletions

File tree

changelog/index.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,17 @@
14651465
</label>
14661466
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
14671467

1468+
<li class="md-nav__item">
1469+
<a href="#4141" class="md-nav__link">
1470+
<span class="md-ellipsis">
1471+
1472+
4.14.1
1473+
1474+
</span>
1475+
</a>
1476+
1477+
</li>
1478+
14681479
<li class="md-nav__item">
14691480
<a href="#4140" class="md-nav__link">
14701481
<span class="md-ellipsis">
@@ -1724,6 +1735,17 @@
17241735
</label>
17251736
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
17261737

1738+
<li class="md-nav__item">
1739+
<a href="#4141" class="md-nav__link">
1740+
<span class="md-ellipsis">
1741+
1742+
4.14.1
1743+
1744+
</span>
1745+
</a>
1746+
1747+
</li>
1748+
17271749
<li class="md-nav__item">
17281750
<a href="#4140" class="md-nav__link">
17291751
<span class="md-ellipsis">
@@ -1943,6 +1965,11 @@
19431965

19441966

19451967
<h1 id="changelog">Changelog<a class="headerlink" href="#changelog" title="Permanent link">&para;</a></h1>
1968+
<h2 id="4141">4.14.1<a class="headerlink" href="#4141" title="Permanent link">&para;</a></h2>
1969+
<ul>
1970+
<li><strong>Fix macOS backend-forward path</strong>. 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: <code>writev()</code> to a freshly opened outbound socket whose <code>connect()</code> was still in <code>EINPROGRESS</code> returns <code>ENOTCONN</code> on BSD/Darwin where Linux returns <code>EAGAIN</code>, and the proxy treated anything other than <code>EAGAIN</code>/<code>EINTR</code> as fatal — tearing down the connection before the SYN-ACK arrived. <code>ENOTCONN</code> 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 (<code>test_wrong_secret_rejected</code>, <code>test_stale_timestamp_rejected</code>, <code>test_unknown_sni_falls_back</code>, <code>test_duplicate_client_random_rejected</code>, <code>test_browser_tls_sees_real_backend</code>) now pass on both targets. The bug never manifested in production because production runs Linux.</li>
1971+
<li><strong>Disable knob for the v4.14.0 MSS clamp</strong>. New <code>mss_clamp = false</code> TOML key, <code>--no-mss-clamp</code> CLI flag, and <code>MSS_CLAMP=false</code> 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 <a href="/features/dpi-resistance/#forced-clienthello-fragmentation-automatic">Forced ClientHello fragmentation</a> for what's being turned off.</li>
1972+
</ul>
19461973
<h2 id="4140">4.14.0<a class="headerlink" href="#4140" title="Permanent link">&para;</a></h2>
19471974
<ul>
19481975
<li><strong>Automatic ClientHello fragmentation against TSPU JA4 fingerprinting</strong> (<a href="https://github.com/teleproxy/teleproxy/issues/39">#39</a>). Russian DPI (TSPU/ASBI) classifies traffic to fake-TLS MTProxies by JA4-hashing the <em>client's</em> 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 <code>signature_algorithms</code> — 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 <code>/stats</code> / <code>/metrics</code> listener keeps full system MSS. Pairs with the existing <a href="/features/dpi-resistance/#serverhello-response-variation">ServerHello segmentation</a> so <em>both</em> sides of the handshake become unfriendly to single-packet DPI. Genuinely novel — neither <code>mtg</code> nor <code>telemt</code> 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 <a href="/features/dpi-resistance/#forced-clienthello-fragmentation-automatic">Forced ClientHello fragmentation</a> section for the mechanism and rationale.</li>

fa/changelog/index.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,17 @@
14651465
</label>
14661466
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
14671467

1468+
<li class="md-nav__item">
1469+
<a href="#4141" class="md-nav__link">
1470+
<span class="md-ellipsis">
1471+
1472+
4.14.1
1473+
1474+
</span>
1475+
</a>
1476+
1477+
</li>
1478+
14681479
<li class="md-nav__item">
14691480
<a href="#4140" class="md-nav__link">
14701481
<span class="md-ellipsis">
@@ -1724,6 +1735,17 @@
17241735
</label>
17251736
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
17261737

1738+
<li class="md-nav__item">
1739+
<a href="#4141" class="md-nav__link">
1740+
<span class="md-ellipsis">
1741+
1742+
4.14.1
1743+
1744+
</span>
1745+
</a>
1746+
1747+
</li>
1748+
17271749
<li class="md-nav__item">
17281750
<a href="#4140" class="md-nav__link">
17291751
<span class="md-ellipsis">
@@ -1943,6 +1965,11 @@
19431965

19441966

19451967
<h1 id="changelog">Changelog<a class="headerlink" href="#changelog" title="Permanent link">&para;</a></h1>
1968+
<h2 id="4141">4.14.1<a class="headerlink" href="#4141" title="Permanent link">&para;</a></h2>
1969+
<ul>
1970+
<li><strong>Fix macOS backend-forward path</strong>. 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: <code>writev()</code> to a freshly opened outbound socket whose <code>connect()</code> was still in <code>EINPROGRESS</code> returns <code>ENOTCONN</code> on BSD/Darwin where Linux returns <code>EAGAIN</code>, and the proxy treated anything other than <code>EAGAIN</code>/<code>EINTR</code> as fatal — tearing down the connection before the SYN-ACK arrived. <code>ENOTCONN</code> 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 (<code>test_wrong_secret_rejected</code>, <code>test_stale_timestamp_rejected</code>, <code>test_unknown_sni_falls_back</code>, <code>test_duplicate_client_random_rejected</code>, <code>test_browser_tls_sees_real_backend</code>) now pass on both targets. The bug never manifested in production because production runs Linux.</li>
1971+
<li><strong>Disable knob for the v4.14.0 MSS clamp</strong>. New <code>mss_clamp = false</code> TOML key, <code>--no-mss-clamp</code> CLI flag, and <code>MSS_CLAMP=false</code> 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 <a href="/features/dpi-resistance/#forced-clienthello-fragmentation-automatic">Forced ClientHello fragmentation</a> for what's being turned off.</li>
1972+
</ul>
19461973
<h2 id="4140">4.14.0<a class="headerlink" href="#4140" title="Permanent link">&para;</a></h2>
19471974
<ul>
19481975
<li><strong>Automatic ClientHello fragmentation against TSPU JA4 fingerprinting</strong> (<a href="https://github.com/teleproxy/teleproxy/issues/39">#39</a>). Russian DPI (TSPU/ASBI) classifies traffic to fake-TLS MTProxies by JA4-hashing the <em>client's</em> 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 <code>signature_algorithms</code> — 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 <code>/stats</code> / <code>/metrics</code> listener keeps full system MSS. Pairs with the existing <a href="/features/dpi-resistance/#serverhello-response-variation">ServerHello segmentation</a> so <em>both</em> sides of the handshake become unfriendly to single-packet DPI. Genuinely novel — neither <code>mtg</code> nor <code>telemt</code> 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 <a href="/features/dpi-resistance/#forced-clienthello-fragmentation-automatic">Forced ClientHello fragmentation</a> section for the mechanism and rationale.</li>

fa/features/dpi-resistance/index.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1931,7 +1931,14 @@ <h3 id="serverhello">تنوع پاسخ ServerHello<a class="headerlink" href="#s
19311931
<h3 id="clienthello">قطعه‌قطعه‌سازی اجباری ClientHello (خودکار)<a class="headerlink" href="#clienthello" title="Permanent link">&para;</a></h3>
19321932
<p>Teleproxy یک TCP MSS کوچک (۲۵۶ بایت) را در SYN-ACK روی پورت گوش‌دهنده عمومی پروکسی اعلام می‌کند. هسته کلاینت از این محدودیت پیروی می‌کند و ClientHello خروجی (~۵۰۰-۷۰۰ بایت) را به ۲-۳ بخش TCP تقسیم می‌کند. فهرست رمزها، افزونه‌ها، الگوریتم‌های امضا و ALPN در مرز بخش‌ها قرار می‌گیرند — DPI که JA4 را از یک بسته محاسبه می‌کند تنها بخشی از داده‌های موردنیاز را می‌بیند.</p>
19331933
<p>به صورت خودکار کار می‌کند، نیازی به پیکربندی ندارد، و در برابر کلاینت‌های Telegram اصلاح‌نشده روی هر سکویی عمل می‌کند. شنونده HTTP <code>/stats</code> / <code>/metrics</code> از MSS پیش‌فرض سیستم استفاده می‌کند و تحت تأثیر قرار نمی‌گیرد.</p>
1934-
<p>تعادل: لینوکس MSS سوکت شنونده را به هر دو جهت هر اتصال پذیرفته‌شده اعمال می‌کند، بنابراین بخش‌های سرور→کلاینت نیز در ۲۵۶ بایت محدود می‌شوند. تعداد بسته‌ها حدود ۵ برابر افزایش می‌یابد و سربار سرآیند TCP/IP از ~۳٪ به ~۱۵٪ می‌رسد. روی سخت‌افزار مدرن با TSO/GSO، هزینه CPU قابل مدیریت است، اما پروکسی‌هایی که پهنای باند را اشباع می‌کنند کاهش قابل اندازه‌گیری توان عملیاتی را می‌بینند. به طور پیش‌فرض قطعه‌قطعه‌سازی فعال است — برای بیشتر کاربران، یک پروکسی کار‌کننده اما کندتر بهتر از یک پروکسی سریع مسدودشده است.</p>
1934+
<p>تعادل: لینوکس MSS سوکت شنونده را به هر دو جهت هر اتصال پذیرفته‌شده اعمال می‌کند، بنابراین بخش‌های سرور→کلاینت نیز در ۲۵۶ بایت محدود می‌شوند. تعداد بسته‌ها حدود ۵ برابر افزایش می‌یابد و سربار سرآیند TCP/IP از ~۳٪ به ~۱۵٪ می‌رسد. روی سخت‌افزار مدرن با TSO/GSO، هزینه CPU قابل مدیریت است، اما پروکسی‌هایی که پهنای باند را اشباع می‌کنند کاهش قابل اندازه‌گیری توان عملیاتی را می‌بینند.</p>
1935+
<p><strong>نحوه غیرفعال‌سازی.</strong> اپراتورهایی که ترجیح می‌دهند ریسک شناسایی JA4 را به جای سربار توان عملیاتی بپذیرند می‌توانند کلامپ را غیرفعال کنند:</p>
1936+
<ul>
1937+
<li>TOML: <code>mss_clamp = false</code> (کلید سطح بالا)</li>
1938+
<li>CLI: <code>--no-mss-clamp</code></li>
1939+
<li>Docker / <code>start.sh</code>: <code>MSS_CLAMP=false</code> (یا <code>MSS_CLAMP=0</code>)</li>
1940+
</ul>
1941+
<p>هر یک از این روش‌ها MSS اعلام‌شده در SYN-ACK را به مقدار پیش‌فرض سیستم بازمی‌گرداند — هر اتصال پذیرفته‌شده در هر دو جهت یک MSS عادی دریافت می‌کند و پروکسی ترافیک حجیم MTProto را با سرعت کامل حمل می‌کند. به طور پیش‌فرض قطعه‌قطعه‌سازی فعال باقی می‌ماند — برای بیشتر کاربران، یک پروکسی کار‌کننده اما کمی کندتر بهتر از یک پروکسی سریع اما مسدودشده است.</p>
19351942
<h3 id="grease">تصادفی‌سازی GREASE<a class="headerlink" href="#grease" title="Permanent link">&para;</a></h3>
19361943
<p>هر ClientHello (برای کاوش دامنه بالادست) از مقادیر GREASE تازه طبق RFC 8701 استفاده می‌کند و از تطبیق اثر انگشت ثابت جلوگیری می‌کند.</p>
19371944
<h2 id="_2">شما چه می‌توانید انجام دهید (راه‌اندازی سرور)<a class="headerlink" href="#_2" title="Permanent link">&para;</a></h2>

features/dpi-resistance/index.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,14 @@ <h3 id="serverhello-response-variation">ServerHello response variation<a class="
19531953
<h3 id="forced-clienthello-fragmentation-automatic">Forced ClientHello fragmentation (automatic)<a class="headerlink" href="#forced-clienthello-fragmentation-automatic" title="Permanent link">&para;</a></h3>
19541954
<p>Teleproxy announces a small TCP Maximum Segment Size (256 bytes) in the SYN-ACK on its public listening port. The client kernel obeys the limit and chops the outgoing ClientHello (~500-700 bytes) into 2-3 TCP segments. Critically, ALPN and <code>signature_algorithms</code> — 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.</p>
19551955
<p>This is automatic, requires no configuration, and works against unmodified Telegram clients on every platform. The HTTP <code>/stats</code> and <code>/metrics</code> listener uses the full system MSS and is unaffected.</p>
1956-
<p>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× compared to MSS=1460 and per-byte TCP/IP header overhead grows from ~3% to ~15%. On modern hardware with TSO/GSO offload, CPU cost stays manageable, and a 20MB MTProto download still completes well inside any sensible timeout. Bandwidth-saturated proxies will see a measurable throughput cap; heavy operators who would rather take the JA4 detection risk than the overhead can rebuild without the <code>SM_LOWMSS</code> bit — but the default ships with fragmentation on because for most users a working proxy that runs slower beats a fast proxy that gets blocked.</p>
1956+
<p>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× compared to MSS=1460 and per-byte TCP/IP header overhead grows from ~3% to ~15%. On modern hardware with TSO/GSO offload, CPU cost stays manageable, and a 20MB MTProto download still completes well inside any sensible timeout. Bandwidth-saturated proxies will see a measurable throughput cap.</p>
1957+
<p><strong>Turning it off.</strong> Operators who would rather take the JA4 detection risk than the throughput overhead can disable the clamp:</p>
1958+
<ul>
1959+
<li>TOML: <code>mss_clamp = false</code> (top-level key)</li>
1960+
<li>CLI: <code>--no-mss-clamp</code></li>
1961+
<li>Docker / <code>start.sh</code>: <code>MSS_CLAMP=false</code> (or <code>MSS_CLAMP=0</code>)</li>
1962+
</ul>
1963+
<p>Any one of these turns the SYN-ACK MSS announcement back to system default — every accepted connection gets a normal-size MSS in both directions, and the proxy carries bulk MTProto at full speed. The default remains on, because for most users a working proxy that runs slightly slower beats a fast proxy that gets blocked.</p>
19571964
<h3 id="grease-randomization">GREASE randomization<a class="headerlink" href="#grease-randomization" title="Permanent link">&para;</a></h3>
19581965
<p>Each ClientHello (for upstream domain probing) uses fresh GREASE values per RFC 8701, preventing static fingerprint matching.</p>
19591966
<h2 id="what-you-can-do-server-setup">What You Can Do (Server Setup)<a class="headerlink" href="#what-you-can-do-server-setup" title="Permanent link">&para;</a></h2>

0 commit comments

Comments
 (0)