P2P App Store Emancipation: - secubox-p2p: Package distribution via mesh peers (CGI API, RPCD, CLI) - packages.js: LuCI view with LOCAL/PEER badges, fetch/install actions - devstatus.js: Dev Status widget with Gitea commits, v1.0 progress tracking - secubox-feed: sync-content command for auto-installing content packages - ACL fix for P2P feed RPCD methods Remote Access: - secubox-app-rustdesk: Native hbbs/hbbr relay server from GitHub releases - secubox-app-guacamole: LXC Debian container with guacd + Tomcat (partial) Content Distribution: - secubox-content-pkg: Auto-package Metablogizer/Streamlit as IPKs - Auto-publish hooks in metablogizerctl and streamlitctl Mesh Media: - secubox-app-ksmbd: In-kernel SMB3 server with ksmbdctl CLI - Pre-configured shares for Jellyfin, Lyrion, Backup UI Consistency: - client-guardian: Ported to sh-page-header chip layout - auth-guardian: Ported to sh-page-header chip layout Fixes: - services.js: RPC expect unwrapping bug fix - metablogizer: Chunked upload for uhttpd 64KB limit Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
409 lines
9.8 KiB
Bash
409 lines
9.8 KiB
Bash
#!/bin/sh
|
|
# SecuBox RustDesk Server Manager
|
|
|
|
CONFIG="rustdesk"
|
|
VERSION="1.1.15"
|
|
DOWNLOAD_URL="https://github.com/rustdesk/rustdesk-server/releases/download/${VERSION}"
|
|
DATA_PATH_DEFAULT="/srv/rustdesk"
|
|
HBBS_PID=""
|
|
HBBR_PID=""
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: rustdeskctl <command>
|
|
|
|
Commands:
|
|
install Download RustDesk server binaries
|
|
uninstall Remove binaries (preserves key)
|
|
status Show server status and public key
|
|
keygen Regenerate authentication key
|
|
logs [-f] Show server logs
|
|
configure-firewall Open WAN ports (21115-21117)
|
|
mesh-register Register with P2P mesh
|
|
service-run Internal: run servers via procd
|
|
service-stop Internal: stop servers
|
|
USAGE
|
|
}
|
|
|
|
# ---------- helpers ----------
|
|
|
|
require_root() { [ "$(id -u)" -eq 0 ]; }
|
|
|
|
uci_get() {
|
|
local key="$1"
|
|
local section="${2:-server}"
|
|
uci -q get ${CONFIG}.${section}.$key
|
|
}
|
|
|
|
uci_set() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local section="${3:-server}"
|
|
uci set ${CONFIG}.${section}.$key="$value"
|
|
}
|
|
|
|
log_info() { echo "[INFO] $*"; logger -t rustdeskctl "$*"; }
|
|
log_error() { echo "[ERROR] $*" >&2; logger -t rustdeskctl -p err "$*"; }
|
|
|
|
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
|
|
|
defaults() {
|
|
data_path="$(uci_get data_path || echo $DATA_PATH_DEFAULT)"
|
|
id_port="$(uci_get id_port || echo 21116)"
|
|
relay_port="$(uci_get relay_port || echo 21117)"
|
|
}
|
|
|
|
detect_arch() {
|
|
case "$(uname -m)" in
|
|
aarch64) echo "arm64v8" ;;
|
|
armv7l) echo "armv7" ;;
|
|
x86_64) echo "amd64" ;;
|
|
*) echo "amd64" ;;
|
|
esac
|
|
}
|
|
|
|
# ---------- installation ----------
|
|
|
|
cmd_install() {
|
|
require_root || { log_error "Must run as root"; return 1; }
|
|
defaults
|
|
|
|
local arch=$(detect_arch)
|
|
local zip_file="/tmp/rustdesk-server.zip"
|
|
local zip_url="${DOWNLOAD_URL}/rustdesk-server-linux-${arch}.zip"
|
|
|
|
log_info "Downloading RustDesk server v${VERSION} for ${arch}..."
|
|
|
|
ensure_dir "$data_path"
|
|
cd "$data_path" || return 1
|
|
|
|
# Download
|
|
wget -q -O "$zip_file" "$zip_url" || {
|
|
log_error "Failed to download from $zip_url"
|
|
return 1
|
|
}
|
|
|
|
# Extract
|
|
unzip -o -q "$zip_file" -d "$data_path" || {
|
|
log_error "Failed to extract archive"
|
|
rm -f "$zip_file"
|
|
return 1
|
|
}
|
|
rm -f "$zip_file"
|
|
|
|
# Find and move binaries (they may be in a subdirectory)
|
|
# Check common subdirectory patterns from RustDesk releases
|
|
for subdir in arm64v8 aarch64-unknown-linux-gnu amd64 x86_64-unknown-linux-gnu armv7; do
|
|
if [ -d "$data_path/$subdir" ]; then
|
|
log_info "Moving binaries from $subdir subdirectory..."
|
|
mv "$data_path/$subdir"/* "$data_path/" 2>/dev/null
|
|
rmdir "$data_path/$subdir" 2>/dev/null
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Make executable
|
|
chmod +x "$data_path/hbbs" "$data_path/hbbr" 2>/dev/null
|
|
|
|
# Verify binaries exist
|
|
if [ ! -x "$data_path/hbbs" ] || [ ! -x "$data_path/hbbr" ]; then
|
|
log_error "Binaries not found after extraction"
|
|
ls -la "$data_path"
|
|
return 1
|
|
fi
|
|
|
|
# Generate key if not exists
|
|
if [ ! -f "$data_path/id_ed25519" ]; then
|
|
log_info "Generating authentication key..."
|
|
cd "$data_path"
|
|
# Run hbbs briefly to generate key
|
|
timeout 3 ./hbbs -p "$id_port" >/dev/null 2>&1 || true
|
|
sleep 1
|
|
fi
|
|
|
|
# Store key in UCI if generated
|
|
if [ -f "$data_path/id_ed25519.pub" ]; then
|
|
local pubkey=$(cat "$data_path/id_ed25519.pub" 2>/dev/null)
|
|
uci_set key "$pubkey"
|
|
uci commit "$CONFIG"
|
|
fi
|
|
|
|
log_info "RustDesk server installed successfully"
|
|
log_info "Data directory: $data_path"
|
|
|
|
# Show key
|
|
if [ -f "$data_path/id_ed25519.pub" ]; then
|
|
echo ""
|
|
echo "Public key for client configuration:"
|
|
cat "$data_path/id_ed25519.pub"
|
|
fi
|
|
|
|
# Enable service
|
|
uci_set enabled '1'
|
|
uci commit "$CONFIG"
|
|
/etc/init.d/rustdesk enable
|
|
/etc/init.d/rustdesk start
|
|
|
|
log_info "Service enabled and started"
|
|
}
|
|
|
|
cmd_uninstall() {
|
|
require_root || { log_error "Must run as root"; return 1; }
|
|
defaults
|
|
|
|
log_info "Stopping RustDesk server..."
|
|
/etc/init.d/rustdesk stop 2>/dev/null
|
|
/etc/init.d/rustdesk disable 2>/dev/null
|
|
|
|
# Remove binaries but keep keys
|
|
rm -f "$data_path/hbbs" "$data_path/hbbr" 2>/dev/null
|
|
|
|
uci_set enabled '0'
|
|
uci commit "$CONFIG"
|
|
|
|
log_info "RustDesk binaries removed (keys preserved in $data_path)"
|
|
}
|
|
|
|
cmd_keygen() {
|
|
require_root || { log_error "Must run as root"; return 1; }
|
|
defaults
|
|
|
|
log_info "Regenerating authentication key..."
|
|
|
|
# Stop service
|
|
/etc/init.d/rustdesk stop 2>/dev/null
|
|
|
|
# Remove old keys
|
|
rm -f "$data_path/id_ed25519" "$data_path/id_ed25519.pub" 2>/dev/null
|
|
|
|
# Generate new key by running hbbs briefly
|
|
cd "$data_path"
|
|
timeout 3 ./hbbs -p "$id_port" >/dev/null 2>&1 || true
|
|
sleep 1
|
|
|
|
if [ -f "$data_path/id_ed25519.pub" ]; then
|
|
local pubkey=$(cat "$data_path/id_ed25519.pub" 2>/dev/null)
|
|
uci_set key "$pubkey"
|
|
uci commit "$CONFIG"
|
|
log_info "New key generated"
|
|
echo ""
|
|
echo "New public key:"
|
|
cat "$data_path/id_ed25519.pub"
|
|
else
|
|
log_error "Failed to generate key"
|
|
return 1
|
|
fi
|
|
|
|
# Restart service
|
|
/etc/init.d/rustdesk start
|
|
}
|
|
|
|
cmd_status() {
|
|
defaults
|
|
|
|
echo "RustDesk Server Status"
|
|
echo "======================"
|
|
echo ""
|
|
echo "Configuration:"
|
|
echo " Enabled: $(uci_get enabled || echo '0')"
|
|
echo " ID Port: $id_port"
|
|
echo " Relay Port: $relay_port"
|
|
echo " Data Path: $data_path"
|
|
echo ""
|
|
|
|
# Check binaries
|
|
if [ -x "$data_path/hbbs" ]; then
|
|
echo "Binaries: Installed"
|
|
else
|
|
echo "Binaries: Not installed (run: rustdeskctl install)"
|
|
return 0
|
|
fi
|
|
|
|
# Check processes
|
|
echo ""
|
|
echo "Processes:"
|
|
if pgrep -f "hbbs.*-p.*$id_port" >/dev/null 2>&1; then
|
|
echo " hbbs (ID): RUNNING (port $id_port)"
|
|
else
|
|
echo " hbbs (ID): STOPPED"
|
|
fi
|
|
|
|
if pgrep -f "hbbr.*-p.*$relay_port" >/dev/null 2>&1; then
|
|
echo " hbbr (Relay): RUNNING (port $relay_port)"
|
|
else
|
|
echo " hbbr (Relay): STOPPED"
|
|
fi
|
|
|
|
# Show key
|
|
echo ""
|
|
if [ -f "$data_path/id_ed25519.pub" ]; then
|
|
echo "Public Key:"
|
|
echo " $(cat "$data_path/id_ed25519.pub")"
|
|
echo ""
|
|
echo "Client Configuration:"
|
|
echo " ID Server: $(uci -q get network.lan.ipaddr || echo '<router-ip>'):$id_port"
|
|
echo " Key: $(cat "$data_path/id_ed25519.pub")"
|
|
else
|
|
echo "Public Key: Not generated"
|
|
fi
|
|
}
|
|
|
|
cmd_logs() {
|
|
defaults
|
|
local follow=""
|
|
[ "$1" = "-f" ] && follow="-f"
|
|
|
|
if [ -f "$data_path/hbbs.log" ]; then
|
|
echo "=== hbbs log ==="
|
|
tail $follow -n 50 "$data_path/hbbs.log"
|
|
fi
|
|
|
|
if [ -f "$data_path/hbbr.log" ]; then
|
|
echo ""
|
|
echo "=== hbbr log ==="
|
|
tail $follow -n 50 "$data_path/hbbr.log"
|
|
fi
|
|
|
|
# Also check system log
|
|
if [ -z "$follow" ]; then
|
|
echo ""
|
|
echo "=== System log ==="
|
|
logread | grep -E "(hbbs|hbbr|rustdesk)" | tail -20
|
|
fi
|
|
}
|
|
|
|
cmd_configure_firewall() {
|
|
require_root || { log_error "Must run as root"; return 1; }
|
|
defaults
|
|
|
|
local changed=0
|
|
|
|
# Check if rules already exist
|
|
if ! uci show firewall 2>/dev/null | grep -q "RustDesk-ID"; then
|
|
log_info "Creating firewall rules..."
|
|
|
|
# ID server (TCP+UDP)
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='RustDesk-ID-TCP'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port="$id_port"
|
|
uci set firewall.@rule[-1].proto='tcp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='RustDesk-ID-UDP'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port="$id_port"
|
|
uci set firewall.@rule[-1].proto='udp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
|
|
# Relay server (TCP)
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='RustDesk-Relay'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port="$relay_port"
|
|
uci set firewall.@rule[-1].proto='tcp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
|
|
# NAT test (TCP)
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='RustDesk-NAT'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port='21115'
|
|
uci set firewall.@rule[-1].proto='tcp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
|
|
changed=1
|
|
fi
|
|
|
|
if [ "$changed" = "1" ]; then
|
|
uci commit firewall
|
|
/etc/init.d/firewall reload
|
|
log_info "Firewall rules created for ports 21115-21117"
|
|
else
|
|
log_info "Firewall rules already exist"
|
|
fi
|
|
|
|
uci_set firewall_wan '1' network
|
|
uci commit "$CONFIG"
|
|
}
|
|
|
|
cmd_mesh_register() {
|
|
defaults
|
|
|
|
if [ -x /usr/sbin/secubox-p2p ]; then
|
|
/usr/sbin/secubox-p2p register-service rustdesk "$id_port" 2>/dev/null
|
|
log_info "Registered RustDesk with mesh"
|
|
else
|
|
log_error "secubox-p2p not available"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------- service management ----------
|
|
|
|
cmd_service_run() {
|
|
require_root || exit 1
|
|
defaults
|
|
|
|
# Verify binaries
|
|
[ -x "$data_path/hbbs" ] || { log_error "hbbs not found"; exit 1; }
|
|
[ -x "$data_path/hbbr" ] || { log_error "hbbr not found"; exit 1; }
|
|
|
|
cd "$data_path" || exit 1
|
|
|
|
log_info "Starting RustDesk servers..."
|
|
|
|
# Start hbbs (ID server)
|
|
./hbbs -p "$id_port" -r "127.0.0.1:$relay_port" >> "$data_path/hbbs.log" 2>&1 &
|
|
HBBS_PID=$!
|
|
echo $HBBS_PID > "$data_path/hbbs.pid"
|
|
|
|
sleep 1
|
|
|
|
# Start hbbr (relay server)
|
|
./hbbr -p "$relay_port" >> "$data_path/hbbr.log" 2>&1 &
|
|
HBBR_PID=$!
|
|
echo $HBBR_PID > "$data_path/hbbr.pid"
|
|
|
|
log_info "hbbs started (PID: $HBBS_PID), hbbr started (PID: $HBBR_PID)"
|
|
|
|
# Wait for processes
|
|
trap 'cmd_service_stop; exit 0' TERM INT
|
|
wait
|
|
}
|
|
|
|
cmd_service_stop() {
|
|
defaults
|
|
|
|
log_info "Stopping RustDesk servers..."
|
|
|
|
# Kill by PID file
|
|
[ -f "$data_path/hbbs.pid" ] && kill $(cat "$data_path/hbbs.pid") 2>/dev/null
|
|
[ -f "$data_path/hbbr.pid" ] && kill $(cat "$data_path/hbbr.pid") 2>/dev/null
|
|
|
|
# Cleanup
|
|
rm -f "$data_path/hbbs.pid" "$data_path/hbbr.pid"
|
|
|
|
# Kill any remaining
|
|
pkill -f "hbbs.*-p.*$id_port" 2>/dev/null
|
|
pkill -f "hbbr.*-p.*$relay_port" 2>/dev/null
|
|
|
|
log_info "Servers stopped"
|
|
}
|
|
|
|
# ---------- main ----------
|
|
|
|
case "$1" in
|
|
install) cmd_install ;;
|
|
uninstall) cmd_uninstall ;;
|
|
status) cmd_status ;;
|
|
keygen) cmd_keygen ;;
|
|
logs) shift; cmd_logs "$@" ;;
|
|
configure-firewall) cmd_configure_firewall ;;
|
|
mesh-register) cmd_mesh_register ;;
|
|
service-run) cmd_service_run ;;
|
|
service-stop) cmd_service_stop ;;
|
|
*) usage; exit 1 ;;
|
|
esac
|