v0.24.1

sleep3r/mtproto.zigv0.24.1May 19, 2026by sleep3r

Upgrading?

AI Summary

Fixes tunnel pre-start failures caused by strict Telegram HTTPS probe requirements or missing route tables.

Key Highlights

  • Tunnel pre-start no longer fails if the Telegram HTTPS probe is down.
  • Policy rule setup is now idempotent and handles existing rules gracefully.
  • First run no longer fails on an empty route table 200.

Full Release Notes

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

## Что решает этот релиз

`v0.24.1` - patch-релиз для операторов, которые настраивают AmneziaWG / WireGuard tunnel mode через `mtbuddy setup tunnel`.

В `v0.23.0` tunnel mode перешёл на явный contract `SO_MARK=200 -> table 200`: прокси помечает upstream sockets, а host policy routing отправляет только этот трафик через выбранный tunnel interface. Но в реальном install path нашёлся неприятный edge case: после успешной установки конфига туннеля `mtproto-proxy.service` мог не стартовать, потому что падал `ExecStartPre=/usr/local/bin/setup_tunnel.sh`.

Выглядело это так:

```text
Restarting proxy......
Job for mtproto-proxy.service failed because the control process exited with error code.
Proxy failed to start. Check: journalctl -u mtproto-proxy -n 30
```

Причин было две:

- Telegram HTTPS probe через `curl --interface awg0 https://core.telegram.org/getProxyConfig` считался обязательным условием старта. Если `core.telegram.org`, DNS или HTTPS probe недоступны, pre-start валил весь proxy service, даже когда policy route уже можно было поставить.
- На первом запуске table 200 ещё могла не существовать. Команда чтения предыдущего route возвращала non-zero, а `set -euo pipefail` завершал скрипт до выбора tunnel.

`v0.24.1` делает pre-start поведение более прагматичным: сервис стартует, если tunnel interface поднят, `awg/wg show` отвечает, policy rule установлен, а `ip route get ... mark 200` реально указывает на выбранный interface. Telegram probe остаётся полезной диагностикой, но больше не блокирует запуск прокси.

> [!NOTE]
> Конфиг менять не нужно. После обновления достаточно повторить `mtbuddy setup tunnel ...`, чтобы пересоздать `/usr/local/bin/setup_tunnel.sh` и systemd unit с исправленным поведением.

> [!TIP]
> Если сервер уже застрял на старом `ExecStartPre`, обновите `mtbuddy` до `v0.24.1` и заново выполните tunnel setup с тем же `.conf` или `vpn://` link.

## Что изменено

### Tunnel pre-start больше не зависит от Telegram HTTPS probe ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- `curl --interface <iface> https://core.telegram.org/getProxyConfig` больше не является fatal check.
- Tunnel считается пригодным, когда:
  - interface можно поднять через `awg-quick` / `wg-quick`;
  - `awg show <iface>` / `wg show <iface>` возвращает состояние;
  - route table 200 указывает default route на выбранный interface;
  - `ip -4 route get <telegram-dc-ip> mark 200` подтверждает `dev <iface>`.
- Если Telegram probe падает, controller пишет reason `policy route ready; Telegram probe failed` и логирует это как диагностику, не ломая `mtproto-proxy.service`.

### Policy rule setup стал идемпотентным ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- `fwmark 200 -> table 200` теперь ставится через explicit ensure-step.
- Старые rules с тем же `fwmark/table/priority` удаляются перед добавлением нового rule.
- После добавления rule скрипт отдельно проверяет, что `ip -4 rule show` действительно содержит нужный `fwmark` mapping.
- Это делает повторные запуски `setup_tunnel.sh`, timer failover и `systemctl restart mtproto-proxy` устойчивее к уже существующим rules.

### Первый запуск больше не падает на пустой table 200 ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- Чтение предыдущего default route из table 200 теперь допускает отсутствие самой FIB table.
- Пустая table 200 больше не завершает script из-за `set -euo pipefail`.
- Route всё равно создаётся через `ip -4 route replace default dev <iface> table 200` после выбора tunnel.

### Installer e2e теперь покрывает этот сценарий ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- В installer e2e добавлен tunnel regression test.
- Тест подставляет fake `awg` / `awg-quick`, создаёт dummy/veth `awg0` и специально ломает только `curl --interface awg0`.
- Проверяется, что `mtbuddy setup tunnel`:
  - успешно рестартит `mtproto-proxy`;
  - оставляет `mtproto-tunnel-pool.timer` active/enabled;
  - пишет `active=awg0` и `status=healthy` в pool state;
  - создаёт `fwmark 200 -> table 200 -> dev awg0`;
  - не считает падение Telegram probe фатальным.
- Debug dump installer e2e теперь сохраняет tunnel rules/routes/state для будущих разборов.

## Проверено

- `bash -n test/installer-e2e/run.sh`
- `zig build test`
- `MTPROTO_INSTALLER_E2E_IMAGES='debian:12' test/installer-e2e/run.sh`

</details>

---

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

## What this release addresses

`v0.24.1` is a patch release for operators configuring AmneziaWG / WireGuard tunnel mode through `mtbuddy setup tunnel`.

`v0.23.0` moved tunnel mode to an explicit `SO_MARK=200 -> table 200` contract: the proxy marks upstream sockets, and host policy routing sends only that traffic through the selected tunnel interface. A real install-path edge case showed up afterwards: tunnel config installation could succeed, but `mtproto-proxy.service` failed to start because `ExecStartPre=/usr/local/bin/setup_tunnel.sh` exited non-zero.

It looked like this:

```text
Restarting proxy......
Job for mtproto-proxy.service failed because the control process exited with error code.
Proxy failed to start. Check: journalctl -u mtproto-proxy -n 30
```

There were two root causes:

- The Telegram HTTPS probe through `curl --interface awg0 https://core.telegram.org/getProxyConfig` was treated as a startup requirement. If `core.telegram.org`, DNS, or that HTTPS probe was unavailable, pre-start failed the whole proxy service even when the policy route could be installed.
- On first run, table 200 may not exist yet. Reading the previous route returned non-zero, and `set -euo pipefail` terminated the script before tunnel selection.

`v0.24.1` makes pre-start behavior more pragmatic: the service starts when the tunnel interface is up, `awg/wg show` works, the policy rule is installed, and `ip route get ... mark 200` actually resolves through the selected interface. The Telegram probe remains useful diagnostics, but it no longer blocks proxy startup.

> [!NOTE]
> No config changes are required. After updating, rerun `mtbuddy setup tunnel ...` to regenerate `/usr/local/bin/setup_tunnel.sh` and the systemd unit with the fixed behavior.

> [!TIP]
> If a server is already stuck on the old `ExecStartPre` failure, update `mtbuddy` to `v0.24.1` and rerun tunnel setup with the same `.conf` or `vpn://` link.

## What changed

### Tunnel pre-start no longer depends on the Telegram HTTPS probe ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- `curl --interface <iface> https://core.telegram.org/getProxyConfig` is no longer a fatal check.
- A tunnel is considered usable when:
  - the interface can be brought up with `awg-quick` / `wg-quick`;
  - `awg show <iface>` / `wg show <iface>` returns state;
  - route table 200 points its default route at the selected interface;
  - `ip -4 route get <telegram-dc-ip> mark 200` confirms `dev <iface>`.
- If the Telegram probe fails, the controller writes reason `policy route ready; Telegram probe failed` and logs it as diagnostics without breaking `mtproto-proxy.service`.

### Policy rule setup is idempotent ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- `fwmark 200 -> table 200` is now installed through an explicit ensure step.
- Stale rules with the same `fwmark/table/priority` are removed before adding the new rule.
- After adding the rule, the script verifies that `ip -4 rule show` contains the expected `fwmark` mapping.
- This makes repeated `setup_tunnel.sh` runs, timer failover, and `systemctl restart mtproto-proxy` more robust when rules already exist.

### First run no longer fails on an empty table 200 ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- Reading the previous default route from table 200 now tolerates the FIB table not existing yet.
- An empty table 200 no longer exits the script via `set -euo pipefail`.
- The route is still created with `ip -4 route replace default dev <iface> table 200` after tunnel selection.

### Installer e2e now covers this scenario ([#269](https://github.com/sleep3r/mtproto.zig/pull/269))

- Added a tunnel regression test to installer e2e.
- The test injects fake `awg` / `awg-quick`, creates a dummy/veth `awg0`, and deliberately fails only `curl --interface awg0`.
- It verifies that `mtbuddy setup tunnel`:
  - restarts `mtproto-proxy` successfully;
  - leaves `mtproto-tunnel-pool.timer` active/enabled;
  - writes `active=awg0` and `status=healthy` to pool state;
  - creates `fwmark 200 -> table 200 -> dev awg0`;
  - does not treat the Telegram probe failure as fatal.
- Installer e2e debug dumps now include tunnel rules/routes/state for future investigations.

## Verified

- `bash -n test/installer-e2e/run.sh`
- `zig build test`
- `MTPROTO_INSTALLER_E2E_IMAGES='debian:12' test/installer-e2e/run.sh`

</details>

## Changelog

* fix: tunnel prestart policy routing ([#269](https://github.com/sleep3r/mtproto.zig/pull/269)) ([7c62b30](https://github.com/sleep3r/mtproto.zig/commit/7c62b30b744c620dab916e92a89ab07fd3e70062))
* chore(main): release 0.24.1 ([#270](https://github.com/sleep3r/mtproto.zig/pull/270)) ([48df22f](https://github.com/sleep3r/mtproto.zig/commit/48df22f))

Related Repositories