Skip to content

Commit 6c5b62e

Browse files
committed
v0.5.1: static musl builds for OpenWRT (amd64 + arm64)
A user on OpenWRT x86_64 reported the linux release doesn't run there — root cause was glibc vs musl mismatch (our gnu binary was looking for a dynamic linker that doesn't exist on router userlands). Add two musl targets that produce fully static PIE binaries: - x86_64-unknown-linux-musl -> mhrv-rs-linux-musl-amd64.tar.gz - aarch64-unknown-linux-musl -> mhrv-rs-linux-musl-arm64.tar.gz CI uses the messense/rust-musl-cross docker images (better-maintained than cargo-zigbuild with a pinned zig, which has version regressions on the ar wrapper between 0.13 and 0.16). Locally verified: - both archs cross-compile green in docker - resulting x86_64 binary (3.3 MB) runs in an alpine:latest container, --version / --help work, no dynamic lib requirements The musl archive skips the UI (routers are headless) and swaps run.sh for a procd init script (assets/openwrt/mhrv-rs.init) expecting the binary at /usr/bin/mhrv-rs and config at /etc/mhrv-rs/config.json. Side effect: switched tokio-rustls to default-features=false + ring (was pulling aws-lc-rs transitively, which can't easily cross-compile for musl). The main crate already uses ring explicitly, so no runtime behavior change. README gets a 'Running on OpenWRT (or any musl distro)' section in both English and Persian with scp + procd enable/start recipe. Closes #2.
1 parent d51b84a commit 6c5b62e

5 files changed

Lines changed: 136 additions & 64 deletions

File tree

.github/workflows/release.yml

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ jobs:
2929
- target: x86_64-pc-windows-gnu
3030
os: windows-latest
3131
name: mhrv-rs-windows-amd64
32+
- target: x86_64-unknown-linux-musl
33+
os: ubuntu-latest
34+
name: mhrv-rs-linux-musl-amd64
35+
- target: aarch64-unknown-linux-musl
36+
os: ubuntu-latest
37+
name: mhrv-rs-linux-musl-arm64
3238

3339
runs-on: ${{ matrix.os }}
3440

@@ -61,6 +67,7 @@ jobs:
6167
echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
6268
echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
6369
70+
6471
- name: Install Windows MinGW toolchain
6572
if: matrix.target == 'x86_64-pc-windows-gnu'
6673
id: msys2
@@ -80,13 +87,32 @@ jobs:
8087
Add-Content -Path $env:USERPROFILE/.cargo/config.toml -Value "linker = '$gcc'"
8188
8289
- name: Build CLI
90+
if: "!endsWith(matrix.target, '-linux-musl')"
8391
run: cargo build --release --target ${{ matrix.target }} --bin mhrv-rs
8492

93+
# Fully-static musl builds for OpenWRT / Alpine / libc-less systems.
94+
# messense/rust-musl-cross ships a pre-built musl toolchain so `ring`
95+
# (rustls' crypto backend) cross-compiles cleanly on both archs.
96+
- name: Build CLI (musl via docker)
97+
if: matrix.target == 'x86_64-unknown-linux-musl'
98+
run: |
99+
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD":/src -w /src \
100+
messense/rust-musl-cross:x86_64-musl \
101+
cargo build --release --target x86_64-unknown-linux-musl --bin mhrv-rs
102+
103+
- name: Build CLI (musl via docker, arm64)
104+
if: matrix.target == 'aarch64-unknown-linux-musl'
105+
run: |
106+
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD":/src -w /src \
107+
messense/rust-musl-cross:aarch64-musl \
108+
cargo build --release --target aarch64-unknown-linux-musl --bin mhrv-rs
109+
85110
# UI build: we try to build the UI binary on every platform. If it fails
86111
# on cross-compile for linux-arm64 (missing arm64 system libs cross),
87-
# we still ship the CLI.
112+
# we still ship the CLI. We also skip the UI on musl targets (OpenWRT etc.
113+
# are headless, bundling X11 makes no sense).
88114
- name: Build UI
89-
if: matrix.target != 'aarch64-unknown-linux-gnu'
115+
if: matrix.target != 'aarch64-unknown-linux-gnu' && !endsWith(matrix.target, '-linux-musl')
90116
run: cargo build --release --target ${{ matrix.target }} --features ui --bin mhrv-rs-ui
91117

92118
- name: Package (unix)
@@ -99,12 +125,22 @@ jobs:
99125
cp target/${{ matrix.target }}/release/mhrv-rs-ui dist/mhrv-rs-ui
100126
chmod +x dist/mhrv-rs-ui
101127
fi
102-
cp assets/launchers/run.sh dist/run.sh
103-
chmod +x dist/run.sh
104-
if [ "${{ runner.os }}" = "macOS" ]; then
105-
cp assets/launchers/run.command dist/run.command
106-
chmod +x dist/run.command
107-
fi
128+
# OpenWRT / musl archives get the procd init script instead of run.sh,
129+
# since routers don't have a CA to install and run headless via procd.
130+
case "${{ matrix.target }}" in
131+
*-linux-musl)
132+
cp assets/openwrt/mhrv-rs.init dist/mhrv-rs.init
133+
chmod +x dist/mhrv-rs.init
134+
;;
135+
*)
136+
cp assets/launchers/run.sh dist/run.sh
137+
chmod +x dist/run.sh
138+
if [ "${{ runner.os }}" = "macOS" ]; then
139+
cp assets/launchers/run.command dist/run.command
140+
chmod +x dist/run.command
141+
fi
142+
;;
143+
esac
108144
109145
- name: Build macOS .app bundle
110146
if: runner.os == 'macOS'
@@ -130,13 +166,20 @@ jobs:
130166
shell: bash
131167
run: |
132168
cd dist
133-
if [ "${{ runner.os }}" = "Windows" ]; then
134-
7z a -tzip "${{ matrix.name }}.zip" mhrv-rs.exe mhrv-rs-ui.exe run.bat
135-
elif [ "${{ runner.os }}" = "macOS" ]; then
136-
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh run.command
137-
else
138-
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh 2>/dev/null || tar czf "${{ matrix.name }}.tar.gz" mhrv-rs run.sh
139-
fi
169+
case "${{ matrix.target }}" in
170+
*-pc-windows-*)
171+
7z a -tzip "${{ matrix.name }}.zip" mhrv-rs.exe mhrv-rs-ui.exe run.bat
172+
;;
173+
*-apple-darwin)
174+
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh run.command
175+
;;
176+
*-linux-musl)
177+
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs.init
178+
;;
179+
*)
180+
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh 2>/dev/null || tar czf "${{ matrix.name }}.tar.gz" mhrv-rs run.sh
181+
;;
182+
esac
140183
141184
- uses: actions/upload-artifact@v4
142185
with:

Cargo.lock

Lines changed: 1 addition & 47 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mhrv-rs"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
edition = "2021"
55
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
66
license = "MIT"
@@ -24,7 +24,7 @@ ui = ["dep:eframe"]
2424

2525
[dependencies]
2626
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "time", "io-util", "signal", "sync"] }
27-
tokio-rustls = "0.26"
27+
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
2828
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
2929
rustls-pemfile = "2"
3030
webpki-roots = "0.26"

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,27 @@ Example config fragment (both UI and JSON):
211211

212212
HTTP/HTTPS continues to route through the Apps Script relay (no change), and the SNI-rewrite tunnel for `google.com` / `youtube.com` / etc. keeps bypassing both — so YouTube stays as fast as before while Telegram gets a real tunnel.
213213

214+
## Running on OpenWRT (or any musl distro)
215+
216+
The `*-linux-musl-*` archives ship a fully static CLI that runs on OpenWRT, Alpine, and any libc-less Linux userland. Put the binary on the router and start it as a service:
217+
218+
```sh
219+
# From a machine that can reach your router:
220+
scp mhrv-rs root@192.168.1.1:/usr/bin/mhrv-rs
221+
scp mhrv-rs.init root@192.168.1.1:/etc/init.d/mhrv-rs
222+
scp config.json root@192.168.1.1:/etc/mhrv-rs/config.json
223+
224+
# On the router:
225+
chmod +x /usr/bin/mhrv-rs /etc/init.d/mhrv-rs
226+
/etc/init.d/mhrv-rs enable
227+
/etc/init.d/mhrv-rs start
228+
logread -e mhrv-rs -f # tail its logs
229+
```
230+
231+
LAN devices then point their HTTP proxy at the router's LAN IP (default port `8085`) or their SOCKS5 at `<router-ip>:8086`. Set `listen_host` to `0.0.0.0` in `/etc/mhrv-rs/config.json` so the router accepts LAN connections instead of localhost-only.
232+
233+
Memory footprint is ~15-20 MB resident — fine on anything with ≥128 MB RAM. No UI is shipped for musl (routers are headless).
234+
214235
## Diagnostics
215236

216237
- **`mhrv-rs test`** — sends one request through the relay and reports success/latency. Use this first whenever something breaks — it isolates "relay is up" from "client config is wrong".
@@ -429,6 +450,27 @@ Firefox cert store خودش را جدا دارد؛ installer تلاش می‌ک
429450

430451
HTTP/HTTPS هیچ تغییری نمی‌کند (همچنان از Apps Script می‌رود) و تونل SNI-rewrite برای `google.com` / `youtube.com` / … هم سر جای خودش است — پس یوتوب مثل قبل سریع می‌ماند و تلگرام بالاخره یک تونل واقعی می‌گیرد.
431452

453+
### اجرا روی OpenWRT (یا هر سیستم musl)
454+
455+
آرشیوهای `*-linux-musl-*` یک CLI کاملاً static می‌دهند که روی OpenWRT، Alpine و هر userland لینوکسی بدون glibc اجرا می‌شود. باینری را روی روتر بگذارید و به‌عنوان سرویس راه بیندازید:
456+
457+
```sh
458+
# از یک ماشین که به روترتان می‌رسد:
459+
scp mhrv-rs root@192.168.1.1:/usr/bin/mhrv-rs
460+
scp mhrv-rs.init root@192.168.1.1:/etc/init.d/mhrv-rs
461+
scp config.json root@192.168.1.1:/etc/mhrv-rs/config.json
462+
463+
# روی خود روتر:
464+
chmod +x /usr/bin/mhrv-rs /etc/init.d/mhrv-rs
465+
/etc/init.d/mhrv-rs enable
466+
/etc/init.d/mhrv-rs start
467+
logread -e mhrv-rs -f
468+
```
469+
470+
بعدش دستگاه‌های LAN، proxy HTTP خودشان را روی IP روتر پورت `8085` (یا SOCKS5 روی `8086`) بگذارند. در `/etc/mhrv-rs/config.json` مقدار `listen_host` را به `0.0.0.0` تغییر دهید تا روتر از LAN هم connection بپذیرد (نه فقط localhost).
471+
472+
مصرف حافظه حدود ۱۵-۲۰ مگابایت است — روی هر روتری با حداقل ۱۲۸ مگابایت RAM اجرا می‌شود. UI برای musl ساخته نمی‌شود (روترها بدون صفحه‌نمایش هستند).
473+
432474
### محدودیت‌های شناخته‌شده
433475

434476
این‌ها محدودیت‌های ذاتی روش Apps Script + SNI هستند، نه باگ در این کلاینت. نسخهٔ اصلی پایتون هم دقیقاً همین‌ها را دارد.

assets/openwrt/mhrv-rs.init

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/sh /etc/rc.common
2+
# OpenWRT procd init script for mhrv-rs.
3+
# Install as /etc/init.d/mhrv-rs, then:
4+
# /etc/init.d/mhrv-rs enable
5+
# /etc/init.d/mhrv-rs start
6+
#
7+
# Expects:
8+
# /usr/bin/mhrv-rs (the static musl binary from the release)
9+
# /etc/mhrv-rs/config.json (your config)
10+
11+
START=99
12+
USE_PROCD=1
13+
14+
BIN=/usr/bin/mhrv-rs
15+
CONFIG=/etc/mhrv-rs/config.json
16+
17+
start_service() {
18+
[ -x "$BIN" ] || return 1
19+
[ -f "$CONFIG" ] || return 1
20+
21+
procd_open_instance
22+
procd_set_param command "$BIN" --config "$CONFIG" --no-cert-check
23+
procd_set_param respawn 3600 5 5
24+
procd_set_param stdout 1
25+
procd_set_param stderr 1
26+
procd_set_param file "$CONFIG"
27+
procd_close_instance
28+
}
29+
30+
reload_service() {
31+
stop
32+
start
33+
}

0 commit comments

Comments
 (0)