Multi-Hoster-Upload/tasks/todo.md
Administrator 8dff455062 docs(todo): Tailscale network-bind + fail-closed allowlist plan + review
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:35:38 +02:00

37 lines
3.1 KiB
Markdown

# 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.