Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RouterDesk

Self-hosted RustDesk ID/relay server (hbbs + hbbr) running on a stock ASUSWRT-Merlin router - no PC, NAS, VPS or Docker needed. One command installs the server, firewall rules, boot autostart, a daily self-updater, a crash watchdog, an SSH management TUI and a native page in the router's own web interface. The router is the natural edge box for this job: it is already always-on, it owns the WAN connection, and because the server runs on the router the ports are plain INPUT firewall rules on the router itself - not port-forwards to yet another machine that has to stay awake behind it.

Verified platform: ASUS RT-BE92U (aarch64, ASUSWRT-Merlin 3006.102.x). Any Merlin router with Entware and a supported CPU should work - see Requirements.

Why self-host instead of TeamViewer or AnyDesk? Both are closed-source, so you can't audit what happens to your session data, and their commercial-use licensing has caught more than one user off guard - by default every connection also routes through their infrastructure. RouterDesk flips that: open-source top to bottom, self-hosted on hardware you already own, no account, no subscription, no third party in the data path.

Features

  • One-click installer - single self-contained POSIX sh script, curl | sh safe (nothing executes until the last line, so a truncated download cannot half-run). Safe to re-run: server key, peer DB and settings are preserved.
  • Conf-driven services - Entware init.d services read /opt/rustdesk/rustdesk.conf; change settings without editing scripts.
  • Daily self-update at 05:00 with run-test, health check and automatic rollback to the previous binaries if the new ones do not come up.
  • Crash watchdog every 5 minutes - restarts dead daemons, respects an intentional rustdesk stop, and halts itself after 3 consecutive failed restarts instead of hammering a broken install.
  • Native router webUI page - shows up in the router's own web interface under the left-menu Addons section (scMerlin-style mount, with icon): status badges, start/stop/restart, update-now, settings form, log viewer, public-key copy button.
  • TUI + CLI control script - rustdesk with no arguments is an interactive menu over SSH; every action is also a scriptable subcommand.
  • Key enforcement (-k _) - only clients presenting your server's public key may register or use the relay, blocking open-relay abuse.
  • CGNAT detection - the installer checks whether the router really has a public IPv4 and warns honestly when remote IPv4 access cannot work.
  • IPv4 + IPv6 firewall automation - idempotent INPUT rules (not port-forwards), with optional ip6tables mirroring.
  • Relay bandwidth tuning knobs - optional hbbr per-connection / total bandwidth caps via the conf file.
  • Key regeneration - rustdesk regen-key mints a new server identity, keeping timestamped backups of the old key.
  • Clean uninstall - removes everything it installed while preserving the server identity (key + peer DB) unless you ask for --purge.

Requirements

  • ASUS router running ASUSWRT-Merlin 3006.x with an aarch64 (arm64v8) or armv7 CPU - rustdesk-server publishes binaries for these two only.
  • Entware installed on a USB drive (via amtm; the drive must stay plugged in - services live under /opt).
  • JFFS custom scripts enabled: Administration > System > "Enable JFFS custom scripts and configs" = Yes.
  • Disk: the server itself needs ~10 MB under /opt; keep a few tens of MB free for the backup generation and update staging.
  • RAM: 512 MB or more recommended. Below that the installer warns and strongly advises adding a swap file of 1 GB+ first (amtm > sw); routers with 512 MB-1 GB also benefit from swap for headroom.
  • A DDNS hostname that resolves directly to the router (plain A and optionally AAAA record - myrouter.asuscomm.com or your own domain). No Cloudflare proxy / HTTP CDN: RustDesk speaks raw TCP/UDP on the 2111x ports.
  • The native webUI page additionally needs addon-API firmware support (am_addons, present on current Merlin builds); the installer skips it gracefully where unsupported.

Install

Quick (recommended)

SSH into the router as the admin user and run:

curl -fsSL https://raw.githubusercontent.com/medking82/RouterDesk/main/install.sh | sh

The installer prompts interactively (it reads from /dev/tty, so prompts work fine under curl | sh): relay/ID hostname, key enforcement, IPv6 rules, webUI page. Prefer to read before you pipe? Download install.sh, inspect it, then sh install.sh - identical result.

For non-interactive runs, pre-seed the answers via environment variables:

Variable Meaning Default
RD_RELAY_HOST Relay/ID hostname handed to clients (your DDNS name) prompted (required)
RD_ENFORCE_KEY Y/N - require the server key to register/relay Y
RD_ENABLE_IPV6 Y/N - also open the ports via ip6tables Y when IPv6 is active
RD_WEBUI Y/N - install the native webUI page Y when firmware supports it
RD_LOWRAM_OK Y - proceed on a <512 MB router without asking unset (prompts)
curl -fsSL https://raw.githubusercontent.com/medking82/RouterDesk/main/install.sh \
  | RD_RELAY_HOST=ddns.example.com RD_ENFORCE_KEY=Y RD_ENABLE_IPV6=N RD_WEBUI=Y sh

On re-runs, current settings become the prompt defaults; binaries and scripts are refreshed, key/DB/conf are preserved.

Manual

install.sh is fully self-contained (every router file ships inside it as an embedded payload), so only the one script needs to reach the router:

git clone https://github.com/medking82/RouterDesk.git
scp RouterDesk/install.sh admin@192.168.50.1:/tmp/
ssh admin@192.168.50.1
sh /tmp/install.sh

(Use your router admin username and LAN IP.) Prefer to see what it automates, step by step? Read docs/runbook.md - the full manual walkthrough, useful for porting to non-Merlin routers.

Client setup

On every RustDesk client (controller and controlled): Settings > Network > unlock > ID/Relay Server:

Field Value
ID server ddns.example.com
Relay server ddns.example.com
API server (leave blank - OSS)
Key <your server public key>

Where to find the key: it is printed at the end of the install, shown by rustdesk status on the router, available via the webUI page's copy button, or cat /opt/rustdesk/id_ed25519.pub. After applying, the client's status dot should go green ("Ready").

Management

CLI

rustdesk <command>
Command What it does
start / stop / restart Manage hbbs (ID server) + hbbr (relay)
status Status report; exit code 0 = both running
status-web Machine-readable KEY=VALUE status (webUI backend; always exit 0)
logs [hbbs|hbbr|update] Show last log lines (default: both services)
update Run the self-updater now
watchdog Cron backend: restart daemons that died
regen-key [--yes] New server identity; old key kept as timestamped .bak files
set-relay <host> Change the relay/ID host, restart hbbs
set-key-enforce Y|N Set key enforcement (-k _), restart services
set-relay-always Y|N Set ALWAYS_USE_RELAY (skip hole-punch), restart hbbs
set-ipv6 Y|N Set ENABLE_IPV6 and apply/remove the live ip6tables rules
toggle-key Flip key enforcement (shorthand)
toggle-relay-always Flip ALWAYS_USE_RELAY (shorthand)
uninstall [--purge] [--yes] Remove the server (--purge also deletes key/DB/conf)

rustdesk stop writes an intentional-stop marker so the watchdog leaves the daemons alone; start/restart (and a reboot) clear it.

TUI

rustdesk with no arguments opens an interactive menu (status view plus numbered actions for everything above).

webUI

If installed, the router web interface gains a native Addons > RustDesk page (re-login once if it does not appear immediately): live hbbs/hbbr status badges, listening ports, installed version, the server public key with a copy button, start/stop/restart/update buttons, a settings form (relay host, key enforcement, always-relay, IPv6) and a log viewer. It is a thin shell over the same rustdesk control script, wired through the stock Merlin addon mechanism (see docs/gui-addon-design.md).

Configuration

Everything lives in /opt/rustdesk/rustdesk.conf (sourced by the services; edit via the rustdesk setters, or by hand + rustdesk restart):

Key Meaning Default
RELAY_HOST Hostname handed to clients as the relay (-r <host>:21117) set at install
ENFORCE_KEY Y = run hbbs/hbbr with -k _ (key required) Y
ENABLE_IPV6 Y = maintain ip6tables INPUT rules asked at install
ALWAYS_USE_RELAY Y = tell clients to skip hole-punching, go straight to relay N
RD_ARCH Release asset tag used by the updater (arm64v8/armv7) detected
SINGLE_BANDWIDTH Optional: hbbr per-connection cap, Mb/s unset (upstream 128)
TOTAL_BANDWIDTH Optional: hbbr whole-relay cap, Mb/s unset (upstream 1024)
LIMIT_SPEED Optional: hbbr throttle for downgraded connections, Mb/s unset (upstream 32)
DOWNGRADE_THRESHOLD Optional: hbbr downgrade trigger ratio unset (upstream 0.66)

The four bandwidth knobs are advanced hbbr tuning: leave them unset and hbbr uses its upstream defaults; set a value and rustdesk restart to override.

Updating

A daily cron job (05:00) runs /opt/rustdesk/update.sh; rustdesk update runs the same thing on demand. It resolves the newest rustdesk-server tag via the GitHub releases/latest redirect (no API, no rate limit), and does nothing further if the installed version matches. On a new release it downloads the right zip for your CPU, run-tests the new binaries (with a patchelf fallback for odd libcs), backs up the running ones to *.bak, hot-swaps, restarts, then health-checks that the ports are listening - and rolls back to the *.bak binaries automatically if they are not. History lands in /opt/rustdesk/update.log; the installed tag in /opt/rustdesk/VERSION. Your server key and peer DB are never touched by updates - clients keep working. (The RustDesk client apps update themselves independently.)

CGNAT - the honest section

If your ISP puts you behind CGNAT (no public IPv4 on the router's WAN), then remote access over IPv4 is impossible no matter which ports you open - there is no public address for clients to reach. The installer detects this (WAN IP in private/CGNAT ranges, or WAN IP differing from an external echo) and says so instead of letting you debug a config that can never work. Your real options:

  1. IPv6 end-to-end - if both the router and your remote clients have working IPv6, publish an AAAA record and enable the IPv6 rules (next section). Many CGNAT'd ISPs and most mobile carriers do provide IPv6.
  2. Ask your ISP for a public IPv4 - often a paid add-on.
  3. LAN/VPN only - keep the server private and reach it through a VPN into your LAN (e.g. WireGuard), then connect as if local.

IPv6

Answering yes to IPv6 (or rustdesk set-ipv6 Y) sets ENABLE_IPV6=Y and maintains ip6tables INPUT rules for the same ports; the firewall block re-reads the setting on every firewall restart, so it survives reboots and firmware firewall reloads. hbbs/hbbr already listen dual-stack.

The DNS half is yours: publish an AAAA record for your DDNS name pointing at the router's global IPv6 address (some DDNS providers update AAAA automatically, others need a custom script). Once both halves are in place, clients with working IPv6 - including phones on CGNAT'd mobile carriers - can connect direct over IPv6 even when your IPv4 is unreachable.

Security notes

What this exposes on the WAN (as router INPUT rules, not forwards):

Port Proto Service Open on WAN Purpose
21115 TCP hbbs yes NAT type test
21116 TCP + UDP hbbs yes ID registration / heartbeat / hole-punch
21117 TCP hbbr yes Relay traffic
21118 / 21119 TCP hbbs / hbbr no Web client (listening, not firewall-opened)
  • Keep key enforcement ON (the default). With -k _, only clients presenting your server's public key can register or relay - without it, any internet stranger can discover and use your relay for their own traffic.
  • Keys and DB stay on the router. /opt/rustdesk/id_ed25519 is the private key and never leaves the box; id_ed25519.pub is what you hand to clients; db_v2.sqlite3 holds registered peers. Back up id_ed25519* and db_v2.sqlite3 off-router - they are excluded from this repo by design, and losing the key means reconfiguring every client against a new one.
  • webUI data sits behind the router admin session. The page and its status files are served by the router's httpd inside the authenticated UI; nothing secret enters the web directory (public key, process state, log tails only).

Known trade-offs

  • Relay sessions cap at ~30 fps by upstream RustDesk design (rustdesk/rustdesk discussion #11804). Direct P2P sessions are not capped; this only bites when traffic falls back to the relay.
  • Relay traffic rides your home upload. Fine for occasional support sessions; sustained HD streaming through hbbr is limited by your uplink (see the bandwidth knobs above if you need to cap it).
  • Phone clients have decode ceilings. Streaming a 4K desktop to a phone can be limited by the phone's H.265/HEVC decode capability - a client-device limit, not a server one.
  • The 05:00 updater restarts a manually-stopped server when a new release lands (an update implies "should run", and it clears the stop marker). This is documented behavior, not a bug: if you want the server down long-term, uninstall it or remove the RustDeskUpdate cron job (cru d RustDeskUpdate).

Uninstall

sh /opt/rustdesk/uninstall.sh           # or: rustdesk uninstall
sh /opt/rustdesk/uninstall.sh --purge   # also delete key, peer DB and conf

Without --purge it removes services, cron jobs, firewall rules, the control script and the webUI page, but keeps the server identity (id_ed25519/.pub), the peer DB and your settings - re-running install.sh later picks them up unchanged and your clients keep working. --purge deletes those too; every client would need repointing afterwards. Only the marker blocks this installer added to /jffs/scripts/* are stripped - your own lines in those files are untouched.

Repo layout

Paths mirror where files land on the router:

install.sh                   one-click installer; embeds every file below as a payload
uninstall.sh                 clean removal            -> /opt/rustdesk/uninstall.sh
opt/etc/init.d/S98hbbr       hbbr init.d service      -> /opt/etc/init.d/S98hbbr
opt/etc/init.d/S99hbbs       hbbs init.d service      -> /opt/etc/init.d/S99hbbs
opt/rustdesk/update.sh       daily self-updater       -> /opt/rustdesk/update.sh
opt/bin/rustdesk             control script (TUI/CLI) -> /opt/bin/rustdesk
webui/rustdesk.asp           native webUI page        -> /jffs/addons/rustdesk/rustdesk.asp
webui/mount-webui.sh         webUI mount/glue         -> /jffs/addons/rustdesk/mount-webui.sh
jffs/scripts/*               reference copies of the firewall/boot hooks the
                             installer writes as marker blocks on the live router
docs/runbook.md              the full manual walkthrough (what install.sh automates)
docs/gui-addon-design.md     webUI addon architecture + integration contract
scripts/                     payload regen/verify tooling (see scripts/README.md)

install.sh is generated-in-place: the embedded heredoc payloads are byte-copies of the real repo files. Edit the source file, then re-embed and verify with the tools in scripts/ - never hand-edit a payload inside install.sh.

Credits

  • RustDesk and rustdesk-server - the excellent open-source remote desktop this installs.
  • scMerlin and the AMTM-OSR / JackYaz addon family - the native webUI mount patterns (menuTree handling, page slots, addon locking) this project's webUI page follows.
  • everstu/Koolcenter_rustdesk
    • prior art: a RustDesk server addon for the koolshare ASUS ecosystem.

License

MIT

About

One-click self-hosted RustDesk ID/relay server for ASUSWRT-Merlin routers - your router IS the server, no PC/NAS/VPS needed. Installer + native Merlin webUI Addon page.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages