<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Proxy on dedinit()</title><link>https://dedinit.ru/tags/proxy/</link><description>Recent content in Proxy on dedinit()</description><generator>Hugo -- 0.165.0</generator><language>ru-ru</language><lastBuildDate>Mon, 31 Aug 2026 03:00:00 +0300</lastBuildDate><atom:link href="https://dedinit.ru/tags/proxy/index.xml" rel="self" type="application/rss+xml"/><item><title>WEB Proxy для Telegram Desktop: ставим tproxy-server на свой VPS и чиним грабли установщика</title><link>https://dedinit.ru/2026/tproxy-web-proxy-telegram-desktop/</link><pubDate>Mon, 31 Aug 2026 03:00:00 +0300</pubDate><guid>https://dedinit.ru/2026/tproxy-web-proxy-telegram-desktop/</guid><description>Рассказываю, как развернул WEB Proxy для Telegram Desktop на собственном VPS (tproxy-server + Caddy + MTProxy), что нашёл в установщике upstream и как починил падение mtproxy.service с правами на бинарь после сборки.</description><content:encoded><![CDATA[<figure class="entry-cover"><img src="https://dedinit.ru/2026/tproxy-web-proxy-telegram-desktop/hero.svg" alt="WEB Proxy для Telegram Desktop: Caddy, tproxy-server, MTProxy"></figure><h1 id="web-proxy-для-telegram-desktop-на-своём-vps">WEB Proxy для Telegram Desktop на своём VPS</h1>
<p>В моей инфраструктуре давно крутится Telegram-трафик через SOCKS5-туннель на VPS — но у этого подхода есть минусы: SOCKS5 легко режется DPI, а клиент надо настраивать отдельно. Альтернатива от самих разработчиков Telegram Desktop — <strong>WEB Proxy</strong>: прокси, который маскируется под обычный HTTPS-сайт на 443-м порту. Такой трафик не отличить от обычного захода на сайт, а подключение настраивается в два поля в настройках клиента.</p>
<p>В этой статье — как я развернул <a href="https://github.com/telegramdesktop/tproxy-server">tproxy-server</a> на свежем VPS (Debian 13, Hostkey), какие проблемы встретил в установщике и как их обошёл. Полезно всем, кто решит поднять свой WEB Proxy.</p>
<h2 id="зачем-web-proxy-а-не-socks5">Зачем WEB Proxy, а не SOCKS5</h2>
<table>
	<thead>
			<tr>
					<th>Критерий</th>
					<th>SOCKS5-туннель</th>
					<th>WEB Proxy (tproxy-server)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Внешний вид трафика</td>
					<td>SOCKS5 (легко детектится)</td>
					<td>Обычный HTTPS на 443</td>
			</tr>
			<tr>
					<td>Маскировка под сайт</td>
					<td>нет</td>
					<td>да (свой статический сайт)</td>
			</tr>
			<tr>
					<td>Настройка в Telegram Desktop</td>
					<td>тип прокси + адрес + порт</td>
					<td>hostname + secret</td>
			</tr>
			<tr>
					<td>Держит сам Telegram</td>
					<td>да</td>
					<td>да (через MTProxy на бэкенде)</td>
			</tr>
	</tbody>
</table>
<p>Схема работы tproxy-server:</p>
<pre tabindex="0"><code>Telegram Desktop → HTTPS:443 → Caddy → tproxy-server:8080 → MTProxy:2398 → Telegram
                                        └ admin :8081 (/readyz, /healthz)
</code></pre><p>Caddy отдаёт Let&rsquo;s Encrypt сертификат и проксирует весь трафик на tproxy-server, который сам решает: обычные запросы отдаёт сайт-маскировку, а запросы WEB Proxy — гонит в MTProxy. Снаружи это просто HTTPS-сайт.</p>
<h2 id="установка-что-делает-installsh">Установка: что делает install.sh</h2>
<p>У upstream отличный установщик — <code>deploy/install.sh</code> делает почти всё сам:</p>
<ul>
<li>ставит пакеты (ca-certificates, curl, nftables);</li>
<li>ставит <strong>Caddy 2.11.4</strong> и <strong>Go 1.26.5</strong> (если нет);</li>
<li>собирает <code>tproxy-server</code> из исходников;</li>
<li>собирает <strong>MTProxy</strong> (pinned commit <code>f36d8af</code>) в <code>/opt/MTProxy</code>;</li>
<li>создаёт systemd-сервисы: <code>tproxy-server</code>, <code>mtproxy</code>, <code>tproxy-firewall</code>, <code>caddy</code>;</li>
<li>настраивает таймер <code>refresh-mtproxy-config</code> (обновление списка серверов Telegram раз в сутки);</li>
<li>поднимает nft-правило, закрывающее порты MTProxy (2398, 8888) от внешнего мира.</li>
</ul>
<p>Запуск:</p>
<div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./deploy/install.sh --hostname vps03.nixg.ru <span style="color:#79c0ff">\
</span></span></span><span style="display:flex;"><span>  --email me@example.com --site-dir /tmp/tproxy-site <span style="color:#79c0ff">\
</span></span></span><span style="display:flex;"><span>  --secret <span style="color:#a5d6ff">&#34;</span><span style="color:#ff7b72">$(</span>cat /tmp/tproxy-secret.txt<span style="color:#ff7b72">)</span><span style="color:#a5d6ff">&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>--secret</code> — 32 hex-символа (<code>openssl rand -hex 16</code>), <code>--site-dir</code> — каталог со статическим сайтом-маскировкой.</p>
<h2 id="грабли-1-mtproxyservice-падает-с-203exec-readyz-503">Грабли №1: mtproxy.service падает с 203/EXEC, readyz 503</h2>
<p>После первого прогона установщика <code>tproxy-server</code> был active, Caddy слушал 80/443, но прокси не работал:</p>
<div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>● mtproxy.service - failed (Result: exit-code), status=203/EXEC
</span></span></code></pre></td></tr></table>
</div>
</div><p>Порт 2398 не слушался, а <code>/readyz</code> административного эндпоинта отдавал 503 (readyz — это «вся цепочка готова», healthz при этом был 200).</p>
<p>Причина оказалась в <strong>правах на бинарь после сборки</strong>. Установщик собирает MTProxy в <code>/opt/MTProxy/objs/bin/mtproto-proxy</code>, но из-за umask бинарь и каталоги получают права <code>700 root:root</code>. Юнит <code>mtproxy.service</code> запускается от пользователя <code>mtproxy</code> (с <code>NoNewPrivileges=true</code> и <code>ProtectSystem=strict</code>) — процесс не может прочитать бинарь и умирает с 203/EXEC.</p>
<p>Лечится одной серией команд:</p>
<div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chmod <span style="color:#a5d6ff">755</span> /opt/MTProxy /opt/MTProxy/objs /opt/MTProxy/objs/bin
</span></span><span style="display:flex;"><span>chown root:mtproxy /opt/MTProxy/objs/bin/mtproto-proxy
</span></span><span style="display:flex;"><span>chmod <span style="color:#a5d6ff">750</span> /opt/MTProxy/objs/bin/mtproto-proxy
</span></span><span style="display:flex;"><span>systemctl restart mtproxy
</span></span></code></pre></td></tr></table>
</div>
</div><p>После этого <code>mtproxy</code> — active (running), слушает <code>0.0.0.0:2398</code>, readyz → 200.</p>
<h2 id="грабли-2-флейк-go-test-в-установщике">Грабли №2: флейк go test в установщике</h2>
<p>Первый прогон install.sh (холодный кэш Go, сборка всех пакетов параллельно) упал на тестах:</p>
<div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>FAIL: internal/config — TestLoadAcceptsSystemdCredentialReadPermissions:
</span></span><span style="display:flex;"><span>group/other-readable profiles file outside a credential directory was accepted
</span></span></code></pre></td></tr></table>
</div>
</div><p>А повторный <code>go test ./...</code> (тёплый кэш) — PASS. Это классическая гонка при холодном параллельном прогоне тестов, а не баг кода. Лечение простое: перезапустить install.sh — дальше идёт нормально.</p>
<h2 id="грабли-3-не-открыты-порты-80443--ложная-тревога">Грабли №3: «не открыты порты 80/443» — ложная тревога</h2>
<p>Снаружи curl на 80/443 не отвечал, и я заподозрил firewall провайдера (Hostkey). Пошёл в панель — а там <strong>вообще нет настроек firewall/прокси</strong>. Полез в документацию, искал API&hellip;</p>
<p>На самом деле всё проще: порты у провайдера открыты по умолчанию, просто на 80/443 <strong>ничего не слушало</strong> — curl получал connection refused. Проверка: поднимаю временный <code>python3 -m http.server 80</code> → TCP connect снаружи OK. После установки Caddy сам слушает 80/443.</p>
<p>Так что если у вас после установки снаружи не открывается сайт — сначала проверьте, что порт слушается (<code>ss -tlnp</code>), а не ищите firewall в панели провайдера.</p>
<h2 id="проверка-после-установки">Проверка после установки</h2>
<div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>systemctl status tproxy-server mtproxy tproxy-firewall caddy   <span style="color:#8b949e;font-style:italic"># все active</span>
</span></span><span style="display:flex;"><span>curl -s -o /dev/null -w <span style="color:#a5d6ff">&#39;%{http_code}&#39;</span> http://127.0.0.1:8081/readyz    <span style="color:#8b949e;font-style:italic"># 200</span>
</span></span><span style="display:flex;"><span>curl -s -o /dev/null -w <span style="color:#a5d6ff">&#39;%{http_code}&#39;</span> https://vps03.nixg.ru/          <span style="color:#8b949e;font-style:italic"># 200 (сайт)</span>
</span></span><span style="display:flex;"><span>openssl s_client -connect vps03.nixg.ru:443 -servername vps03.nixg.ru  <span style="color:#8b949e;font-style:italic"># LE-серт</span>
</span></span><span style="display:flex;"><span>ss -tlnp | grep -E <span style="color:#a5d6ff">&#39;:80 |:443|:2398|:8080|:8081&#39;</span>
</span></span><span style="display:flex;"><span>nft list ruleset | grep -E <span style="color:#a5d6ff">&#39;2398|8888&#39;</span>   <span style="color:#8b949e;font-style:italic"># закрыто от внешнего мира</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Наружу видны только 80/443. MTProxy-бэкенд (2398, 8888) закрыт nft — <code>iifname != &quot;lo&quot; ... drop</code>, то есть бэкенд не светится и не принимает подключения напрямую.</p>
<h2 id="настройка-клиента">Настройка клиента</h2>
<p>В Telegram Desktop: <strong>Settings → Advanced → Connection Type → Use custom proxy → NEW PROXY → WEB Proxy</strong>:</p>
<ul>
<li>hostname: <code>vps03.nixg.ru</code></li>
<li>secret: (32 hex-символа, которые вы сгенерировали)</li>
</ul>
<h2 id="итог">Итог</h2>
<p>WEB Proxy — отличная замена SOCKS5-туннелю, если нужна маскировка под обычный HTTPS: настраивается в два поля, трафик неотличим от сайта, а готовый установщик upstream делает почти всю работу. Главные грабли — права на бинарь MTProxy после сборки (203/EXEC) и ложная тревога с «закрытыми портами». Оба решаются за пару минут, если знать, куда смотреть.</p>
<p>Полный набор файлов (заметки, сайт-маскировку, команду установки) я сложил в репозиторий <a href="https://github.com/telegramdesktop/tproxy-server">tproxy-web</a> — точнее, в свой <a href="https://gitverse.ru/kpa39l/tproxy-web">tproxy-web на gitverse</a>, а INSTALL_NOTES.md с деталями лежит там же.</p>
]]></content:encoded></item></channel></rss>