Skip to content

Commit 6185b73

Browse files
committed
feat(proxy): support explicit LAN sharing modes
Add first-class inbound proxy authentication for non-loopback proxy binds while preserving the frictionless trusted-LAN workflow as an explicit persisted opt-in. The configuration model now supports proxy_auth credentials for HTTP Basic and SOCKS5 username/password authentication, plus allow_unauthenticated_lan for private home networks and personal hotspots where the operator intentionally wants open LAN access. Enforce the selected mode at runtime. HTTP proxy requests require Proxy-Authorization when credentials are configured and return 407 when authentication is missing or invalid. SOCKS5 negotiation selects RFC 1929 username/password authentication when configured, rejects clients that do not offer it, and keeps no-auth negotiation available when the operator has opted into unauthenticated LAN sharing. If allow_unauthenticated_lan is true, runtime auth is disabled even if stale credentials remain in a hand-edited config. Extend validation and TOML migration coverage so non-loopback binds fail unless they are protected by complete credentials or explicitly marked as open LAN. Partial credentials are rejected to avoid ambiguous exposure states, and TOML tests cover both authenticated and open LAN configurations. Update the desktop UI to expose LAN sharing as a real workflow instead of disabling it. The UI now lets users toggle sharing, choose authenticated or open trusted-LAN mode, enter proxy credentials, see the detected LAN endpoint, and receive a persistent-mode warning when switching between authenticated and open LAN behavior. Saving the form makes the selected mode the default for future launches, and switching back plus saving restores the safer authenticated posture. Refresh the example TOML files and English/Persian guide sections with concrete LAN-sharing configuration snippets, client setup notes, and warnings about quota exposure on shared networks.
1 parent 03731e5 commit 6185b73

10 files changed

Lines changed: 568 additions & 56 deletions

config.direct.example.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@ mode = "direct"
44
[network]
55
google_ip = "216.239.38.120"
66
front_domain = "www.google.com"
7-
# Non-loopback binds such as 0.0.0.0 are rejected until inbound proxy authentication is implemented.
7+
# Local-only by default. For LAN sharing, set listen_host = "0.0.0.0" and
8+
# either configure [network.proxy_auth] or set allow_unauthenticated_lan = true
9+
# for a trusted home LAN/personal hotspot.
810
listen_host = "127.0.0.1"
11+
allow_unauthenticated_lan = false
912
listen_port = 8085
1013
socks5_port = 8086
1114
verify_ssl = true
1215

16+
# [network.proxy_auth]
17+
# username = "mhrv"
18+
# password = "CHANGE_ME"
19+
1320
[network.hosts]
1421

1522
[scan]

config.example.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ auth_key = "CHANGE_ME_TO_A_STRONG_SECRET"
66
[network]
77
google_ip = "216.239.38.120"
88
front_domain = "www.google.com"
9-
# Non-loopback binds such as 0.0.0.0 are rejected until inbound proxy authentication is implemented.
9+
# Local-only by default. For LAN sharing, set listen_host = "0.0.0.0" and
10+
# either configure [network.proxy_auth] or set allow_unauthenticated_lan = true
11+
# for a trusted home LAN/personal hotspot.
1012
listen_host = "127.0.0.1"
13+
allow_unauthenticated_lan = false
1114
listen_port = 8085
1215
socks5_port = 8086
1316
verify_ssl = true
1417

18+
# [network.proxy_auth]
19+
# username = "mhrv"
20+
# password = "CHANGE_ME"
21+
1522
[network.hosts]
1623

1724
[scan]

config.exit-node.example.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ auth_key = "PUT_YOUR_APPS_SCRIPT_AUTH_KEY_HERE"
99
[network]
1010
google_ip = "216.239.38.120"
1111
front_domain = "www.google.com"
12-
# Non-loopback binds such as 0.0.0.0 are rejected until inbound proxy authentication is implemented.
12+
# Local-only by default. For LAN sharing, set listen_host = "0.0.0.0" and
13+
# either configure [network.proxy_auth] or set allow_unauthenticated_lan = true
14+
# for a trusted home LAN/personal hotspot.
1315
listen_host = "127.0.0.1"
16+
allow_unauthenticated_lan = false
1417
listen_port = 8085
1518
socks5_port = 8086
1619
verify_ssl = true
1720

21+
# [network.proxy_auth]
22+
# username = "mhrv"
23+
# password = "CHANGE_ME"
24+
1825
[network.hosts]
1926

2027
[scan]

config.fronting-groups.example.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@ mode = "direct"
44
[network]
55
google_ip = "216.239.38.120"
66
front_domain = "www.google.com"
7-
# Non-loopback binds such as 0.0.0.0 are rejected until inbound proxy authentication is implemented.
7+
# Local-only by default. For LAN sharing, set listen_host = "0.0.0.0" and
8+
# either configure [network.proxy_auth] or set allow_unauthenticated_lan = true
9+
# for a trusted home LAN/personal hotspot.
810
listen_host = "127.0.0.1"
11+
allow_unauthenticated_lan = false
912
listen_port = 8085
1013
socks5_port = 8086
1114
verify_ssl = true
1215

16+
# [network.proxy_auth]
17+
# username = "mhrv"
18+
# password = "CHANGE_ME"
19+
1320
[network.hosts]
1421

1522
[scan]

config.full.example.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ auth_key = "CHANGE_ME_TO_A_STRONG_SECRET"
66
[network]
77
google_ip = "216.239.38.120"
88
front_domain = "www.google.com"
9-
# Non-loopback binds such as 0.0.0.0 are rejected until inbound proxy authentication is implemented.
9+
# Local-only by default. For LAN sharing, set listen_host = "0.0.0.0" and
10+
# either configure [network.proxy_auth] or set allow_unauthenticated_lan = true
11+
# for a trusted home LAN/personal hotspot.
1012
listen_host = "127.0.0.1"
13+
allow_unauthenticated_lan = false
1114
listen_port = 8085
1215
socks5_port = 8086
1316
verify_ssl = true
1417

18+
# [network.proxy_auth]
19+
# username = "mhrv"
20+
# password = "CHANGE_ME"
21+
1522
[network.hosts]
1623

1724
[scan]

docs/guide.fa.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,36 @@ HTTP / HTTPS مثل قبل از Apps Script می‌رود (تغییری نمی
269269

270270
## اشتراک‌گذاری هات‌اسپات
271271

272-
mhrv-rs حالا به‌طور پیش‌فرض فقط روی `127.0.0.1` گوش می‌دهد و تا وقتی احراز هویت HTTP/SOCKS برای ورودی پیاده‌سازی نشده باشد، bind غیر loopback را رد می‌کند. این کار جلوی open proxy ناخواسته و مصرف شدن quota Apps Script توسط دستگاه‌های دیگر شبکه را می‌گیرد.
272+
mhrv-rs به‌طور پیش‌فرض فقط روی `127.0.0.1` گوش می‌دهد، یعنی فقط برنامه‌های همان دستگاه می‌توانند از proxy استفاده کنند. برای اشتراک‌گذاری با گوشی، تبلت، لپ‌تاپ یا روتر خانه، `listen_host` را به `0.0.0.0` تغییر بده و یکی از دو حالت LAN را انتخاب کن:
273273

274-
اشتراک‌گذاری هات‌اسپات/LAN بعداً به‌صورت یک حالت صریح و دارای احراز هویت برمی‌گردد. در نسخهٔ فعلی `listen_host` را به `0.0.0.0` تغییر نده؛ اعتبارسنجی کانفیگ fail-closed می‌شود. workflow قدیمی این بود:
274+
- **اشتراک‌گذاری LAN با احراز هویت**: `[network.proxy_auth]` را تنظیم کن. کلاینت HTTP باید `Proxy-Authorization: Basic ...` بفرستد و کلاینت SOCKS5 باید username/password داشته باشد. این حالت برای Wi-Fi مشترک، خوابگاه، محل کار، کتابخانه، و هر هات‌اسپاتی که همهٔ دستگاه‌های وصل‌شده‌اش دست خودت نیستند پیشنهاد می‌شود.
275+
- **اشتراک‌گذاری LAN باز و مطمئن**: `allow_unauthenticated_lan = true` را بگذار. این همان workflow راحت قدیمی برای LAN خانه یا هات‌اسپات شخصی است. هر دستگاهی که به پورت proxy برسد می‌تواند از quota Apps Script و تونل تو استفاده کند، پس روی شبکهٔ مشترک روشنش نکن.
276+
277+
UI هر دو حالت را نشان می‌دهد. بعد از Save، همان حالت برای اجراهای بعدی پیش‌فرض می‌شود؛ هر وقت خواستی به حالت امن‌تر برگردی، گزینه را خاموش کن و دوباره Save بزن.
278+
279+
نمونهٔ TOML:
280+
281+
```toml
282+
[network]
283+
listen_host = "0.0.0.0"
284+
listen_port = 8085
285+
socks5_port = 8086
286+
allow_unauthenticated_lan = false
287+
288+
[network.proxy_auth]
289+
username = "home"
290+
password = "CHANGE_ME"
291+
```
292+
293+
برای خانه/هات‌اسپات خصوصی بدون credential:
294+
295+
```toml
296+
[network]
297+
listen_host = "0.0.0.0"
298+
allow_unauthenticated_lan = true
299+
```
300+
301+
workflow پایهٔ هات‌اسپات:
275302

276303
۱. **اندروید:** هات‌اسپات موبایل را روشن کن + اپ را استارت کن
277304
۲. **دستگاه دیگر:** به Wi-Fi هات‌اسپات اندروید وصل شو
@@ -289,7 +316,7 @@ Settings → Wi-Fi → روی (i) شبکهٔ هات‌اسپات بزن → Conf
289316

290317
HTTP proxy سیستم را روی `192.168.43.1:8080` بگذار، یا per-app SOCKS5 روی `192.168.43.1:1081`.
291318

292-
> گیت امنیتی فعلی: مقدارهای غیر loopback مثل `0.0.0.0`، `::` یا IP شبکهٔ LAN تا زمان اضافه شدن احراز هویت proxy رد می‌شوند.
319+
اگر LAN با احراز هویت روشن است، username/password تنظیم‌شده را در تنظیمات proxy کلاینت وارد کن. اگر کلاینت فقط proxy بدون احراز هویت را پشتیبانی می‌کند، حالت LAN باز را فقط روی شبکه‌ای روشن کن که کنترلش دست خودت است.
293320

294321
## اجرا روی OpenWRT
295322

@@ -308,7 +335,7 @@ chmod +x /usr/bin/mhrv-rs /etc/init.d/mhrv-rs
308335
logread -e mhrv-rs -f # تمام لاگ
309336
```
310337

311-
نسخهٔ فعلی فقط روی loopback گوش می‌دهد. اجرای CLI روی OpenWRT برای تست محلی همچنان کار می‌کند، اما استفاده از روتر به‌عنوان proxy برای کل LAN به حالت authenticated LAN-sharing آینده نیاز دارد. در این نسخه `listen_host` را در `/etc/mhrv-rs/config.toml` به `0.0.0.0` تغییر نده؛ اعتبارسنجی کانفیگ این bind ناامن را رد می‌کند.
338+
OpenWRT می‌تواند proxy کل LAN باشد: در `/etc/mhrv-rs/config.toml` مقدار `listen_host = "0.0.0.0"` را بگذار. اگر افراد دیگر هم می‌توانند به LAN وصل شوند، `[network.proxy_auth]` را تنظیم کن. برای شبکهٔ خصوصی خانه که عمداً رفتار بدون رمز قدیمی را می‌خواهی، `allow_unauthenticated_lan = true` را بگذار.
312339

313340
مصرف حافظه ~۱۵–۲۰ مگابایت — روی هر روتری با ۱۲۸ مگابایت RAM به بالا اجرا می‌شود. UI روی musl نیست (روترها headlessاند).
314341

docs/guide.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,36 @@ The destination sees the exit node's IP, not Google's, so the anti-bot heuristic
269269

270270
## Sharing via hotspot
271271

272-
mhrv-rs listens on `127.0.0.1` by default and rejects non-loopback proxy binds until inbound HTTP/SOCKS authentication is implemented. This prevents accidental open-proxy exposure and Apps Script quota theft on shared Wi-Fi or hotspots.
272+
mhrv-rs listens on `127.0.0.1` by default, so only apps on the same device can use the HTTP/SOCKS proxy. To share the proxy with another phone, tablet, laptop, or a home router, switch the bind address to `0.0.0.0` and choose one of two persisted LAN modes:
273273

274-
Hotspot/LAN sharing will return as an explicit authenticated mode in a later release. On current builds, do not change `listen_host` to `0.0.0.0`; startup validation will fail closed. The old sharing workflow was:
274+
- **Authenticated LAN sharing**: configure `[network.proxy_auth]`. HTTP clients must send `Proxy-Authorization: Basic ...`; SOCKS5 clients must use username/password authentication. This is the recommended mode on shared Wi-Fi, dorm networks, offices, libraries, and any hotspot where you do not fully control every connected device.
275+
- **Open trusted-LAN sharing**: set `allow_unauthenticated_lan = true`. This keeps the old frictionless hotspot workflow for a trusted home LAN or personal hotspot. Anyone who can reach the proxy port can use your Apps Script quota and tunnel, so leave this off on shared networks.
276+
277+
The UI exposes both modes. After you save, the selected mode becomes the default for future launches; switch it back and save again whenever you need to return to the safer authenticated mode.
278+
279+
TOML example:
280+
281+
```toml
282+
[network]
283+
listen_host = "0.0.0.0"
284+
listen_port = 8085
285+
socks5_port = 8086
286+
allow_unauthenticated_lan = false
287+
288+
[network.proxy_auth]
289+
username = "home"
290+
password = "CHANGE_ME"
291+
```
292+
293+
For a private home/hotspot setup without credentials:
294+
295+
```toml
296+
[network]
297+
listen_host = "0.0.0.0"
298+
allow_unauthenticated_lan = true
299+
```
300+
301+
Basic hotspot workflow:
275302

276303
1. **Android:** enable mobile hotspot + start the app
277304
2. **Other device:** connect to the Android hotspot Wi-Fi
@@ -289,7 +316,7 @@ For full device-wide coverage on iOS, use [Shadowrocket](https://apps.apple.com/
289316

290317
Set system HTTP proxy to `192.168.43.1:8080`, or per-app SOCKS5 to `192.168.43.1:1081`.
291318

292-
> Current safety gate: non-loopback values such as `0.0.0.0`, `::`, or a LAN IP are rejected until proxy authentication is available.
319+
If authenticated LAN sharing is enabled, enter the configured username/password in the client proxy settings. If the client only supports unauthenticated proxies, use open trusted-LAN mode only on a network you control.
293320

294321
## Running on OpenWRT
295322

@@ -308,7 +335,7 @@ chmod +x /usr/bin/mhrv-rs /etc/init.d/mhrv-rs
308335
logread -e mhrv-rs -f # tail logs
309336
```
310337

311-
Current builds listen on loopback only. Running the CLI on OpenWRT for local diagnostics still works, but using the router as a LAN-wide proxy requires the upcoming authenticated LAN-sharing mode. Do not set `listen_host` to `0.0.0.0` in `/etc/mhrv-rs/config.toml` on this version; config validation will reject the unsafe bind.
338+
OpenWRT can act as a LAN-wide proxy by setting `listen_host = "0.0.0.0"` in `/etc/mhrv-rs/config.toml`. Use `[network.proxy_auth]` when other people can join the LAN. For a private home network where you intentionally want the old no-password behavior, set `allow_unauthenticated_lan = true`.
312339

313340
Memory footprint ~15–20 MB resident — fine on anything ≥128 MB RAM. No UI on musl (routers are headless).
314341

0 commit comments

Comments
 (0)