v1.9.0

sleep3r/mtproto.zigv1.9.0Jun 15, 2026by sleep3r

Upgrading?

AI Summary

Adds two security/robustness features derived from external analysis of MTProxy: an optional kernel-level SYN rate limiter and a per-endpoint DC connection timeout.

Key Highlights

  • Adds optional kernel SYN rate limiting (mtbuddy setup syn-limit) to drop abusive connections before accept().
  • Adds a per-endpoint DC connect timeout (dc_connect_timeout_sec) to fail fast on dead endpoints.
  • Uses iptables (not nftables) to integrate with the existing stack.
  • Off by default with warnings about carrier-NAT/CGNAT.
  • Does not include other features from the analyzed library due to conflicts or redundancy.

New Features

  • Kernel SYN rate limiter with presets (soft, medium, hard).
  • Per-endpoint DC connect timeout.
  • Drop counter in mtbuddy status.
  • SYN limiter idempotent installation and full uninstall cleanup.

Full Release Notes

<details open>
<summary>🇷🇺 Что нового (RU)</summary>

## Коротко

`v1.9.0` добавляет два усиления, отобранных при разборе [MTproxy-reanimation](https://github.com/Liafanx/MTproxy-reanimation) (тюнинг-обвязка для Telemt/MTProxyMax). Из пяти её приёмов мы взяли только два — остальные мы уже делаем лучше или они конфликтуют с нашими инвариантами. Апгрейд безопасен: ограничение SYN — опциональное и по умолчанию **выключено**, а таймаут коннекта к DC — безопасный дефолт, на здоровые соединения не влияет.

## Ограничение SYN на уровне ядра (опционально) — `mtbuddy setup syn-limit`

- Per-IP ограничение входящих **SYN** на порту прокси через `iptables -m hashlimit`. Дропает абьюзные SYN-всплески **в ядре, до `accept()`** — там, где наши внутрипроксийные стражи слабее всего (flood guard и per-/24 лимитер работают **после** `accept()` и сами по умолчанию выключены, так что каждый абьюзный SYN сейчас всё равно стоит сокета и `accept()`).
- Ставится **отдельным** systemd-oneshot (`mtproto-syn-limit.service`), поэтому `CAP_NET_ADMIN` прокси **не выдаётся**.
- **По умолчанию выключено** и с громким предупреждением про carrier-NAT/CGNAT (та же проблема общего egress-IP, из-за которой выключены внутрипроксийные стражи). Пресеты: **soft** `2/с·burst 5` (дефолт при включении, безопаснее для CGNAT), **medium** `1/с·3`, **hard** `1/с·1`. Предупреждает, если включён `accept_proxy_protocol` (ядро видит IP балансировщика, а не клиента).
- Счётчик дропов в `mtbuddy status`; проверяет, что правило реально применилось (модуль `xt_hashlimit` на месте), а не молча превратилось в no-op. Идемпотентная установка + полная очистка при uninstall.
- Взяли **iptables** (а не nftables), чтобы лечь в существующий стек (TCPMSS, nfqws) — без новой зависимости.

```bash
sudo mtbuddy setup syn-limit --preset soft   # soft 2/с·5 | medium 1/с·3 | hard 1/с·1
sudo mtbuddy setup syn-limit --status
sudo mtbuddy setup syn-limit --remove
```

## Per-endpoint таймаут коннекта к DC — `dc_connect_timeout_sec` (дефолт 10с)

- Дедлайн на завершение TCP-`connect()` к **каждому** эндпоинту Telegram DC. Заблэкхоленный эндпоинт не шлёт RST, и ядро держит `SYN_SENT` ~2 минуты. `handshake_timeout_sec` (15с) уже ограничивает весь хендшейк — но **глобально**, поэтому медленный первый эндпоинт съедает бюджет failover для остальных.
- Теперь один мёртвый эндпоинт отбрасывается быстро, и failover переходит к следующему **в пределах** общего потолка `handshake_timeout_sec`.
- На здоровые коннекты не влияет (они завершаются за <1с); проверка привязана к фазе `.connecting_upstream`, поэтому **никогда не трогает установленный релей**.
- Сознательно **не** поднимали `handshake_timeout_sec` до больших значений — это расширило бы окно для active-probe/slow-loris, против которого заточены DD-логика и flood guard.

## Что НЕ взяли (и почему)

- **iOS keepalive sysctl (60/15/3):** для нас no-op — на established-сокетах мы уже ставим `SO_KEEPALIVE 60/10/3` + `TCP_USER_TIMEOUT=30s` (жёстче), а host-wide sysctl не достаёт pre-handshake сокеты без `SO_KEEPALIVE` (их и так жнут idle/handshake-таймауты).
- **iOS MSS=92 + отдельный порт с DNAT:** отдельный порт = вторая `tg://`-ссылка (наши ссылки иммутабельны после раздачи — твёрдое «нет»); MSS-половина конфликтует с нашим `TCPMSS=88`; и это folk-remedy для iOS-зависания, которое мы уже докопали до клиентского бага MtProtoKit (`bad_server_salt`) и закрыли `client_silence_close_sec` (+ апстрим-PR Telegram-iOS#2197).
- **Автодетект деплоя + персист правил в systemd:** у нас уже всё это есть для своего стека.

## Проверено

`zig build test` зелёный + кросс-сборки + install-матрица (debian 11/12, ubuntu 20.04/22.04/24.04) + E2E + bench. Изменение в ядре прокси байт-прозрачно и вне happy-path; firewall-фича опциональна и изолирована в своём unit. Документация обновлена во всех README (en/ru/zh/fa/vi) и THREAT_MODEL.

</details>

---

<details>
<summary>🇬🇧 Release notes (EN)</summary>

## TL;DR

`v1.9.0` adds two hardening features picked from analysing [MTproxy-reanimation](https://github.com/Liafanx/MTproxy-reanimation) (a tuning wrapper for Telemt/MTProxyMax). Of its five techniques we kept only two — the rest we already do better or they conflict with our invariants. Upgrading is safe: the SYN limiter is optional and **off by default**, and the DC connect timeout is a safe default that never affects healthy connections.

## Optional kernel SYN rate-limiter — `mtbuddy setup syn-limit`

- Per-source-IP inbound **SYN** rate-limit on the proxy port via `iptables -m hashlimit`. Drops abusive SYN bursts **in the kernel, before `accept()`** — exactly where our in-proxy guards are weakest (the flood guard and per-/24 limiter run **after** `accept()` and themselves default off, so every abusive SYN currently still costs a socket + an `accept()`).
- Installed as a **separate** systemd oneshot (`mtproto-syn-limit.service`), so `CAP_NET_ADMIN` is **never granted to the proxy**.
- **Off by default**, behind a loud carrier-NAT/CGNAT warning (the same shared-egress-IP problem that keeps the in-proxy guards off). Presets: **soft** `2/s·burst 5` (default when enabling, CGNAT-safer), **medium** `1/s·3`, **hard** `1/s·1`. Warns when `accept_proxy_protocol` is set (kernel sees the LB IP, not clients).
- Drop counter in `mtbuddy status`; verifies the rule actually landed (`xt_hashlimit` present) instead of a silent no-op. Idempotent apply + full uninstall cleanup.
- Uses **iptables** (not nftables) to match our existing stack (TCPMSS, nfqws) — no new dependency.

```bash
sudo mtbuddy setup syn-limit --preset soft   # soft 2/s·5 | medium 1/s·3 | hard 1/s·1
sudo mtbuddy setup syn-limit --status
sudo mtbuddy setup syn-limit --remove
```

## Per-endpoint DC connect timeout — `dc_connect_timeout_sec` (default 10s)

- A deadline for completing the TCP `connect()` to **each** Telegram DC endpoint. A filtered/black-holed endpoint sends no RST, so the kernel sits in `SYN_SENT` for ~2 min. `handshake_timeout_sec` (15s) already caps the whole handshake — but **globally**, so a slow first endpoint starves the failover budget for the rest.
- Now one dead endpoint is abandoned fast and failover advances to the next, **within** the overall `handshake_timeout_sec` ceiling.
- Never affects healthy connects (they finish in <1s); the check is gated on the `.connecting_upstream` phase, so it **can never touch an established relay**.
- Deliberately does **not** raise `handshake_timeout_sec` — that would widen the active-probe / slow-loris window our DD-decision and flood guards exist to shrink.

## Not ported (and why)

- **iOS keepalive sysctl (60/15/3):** a no-op for us — relay sockets already set `SO_KEEPALIVE 60/10/3` + `TCP_USER_TIMEOUT=30s` (stricter), and a host-wide sysctl can't touch pre-handshake sockets that lack `SO_KEEPALIVE` (already reaped by our idle/handshake timeouts).
- **iOS MSS=92 + separate port DNAT:** a second port = a second `tg://` link (our links are immutable once distributed — hard no); the MSS half conflicts with our `TCPMSS=88`; and it's a mechanism-free folk remedy for the iOS resume hang we already root-caused to the client MtProtoKit `bad_server_salt` bug and addressed with `client_silence_close_sec` (+ upstream PR Telegram-iOS#2197).
- **Deployment autodetection + systemd rule persistence:** already implemented for our own stack.

## Verified

`zig build test` green + cross-builds + install matrix (debian 11/12, ubuntu 20.04/22.04/24.04) + E2E + bench. The proxy-core change is byte-transparent and off the happy path; the firewall feature is opt-in and isolated in its own unit. Docs updated across all READMEs (en/ru/zh/fa/vi) and THREAT_MODEL.

</details>

---

**Full Changelog**: https://github.com/sleep3r/mtproto.zig/compare/v1.8.1...v1.9.0

Related Repositories