3.1 KiB
Tailscale / network-bind diagnostics — match the downloader (rd-diagnostics-mcp)
Goal: make the MHU read-only diagnostics reachable like the Real-Debrid-Downloader does over Tailscale — host embedded in the connection code, two bind modes (local / network), and a fail-closed IP allowlist as the access gate. No Tailscale auto-detection (the downloader has none); Tailscale is just one of the offered interface IPs reached over the tunnel, gated by the allowlist.
Plan
- lib/ip-allowlist.js — fail-closed allowlist (normalizeIp/::ffff:, isLoopback, ipv4ToInt, matchIpRule exact+CIDR+wildcard, evaluateClientAllowed: loopback always, empty=loopback-only). + unit tests.
- remote-server.js — accept config.allowlist; reject non-allowlisted peers at connection (close 4005). + protocol test.
- config-store.js — diagnostics subtree: bindMode ('local'), publicHost (''), allowlist ([]).
- main.js — bindMode->host (local=127.0.0.1, network=0.0.0.0, network requires non-empty allowlist); buildDiagnosticCode with host (h); getSuggestedRemoteHosts (os.networkInterfaces); pass allowlist; IPC save-settings/status.
- gateway/code.js — decode h/p/t/n/fp/s (tolerant of old port/token/label). + test.
- gateway/index.js — connect_server takes host from the code; host arg optional override.
- renderer/app.js — bind-mode selector, publicHost input + suggested-host chips, allowlist textarea (network), network-requires-allowlist validation.
- docs/remote-diagnostics-setup.md — network mode + allowlist + Tailscale (set allowlist to your tailnet, e.g. 100.64.0.0/10).
- Tests: ip-allowlist unit, remote-server allowlist protocol, gateway decode, integration (network bind + allowlist), adversarial fail-closed (empty allowlist rejects non-loopback).
- Release v3.3.86 (gitea + github mirror).
Review (done)
All steps implemented and verified. lib/ip-allowlist.js (fail-closed, ::ffff:, CIDR incl. 100.64.0.0/10) + 8 unit tests. remote-server.js rejects non-allowlisted peers (close 4005), opt-in via config.allowlist (existing remote-control unaffected) + 2 protocol tests (fail-closed wiring + loopback-always-allowed). Code now carries the host (mhu1_{v,h,p,t,n,fp?,s?}); gateway decode is tolerant of the legacy long keys; connect_server takes the host from the code (host arg optional override) — proven end-to-end by the integration harness connecting with NO host arg. Renderer: bind-mode selector + public-host input + suggested-host chips + allowlist textarea + network-requires-allowlist validation. Docs rewritten for Tailscale (set allowlist to the tailnet, put the Tailscale IP/MagicDNS in the code address). 393 app tests + 9 gateway tests + e2e + integration + adversarial all green, lint 0 errors.
Security model shift
v3.3.85 hard-locked loopback. This change replaces that with the downloader's model: network bind (0.0.0.0) is allowed ONLY with a non-empty fail-closed IP allowlist (empty => loopback only). The allowlist (real socket peer, ::ffff: normalized, CIDR) + token are the gate; the tunnel (Tailscale/WireGuard) is the confidentiality layer. Plaintext ws:// — document the trust boundary.