From 52e7e478e4649041dcd9ab32e6828c8e86c2cf5d Mon Sep 17 00:00:00 2001 From: kitadmin Date: Wed, 22 Jul 2026 03:10:32 +0000 Subject: [PATCH] feat: GL kit-connect ipk + one-button wizard + web dashboard for both platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Synology kit-connect (SPK): - wizard.sh — one-button Keylink IT fleet setup (auto-runs on install) - www/index.html — dark-themed local dashboard (same design as busrouter) - bin/serve-dashboard.sh — busybox httpd on port 8089 with CGI endpoints - start-stop-status updated to manage dashboard alongside daemon - INFO updated: RT2600ac + RT6600ax compatibility noted - postinst now runs wizard --auto for first-time setup GL kit-connect (OpenWrt ipk): - Makefile — DEPENDS: +curl +openssh-client +tailscale - procd init — USE_PROCD=1, respawn with 5s delay - connect-daemon.sh — Tailscale + reverse SSH (same as Synology version) - connect-wizard.sh — one-button setup, idempotent, UCI config - connect-register.sh — standalone hub registration - www/kitconnect/index.html — dark-themed dashboard with wizard button - www/kitconnect/wizard.cgi — web-triggered wizard endpoint - UCI config at /etc/config/kitconnect Both platforms share: same hub protocol, same Tailscale key, same port pool. Co-Authored-By: Claude Opus 4.8 --- gl-kit-connect/Makefile | 69 +++++++ gl-kit-connect/files/etc/config/kitconnect | 26 +++ gl-kit-connect/files/etc/init.d/kitconnect | 43 ++++ .../files/usr/sbin/connect-daemon.sh | 188 ++++++++++++++++++ .../files/usr/sbin/connect-register.sh | 58 ++++++ .../files/usr/sbin/connect-wizard.sh | 162 +++++++++++++++ .../files/www/kitconnect/index.html | 185 +++++++++++++++++ .../files/www/kitconnect/wizard.cgi | 60 ++++++ kit-connect/INFO | 4 +- kit-connect/bin/serve-dashboard.sh | 82 ++++++++ kit-connect/build.sh | 62 +++--- kit-connect/scripts/postinst | 56 +++--- kit-connect/scripts/start-stop-status | 48 ++++- kit-connect/wizard.sh | 137 +++++++++++++ kit-connect/www/index.html | 136 +++++++++++++ syno-balance/INFO | 2 +- 16 files changed, 1241 insertions(+), 77 deletions(-) create mode 100644 gl-kit-connect/Makefile create mode 100644 gl-kit-connect/files/etc/config/kitconnect create mode 100644 gl-kit-connect/files/etc/init.d/kitconnect create mode 100644 gl-kit-connect/files/usr/sbin/connect-daemon.sh create mode 100644 gl-kit-connect/files/usr/sbin/connect-register.sh create mode 100644 gl-kit-connect/files/usr/sbin/connect-wizard.sh create mode 100644 gl-kit-connect/files/www/kitconnect/index.html create mode 100644 gl-kit-connect/files/www/kitconnect/wizard.cgi create mode 100755 kit-connect/bin/serve-dashboard.sh create mode 100755 kit-connect/wizard.sh create mode 100644 kit-connect/www/index.html diff --git a/gl-kit-connect/Makefile b/gl-kit-connect/Makefile new file mode 100644 index 0000000..70e2b0e --- /dev/null +++ b/gl-kit-connect/Makefile @@ -0,0 +1,69 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=kit-connect +PKG_VERSION:=0.1.0 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/kit-connect + SECTION:=net + CATEGORY:=Network + TITLE:=Pioneer Bus Fleet — Connectivity Agent + DEPENDS:=+curl +openssh-client +tailscale + PKGARCH:=all +endef + +define Package/kit-connect/description + Unified management connectivity for Pioneer bus fleet routers. + Deploys Tailscale + reverse SSH tunnel. Hub auto-assigns ports. + Includes one-button setup wizard and local status dashboard. + + Compatible: GL-XE3000, GL-MT3000, and all OpenWrt-based fleet routers. +endef + +define Build/Compile +endef + +define Package/kit-connect/install + $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/kitconnect + $(INSTALL_DIR) $(1)/usr/sbin $(1)/www/kitconnect + + # Init + config + $(INSTALL_BIN) ./files/etc/init.d/kitconnect $(1)/etc/init.d/kitconnect + $(INSTALL_CONF) ./files/etc/config/kitconnect $(1)/etc/config/kitconnect + + # Binaries + $(INSTALL_BIN) ./files/usr/sbin/connect-daemon.sh $(1)/usr/sbin/connect-daemon.sh + $(INSTALL_BIN) ./files/usr/sbin/connect-register.sh $(1)/usr/sbin/connect-register.sh + $(INSTALL_BIN) ./files/usr/sbin/connect-wizard.sh $(1)/usr/sbin/connect-wizard.sh + + # SSH key (private — must be 600) + $(INSTALL_DATA) ./files/etc/kitconnect/connect_id_ed25519 $(1)/etc/kitconnect/connect_id_ed25519 + chmod 600 $(1)/etc/kitconnect/connect_id_ed25519 + + # Web UI + $(INSTALL_DATA) ./files/www/kitconnect/index.html $(1)/www/kitconnect/index.html + $(INSTALL_BIN) ./files/www/kitconnect/wizard.cgi $(1)/www/kitconnect/wizard.cgi +endef + +define Package/kit-connect/postinst +#!/bin/sh +# Run the setup wizard on first install +if [ -z "$${IPKG_INSTROOT}" ]; then + /etc/init.d/kitconnect enable 2>/dev/null || true + /usr/sbin/connect-wizard.sh --auto 2>/dev/null || true + /etc/init.d/kitconnect start 2>/dev/null || true +fi +endef + +define Package/kit-connect/prerm +#!/bin/sh +# Stop service before removal +if [ -z "$${IPKG_INSTROOT}" ]; then + /etc/init.d/kitconnect stop 2>/dev/null || true + /etc/init.d/kitconnect disable 2>/dev/null || true +fi +endef + +$(eval $(call BuildPackage,kit-connect)) diff --git a/gl-kit-connect/files/etc/config/kitconnect b/gl-kit-connect/files/etc/config/kitconnect new file mode 100644 index 0000000..da644c6 --- /dev/null +++ b/gl-kit-connect/files/etc/config/kitconnect @@ -0,0 +1,26 @@ +config kitconnect 'main' + # ── Device Identity ──────────────────────────────────── + option device_id '' + # Auto-detected from /etc/busrouter/device-id or hostname + + # ── Hub Registration ──────────────────────────────────── + option hub_host '162.243.83.36' + option hub_port '8080' + + # ── Reverse SSH Tunnel ────────────────────────────────── + option tunnel_enable '1' + option tunnel_remote_host '162.243.83.36' + option tunnel_remote_user 'node' + option tunnel_remote_ssh_port '22' + option tunnel_remote_port '0' # 0 = auto-assign via hub + option tunnel_local_port '2223' + option tunnel_retry_delay '30' + + # ── Tailscale ────────────────────────────────────────── + option tailscale_enable '1' + option tailscale_auth_key 'tskey-auth-kJz8wqNVo211CNTRL-GNL5EFjp5aWQcaWPSVn2aW9TNworKUNBV' + option tailscale_hostname '' + + # ── Behaviour ────────────────────────────────────────── + option watchdog_interval '60' + option forward_fail_limit '2' diff --git a/gl-kit-connect/files/etc/init.d/kitconnect b/gl-kit-connect/files/etc/init.d/kitconnect new file mode 100644 index 0000000..8d07a8f --- /dev/null +++ b/gl-kit-connect/files/etc/init.d/kitconnect @@ -0,0 +1,43 @@ +#!/bin/sh /etc/rc.common +# kit-connect — Pioneer Bus Fleet connectivity agent +# Manages Tailscale + reverse SSH tunnel to the fleet hub. +# One package, all GL routers. Hub assigns ports and keys. + +USE_PROCD=1 +START=90 + +EXTRA_COMMANDS="wizard status" +EXTRA_HELP=" wizard Run the Keylink IT setup wizard + status Show connectivity status" + +start_service() { + procd_open_instance + procd_set_param command /usr/sbin/connect-daemon.sh + procd_set_param respawn 3600 5 0 # max 1 respawn/hr, 5s delay, unlimited + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} + +wizard() { + /usr/sbin/connect-wizard.sh +} + +status() { + echo "=== kit-connect Status ===" + echo "Device: $(cat /etc/busrouter/device-id 2>/dev/null || hostname)" + if [ -f /var/run/kitconnect.pid ] && kill -0 "$(cat /var/run/kitconnect.pid)" 2>/dev/null; then + echo "Daemon: running (PID $(cat /var/run/kitconnect.pid))" + else + echo "Daemon: stopped" + fi + uci -q show kitconnect 2>/dev/null | while IFS='=' read -r key val; do + echo "Config: ${key##*.}=${val//\'/}" + done + echo "Tunnel PID: $(cat /var/run/kitconnect-tunnel.pid 2>/dev/null || echo 'none')" + if command -v tailscale >/dev/null 2>&1; then + tailscale status 2>/dev/null | head -3 + else + echo "Tailscale: not installed" + fi +} diff --git a/gl-kit-connect/files/usr/sbin/connect-daemon.sh b/gl-kit-connect/files/usr/sbin/connect-daemon.sh new file mode 100644 index 0000000..beb01b9 --- /dev/null +++ b/gl-kit-connect/files/usr/sbin/connect-daemon.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# kit-connect daemon — manages Tailscale + reverse SSH tunnel. +# Runs under procd supervision (auto-respawn on crash). +# Config: /etc/config/kitconnect (UCI) + hub-assigned settings. +# One package, all GL routers. Hub assigns ports and keys. + +PKG="kit-connect" +STATE_DIR="/var/run/kitconnect" +CONF="/etc/config/kitconnect" +KEY="/etc/kitconnect/connect_id_ed25519" +PID_FILE="$STATE_DIR/daemon.pid" +TUNNEL_PID="$STATE_DIR/tunnel.pid" +LOG_TAG="kit-connect" + +log() { + logger -t "$LOG_TAG" -p daemon.info "$*" +} + +warn() { + logger -t "$LOG_TAG" -p daemon.warn "WARN: $*" +} + +mkdir -p "$STATE_DIR" +chmod 600 "$KEY" 2>/dev/null +echo "$$" > "$PID_FILE" + +# ── Config loader (UCI → shell vars) ────────────────────────── +load_config() { + _uci() { uci -q get "kitconnect.main.${1}" 2>/dev/null || echo "${2}"; } + + DEVICE_ID=$(_uci device_id "") + [ -z "$DEVICE_ID" ] && DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || hostname | sed 's/[^a-zA-Z0-9_-]//g') + + HUB_HOST=$(_uci hub_host "162.243.83.36") + HUB_PORT=$(_uci hub_port "8080") + + TUNNEL_ENABLE=$(_uci tunnel_enable "1") + TUNNEL_REMOTE_HOST=$(_uci tunnel_remote_host "162.243.83.36") + TUNNEL_REMOTE_USER=$(_uci tunnel_remote_user "node") + TUNNEL_REMOTE_SSH_PORT=$(_uci tunnel_remote_ssh_port "22") + TUNNEL_REMOTE_PORT=$(_uci tunnel_remote_port "0") + TUNNEL_LOCAL_PORT=$(_uci tunnel_local_port "2223") + TUNNEL_RETRY_DELAY=$(_uci tunnel_retry_delay "30") + + TAILSCALE_ENABLE=$(_uci tailscale_enable "1") + TAILSCALE_AUTH_KEY=$(_uci tailscale_auth_key "") + TAILSCALE_HOSTNAME=$(_uci tailscale_hostname "") + [ -z "$TAILSCALE_HOSTNAME" ] && TAILSCALE_HOSTNAME="$DEVICE_ID" + + WATCHDOG_INTERVAL=$(_uci watchdog_interval "60") + FORWARD_FAIL_LIMIT=$(_uci forward_fail_limit "2") + + # Binary detection + TAILSCALE_BIN=$(command -v tailscale 2>/dev/null || echo "/usr/sbin/tailscale") + TAILSCALED_BIN=$(command -v tailscaled 2>/dev/null || echo "/usr/sbin/tailscaled") +} + +# ── Start Tailscale ──────────────────────────────────────────── +start_tailscale() { + [ "$TAILSCALE_ENABLE" != "1" ] && { log "tailscale: disabled in config"; return 0; } + [ -z "$TAILSCALE_AUTH_KEY" ] && { log "tailscale: no auth key — skipping"; return 0; } + ! command -v "$TAILSCALE_BIN" >/dev/null 2>&1 && { log "tailscale: binary not found"; return 1; } + + # Already connected? + if "$TAILSCALE_BIN" status >/dev/null 2>&1; then + log "tailscale: already connected ($($TAILSCALE_BIN ip -4 2>/dev/null || echo no-ip))" + return 0 + fi + + log "tailscale: starting daemon..." + "$TAILSCALED_BIN" --tun=userspace-networking >/dev/null 2>&1 & + sleep 3 + + log "tailscale: authenticating..." + "$TAILSCALE_BIN" up \ + --auth-key "$TAILSCALE_AUTH_KEY" \ + --hostname "${TAILSCALE_HOSTNAME}" \ + --accept-routes=false \ + --accept-dns=false 2>&1 | while read -r line; do log "tailscale: $line"; done + + local ts_ip + ts_ip=$("$TAILSCALE_BIN" ip -4 2>/dev/null || echo "unknown") + log "tailscale: connected — IP=${ts_ip}" +} + +# ── Start reverse SSH tunnel ─────────────────────────────────── +start_tunnel() { + [ "$TUNNEL_ENABLE" != "1" ] && { log "tunnel: disabled"; return 0; } + [ "$TUNNEL_REMOTE_PORT" = "0" ] && { register_with_hub; load_config; } + [ "$TUNNEL_REMOTE_PORT" = "0" ] && { log "tunnel: no port assigned — skipping"; return 0; } + + log "tunnel: opening R:0.0.0.0:${TUNNEL_REMOTE_PORT} → 127.0.0.1:${TUNNEL_LOCAL_PORT}" + + ssh \ + -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null \ + -o ServerAliveInterval=15 \ + -o ServerAliveCountMax=3 \ + -o ExitOnForwardFailure=yes \ + -o BatchMode=yes \ + -p "${TUNNEL_REMOTE_SSH_PORT}" \ + -N \ + -R "0.0.0.0:${TUNNEL_REMOTE_PORT}:127.0.0.1:${TUNNEL_LOCAL_PORT}" \ + -i "$KEY" \ + "${TUNNEL_REMOTE_USER}@${TUNNEL_REMOTE_HOST}" \ + 2>"$STATE_DIR/ssh_err" & + + SSH_PID=$! + echo "$SSH_PID" > "$TUNNEL_PID" + log "tunnel: SSH PID=${SSH_PID} port=${TUNNEL_REMOTE_PORT}" + + # Watchdog loop — monitors tunnel health + _fail_count=0 + while kill -0 "$SSH_PID" 2>/dev/null; do + sleep "$WATCHDOG_INTERVAL" + kill -0 "$SSH_PID" 2>/dev/null || break + + if timeout 10 ssh \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + -o ConnectTimeout=5 -o BatchMode=yes \ + -p "${TUNNEL_REMOTE_SSH_PORT}" -i "$KEY" \ + "${TUNNEL_REMOTE_USER}@${TUNNEL_REMOTE_HOST}" \ + "timeout 3 bash -c 'echo >/dev/tcp/127.0.0.1/${TUNNEL_REMOTE_PORT}' 2>/dev/null" \ + 2>/dev/null; then + [ "$_fail_count" -gt 0 ] && log "tunnel: forward recovered after ${_fail_count} checks" + _fail_count=0 + else + _fail_count=$((_fail_count + 1)) + warn "tunnel: forward check failed (${_fail_count}/${FORWARD_FAIL_LIMIT})" + [ "$_fail_count" -ge "$FORWARD_FAIL_LIMIT" ] && break + fi + done + + kill "$SSH_PID" 2>/dev/null + rm -f "$TUNNEL_PID" + log "tunnel: exited — will retry in ${TUNNEL_RETRY_DELAY}s" + sleep "$TUNNEL_RETRY_DELAY" + return 1 +} + +# ── Hub registration — get assigned port ─────────────────────── +register_with_hub() { + [ "$TUNNEL_REMOTE_PORT" != "0" ] && { return 0; } + + log "hub: registering device ${DEVICE_ID}..." + + resp=$(curl -s --connect-timeout 10 \ + "http://${HUB_HOST}:${HUB_PORT}/api/register/${DEVICE_ID}" 2>/dev/null) || true + + [ -z "$resp" ] && { warn "hub: unreachable"; return 1; } + + # Parse JSON response (no jq dependency — grep + cut) + port=$(echo "$resp" | grep -o '"tunnel_port"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*') + tskey=$(echo "$resp" | grep -o '"tailscale_auth_key"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + + if [ -n "$port" ] && [ "$port" != "0" ]; then + uci set kitconnect.main.tunnel_remote_port="$port" + uci commit kitconnect + log "hub: assigned port ${port}" + echo "$port" > "$STATE_DIR/hub_port" + fi + + if [ -n "$tskey" ] && [ "$tskey" != "$TAILSCALE_AUTH_KEY" ]; then + uci set kitconnect.main.tailscale_auth_key="$tskey" + uci commit kitconnect + log "hub: updated Tailscale key" + fi +} + +# ── Main loop (one cycle — procd respawns on exit) ──────────── +log "EVENT=DAEMON_START device=${DEVICE_ID}" +load_config + +# Ensure device-id file exists +mkdir -p /etc/busrouter +[ ! -f /etc/busrouter/device-id ] && echo "$DEVICE_ID" > /etc/busrouter/device-id + +# Register with hub (non-fatal — retries next cycle) +register_with_hub 2>/dev/null || true + +# Start Tailscale (runs in background, stays up) +start_tailscale 2>/dev/null || true + +# Start tunnel (blocks until failure, then exits — procd restarts) +start_tunnel 2>/dev/null || true + +log "EVENT=DAEMON_EXIT device=${DEVICE_ID}" +exit 0 diff --git a/gl-kit-connect/files/usr/sbin/connect-register.sh b/gl-kit-connect/files/usr/sbin/connect-register.sh new file mode 100644 index 0000000..379c4b1 --- /dev/null +++ b/gl-kit-connect/files/usr/sbin/connect-register.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# connect-register.sh — Standalone hub registration (callable from wizard or cron) +# Usage: connect-register.sh [--force] +# Re-registers even if already assigned when --force is given. + +HUB_HOST="162.243.83.36" +HUB_PORT="8080" + +DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || hostname | sed 's/[^a-zA-Z0-9_-]//g') +CURRENT_PORT=$(uci -q get kitconnect.main.tunnel_remote_port 2>/dev/null || echo "0") + +[ "$1" = "--force" ] && CURRENT_PORT="0" +[ "$CURRENT_PORT" != "0" ] && { + echo "Already registered (port ${CURRENT_PORT}). Use --force to re-register." + exit 0 +} + +echo "Registering device ${DEVICE_ID} with hub ${HUB_HOST}:${HUB_PORT}..." +resp=$(curl -s --connect-timeout 10 "http://${HUB_HOST}:${HUB_PORT}/api/register/${DEVICE_ID}" 2>/dev/null) + +[ -z "$resp" ] && { + echo "ERROR: Hub unreachable at ${HUB_HOST}:${HUB_PORT}" + exit 1 +} + +port=$(echo "$resp" | grep -o '"tunnel_port"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*') +status=$(echo "$resp" | grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) +tskey=$(echo "$resp" | grep -o '"tailscale_auth_key"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + +echo "" +echo " Device: ${DEVICE_ID}" +echo " Status: ${status:-unknown}" +echo " Port: ${port:-none}" +echo "" + +if [ -n "$port" ] && [ "$port" != "0" ]; then + uci set kitconnect.main.tunnel_remote_port="$port" + uci commit kitconnect + echo "Port ${port} saved to UCI config." +fi + +if [ -n "$tskey" ]; then + uci set kitconnect.main.tailscale_auth_key="$tskey" + uci commit kitconnect + echo "Tailscale auth key updated from hub." +fi + +if [ "$status" = "new" ]; then + echo "" + echo "============================================" + echo " ✓ Device registered successfully!" + echo " Port ${port} assigned for reverse SSH." + echo " Restart kit-connect to apply:" + echo " /etc/init.d/kitconnect restart" + echo "============================================" +elif [ "$status" = "existing" ]; then + echo "✓ Device was already registered (existing assignment confirmed)." +fi diff --git a/gl-kit-connect/files/usr/sbin/connect-wizard.sh b/gl-kit-connect/files/usr/sbin/connect-wizard.sh new file mode 100644 index 0000000..07fd6dc --- /dev/null +++ b/gl-kit-connect/files/usr/sbin/connect-wizard.sh @@ -0,0 +1,162 @@ +#!/bin/sh +# connect-wizard.sh — One-button Keylink IT fleet setup. +# Idempotent — safe to run multiple times. +# Usage: connect-wizard.sh [--auto] [--force] +# --auto Non-interactive mode (for postinst) +# --force Re-register even if already assigned + +set -e + +HUB_HOST="162.243.83.36" +HUB_PORT="8080" +AUTO=0 +FORCE=0 +[ "$1" = "--auto" ] && AUTO=1 +[ "$1" = "--force" ] || [ "$2" = "--force" ] && FORCE=1 + +banner() { + echo "" + echo " ╔══════════════════════════════════════════╗" + echo " ║ KEYLINK IT — Pioneer Bus Fleet Setup ║" + echo " ╚══════════════════════════════════════════╝" + echo "" +} + +step() { echo " ▶ $*"; } +ok() { echo " ✓ $*"; } +warn() { echo " ⚠ $*"; } +fail() { echo " ✗ $*"; } + +banner + +# ── Step 1: Detect device ────────────────────────────────────── +step "Detecting device..." +DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || true) +if [ -z "$DEVICE_ID" ]; then + DEVICE_ID=$(hostname 2>/dev/null | sed 's/[^a-zA-Z0-9_-]//g' || echo "unknown") + # If hostname is generic, try to derive from model + if [ "$DEVICE_ID" = "OpenWrt" ] || [ "$DEVICE_ID" = "openwrt" ] || [ -z "$DEVICE_ID" ]; then + model=$(cat /tmp/sysinfo/model 2>/dev/null | tr ' ' '-' | sed 's/[^a-zA-Z0-9_-]//g' || echo "") + [ -n "$model" ] && DEVICE_ID="${model}-$(cat /sys/class/net/eth0/address 2>/dev/null | tr -d ':' | tail -c 6 || echo '000000')" + [ -z "$DEVICE_ID" ] && DEVICE_ID="GL-unknown" + fi +fi +mkdir -p /etc/busrouter +echo "$DEVICE_ID" > /etc/busrouter/device-id +ok "Device ID: ${DEVICE_ID}" + +# ── Step 2: Check prerequisites ──────────────────────────────── +step "Checking prerequisites..." + +MISSING="" +for cmd in curl ssh ssh-keygen; do + command -v "$cmd" >/dev/null 2>&1 || MISSING="${MISSING} ${cmd}" +done + +if [ -n "$MISSING" ]; then + fail "Missing:${MISSING}" + [ "$AUTO" = "1" ] && exit 1 + echo "" + echo " Install missing packages with:" + echo " opkg update && opkg install curl openssh-client" + exit 1 +fi +ok "All prerequisites found (curl, ssh)" + +# ── Step 3: SSH key ──────────────────────────────────────────── +step "Checking SSH key..." +KEY="/etc/kitconnect/connect_id_ed25519" +if [ ! -f "$KEY" ]; then + warn "Key not found at ${KEY}, generating..." + mkdir -p /etc/kitconnect + ssh-keygen -t ed25519 -f "$KEY" -N "" -C "kit-connect-${DEVICE_ID}" >/dev/null 2>&1 + ok "Generated new Ed25519 key" +else + ok "SSH key present" +fi +chmod 600 "$KEY" + +# ── Step 4: UCI config defaults ──────────────────────────────── +step "Configuring Keylink IT defaults..." + +# Ensure UCI config exists +if ! uci -q get kitconnect.main >/dev/null 2>&1; then + uci set kitconnect.main=kitconnect +fi + +uci -q set kitconnect.main.device_id="$DEVICE_ID" +uci -q set kitconnect.main.hub_host="$HUB_HOST" +uci -q set kitconnect.main.hub_port="$HUB_PORT" +uci -q set kitconnect.main.tunnel_enable='1' +uci -q set kitconnect.main.tunnel_remote_host="$HUB_HOST" +uci -q set kitconnect.main.tunnel_remote_user='node' +uci -q set kitconnect.main.tunnel_remote_ssh_port='22' +uci -q set kitconnect.main.tunnel_remote_port='0' +uci -q set kitconnect.main.tunnel_local_port='2223' +uci -q set kitconnect.main.tailscale_enable='1' +uci -q set kitconnect.main.tailscale_auth_key='tskey-auth-kJz8wqNVo211CNTRL-GNL5EFjp5aWQcaWPSVn2aW9TNworKUNBV' +uci -q set kitconnect.main.tailscale_hostname="$DEVICE_ID" +uci -q set kitconnect.main.watchdog_interval='60' +uci -q set kitconnect.main.forward_fail_limit='2' +uci commit kitconnect +ok "UCI config written" + +# ── Step 5: Register with hub ────────────────────────────────── +step "Registering with fleet hub (${HUB_HOST}:${HUB_PORT})..." + +REG_FLAG="" +[ "$FORCE" = "1" ] && REG_FLAG="--force" + +resp=$(curl -s --connect-timeout 10 "http://${HUB_HOST}:${HUB_PORT}/api/register/${DEVICE_ID}" 2>/dev/null) || true + +if [ -z "$resp" ]; then + warn "Hub unreachable — will retry on next daemon start" + ok "Config saved locally (port will auto-assign when hub is reachable)" +else + port=$(echo "$resp" | grep -o '"tunnel_port"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*') + status=$(echo "$resp" | grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + tskey=$(echo "$resp" | grep -o '"tailscale_auth_key"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + + if [ -n "$port" ] && [ "$port" != "0" ]; then + uci set kitconnect.main.tunnel_remote_port="$port" + uci commit kitconnect + ok "Hub assigned port ${port} (status: ${status})" + fi + if [ -n "$tskey" ]; then + uci set kitconnect.main.tailscale_auth_key="$tskey" + uci commit kitconnect + ok "Tailscale key configured" + fi +fi + +# ── Step 6: Enable & start service ───────────────────────────── +step "Enabling kit-connect service..." +/etc/init.d/kitconnect enable 2>/dev/null && ok "Enabled at boot" || warn "Could not enable (may already be enabled)" + +step "Starting kit-connect..." +if /etc/init.d/kitconnect start 2>/dev/null; then + ok "Service started" +else + warn "Service start returned non-zero (check logread for details)" +fi + +# ── Done ─────────────────────────────────────────────────────── +echo "" +echo " ╔══════════════════════════════════════════╗" +echo " ║ SETUP COMPLETE ║" +echo " ╠══════════════════════════════════════════╣" +echo " ║ Device: $(printf '%-30s' "$DEVICE_ID")║" + +ASSIGNED=$(uci -q get kitconnect.main.tunnel_remote_port 2>/dev/null || echo "pending") +echo " ║ Port: $(printf '%-30s' "$ASSIGNED")║" +echo " ║ Hub: $(printf '%-30s' "${HUB_HOST}:${HUB_PORT}")║" +echo " ╠══════════════════════════════════════════╣" +echo " ║ Dashboard: http://$(hostname)/kitconnect/$(printf ' ')%s║" "" +echo " ║ Status: /etc/init.d/kitconnect status║" +echo " ╚══════════════════════════════════════════╝" +echo "" +echo " The daemon is now running under procd supervision." +echo " If it crashes, procd will restart it automatically." +echo "" + +exit 0 diff --git a/gl-kit-connect/files/www/kitconnect/index.html b/gl-kit-connect/files/www/kitconnect/index.html new file mode 100644 index 0000000..f1feb51 --- /dev/null +++ b/gl-kit-connect/files/www/kitconnect/index.html @@ -0,0 +1,185 @@ + + + + + +Kit Connect — Fleet Status + + + +
+
+ +
Keylink IT Fleet Connectivity
+
+
Loading…
+
+
+ + +
+
⚡ One-Click Setup
+

+ Configures all Keylink IT defaults — device ID, fleet hub, Tailscale, reverse SSH tunnel. + Safe to run multiple times. +

+ +
+
+ + +
+
📡 Connectivity Status
+
+
Loading…
+
+
+ + +
+ + + + diff --git a/gl-kit-connect/files/www/kitconnect/wizard.cgi b/gl-kit-connect/files/www/kitconnect/wizard.cgi new file mode 100644 index 0000000..b7808ab --- /dev/null +++ b/gl-kit-connect/files/www/kitconnect/wizard.cgi @@ -0,0 +1,60 @@ +#!/bin/sh +# wizard.cgi — Web endpoint for the one-button setup wizard. +# GET ?action=status → returns connectivity state (plain text) +# POST → runs the setup wizard, returns output + +echo "Content-Type: text/plain" +echo "" + +METHOD="${REQUEST_METHOD:-GET}" +ACTION=$(echo "${QUERY_STRING:-}" | grep -o 'action=[^&]*' | cut -d= -f2) + +if [ "$METHOD" = "GET" ] && [ "$ACTION" = "status" ]; then + echo "=== kit-connect Status ===" + echo "" + + DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || hostname) + echo "Device: ${DEVICE_ID}" + + PID=$(cat /var/run/kitconnect/daemon.pid 2>/dev/null || echo "") + if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then + echo "Daemon: running (PID ${PID})" + else + echo "Daemon: stopped" + fi + + TUNNEL_PID=$(cat /var/run/kitconnect/tunnel.pid 2>/dev/null || echo "") + if [ -n "$TUNNEL_PID" ] && kill -0 "$TUNNEL_PID" 2>/dev/null; then + echo "Tunnel: active (PID ${TUNNEL_PID})" + else + echo "Tunnel: not connected" + fi + + PORT=$(uci -q get kitconnect.main.tunnel_remote_port 2>/dev/null || echo "pending") + echo "Hub port: ${PORT}" + + HUB=$(uci -q get kitconnect.main.hub_host 2>/dev/null || echo "unknown") + echo "Hub host: ${HUB}" + + if command -v tailscale >/dev/null 2>&1; then + TS_IP=$(tailscale ip -4 2>/dev/null || echo "unknown") + echo "Tailscale: ${TS_IP}" + else + echo "Tailscale: not installed" + fi + + echo "" + echo "Config:" + uci -q show kitconnect 2>/dev/null | while IFS='=' read -r key val; do + printf " %-25s = %s\n" "${key##*.}" "${val//\'/}" + done + + exit 0 +fi + +if [ "$METHOD" = "POST" ]; then + /usr/sbin/connect-wizard.sh --force 2>&1 + exit $? +fi + +echo "Usage: GET ?action=status or POST to run wizard" diff --git a/kit-connect/INFO b/kit-connect/INFO index 3082c07..4e0328b 100644 --- a/kit-connect/INFO +++ b/kit-connect/INFO @@ -1,10 +1,10 @@ package="kit-connect" version="0.1-0001" -description="Unified management connectivity for Pioneer bus fleet routers. Single SPK — deploys Tailscale + reverse SSH tunnel. Hub auto-assigns ports on install. One package, all routers." +description="Unified management connectivity for Pioneer bus fleet routers. Single SPK — deploys Tailscale + reverse SSH tunnel. Hub auto-assigns ports on install. One package, all routers. Compatible: RT2600ac, RT6600ax." +displayname="KIT Bus Router Connect" maintainer="Keylink IT" arch="ipq806x" firmware="1.3.1-9346" checkport="no" startable="yes" -displayname="KIT Bus Router Connect" thirdparty="yes" diff --git a/kit-connect/bin/serve-dashboard.sh b/kit-connect/bin/serve-dashboard.sh new file mode 100755 index 0000000..972c86a --- /dev/null +++ b/kit-connect/bin/serve-dashboard.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# serve-dashboard.sh — Lightweight HTTP server for the kit-connect dashboard. +# Uses busybox httpd, serves on port 8089. +# Started by start-stop-status alongside the main daemon. + +PKG_DIR="/var/packages/kit-connect/target" +WWW_DIR="${PKG_DIR}/www" +CGI_DIR="${PKG_DIR}/www/cgi-bin" +PID_FILE="${PKG_DIR}/var/dashboard.pid" +PORT="${1:-8089}" + +mkdir -p "$CGI_DIR" "$PKG_DIR/var" + +# ── Write busybox httpd config ────────────────────────────────── +cat > "$PKG_DIR/var/httpd.conf" < "$CGI_DIR/kitconnect-status" << 'CGIEOF' +#!/bin/sh +printf "Content-Type: application/json\r\n\r\n" + +DEVICE_ID="unknown" +DAEMON_RUNNING="false" +TUNNEL_PORT="0" +TAILSCALE_IP="" +HUB_REACHABLE="false" + +# Device detection +DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || hostname 2>/dev/null || echo "unknown") + +# Daemon check +[ -f /var/packages/kit-connect/target/var/daemon.pid ] && \ + kill -0 "$(cat /var/packages/kit-connect/target/var/daemon.pid)" 2>/dev/null && \ + DAEMON_RUNNING="true" + +# Tunnel port from config +TUNNEL_PORT=$(grep '^TUNNEL_REMOTE_PORT=' /etc/kit-connect/connect.conf 2>/dev/null | cut -d= -f2 || echo "0") + +# Tailscale +TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "") + +# Hub check +HUB_HOST=$(grep '^HUB_HOST=' /etc/kit-connect/connect.conf 2>/dev/null | cut -d= -f2 || echo "162.243.83.36") +HUB_PORT=$(grep '^HUB_PORT=' /etc/kit-connect/connect.conf 2>/dev/null | cut -d= -f2 || echo "8080") +timeout 3 bash -c "echo >/dev/tcp/${HUB_HOST}/${HUB_PORT}" 2>/dev/null && HUB_REACHABLE="true" + +printf '{"device_id":"%s","daemon_running":%s,"tunnel_port":"%s","tailscale_ip":"%s","hub_host":"%s","hub_port":"%s","hub_reachable":%s,"config_path":"%s","log_path":"%s"}\n' \ + "$DEVICE_ID" "$DAEMON_RUNNING" "$TUNNEL_PORT" "$TAILSCALE_IP" \ + "$HUB_HOST" "$HUB_PORT" "$HUB_REACHABLE" \ + "/etc/kit-connect/connect.conf" \ + "/var/packages/kit-connect/target/var/connect.log" +CGIEOF +chmod +x "$CGI_DIR/kitconnect-status" + +# Wizard endpoint +cat > "$CGI_DIR/kitconnect-wizard" << 'CGIEOF' +#!/bin/sh +printf "Content-Type: text/plain\r\n\r\n" +/var/packages/kit-connect/target/wizard.sh --force 2>&1 +CGIEOF +chmod +x "$CGI_DIR/kitconnect-wizard" + +# ── Start busybox httpd ──────────────────────────────────────── +if command -v busybox >/dev/null 2>&1; then + # Kill any existing instance + [ -f "$PID_FILE" ] && kill "$(cat "$PID_FILE")" 2>/dev/null + rm -f "$PID_FILE" + + busybox httpd -p "$PORT" -h "$WWW_DIR" -c "$PKG_DIR/var/httpd.conf" -v 2>/dev/null & + echo $! > "$PID_FILE" + echo "Dashboard started on port ${PORT}" +else + echo "busybox not available — dashboard not started" + exit 1 +fi diff --git a/kit-connect/build.sh b/kit-connect/build.sh index e69ada7..ec111ae 100755 --- a/kit-connect/build.sh +++ b/kit-connect/build.sh @@ -1,22 +1,17 @@ #!/bin/sh # build.sh — Assemble kit-connect SPK for Synology SRM (ipq806x) +# Compatible: RT2600ac, RT6600ax, all ipq806x SRM routers. # Output: kit-connect-0.1-0001.spk # # SPK structure (outer tar, UNCOMPRESSED): # INFO -# PACKAGE_ICON.PNG (64x64) +# PACKAGE_ICON.PNG (72x72) # PACKAGE_ICON_256.PNG (256x256) # scripts/ -# postinst -# start-stop-status -# preinst -# preuninst -# postuninst +# postinst, start-stop-status, preinst, preuninst, postuninst # conf/ -# privilege -# resource -# package.tgz (gzipped inner tar of bin/ + conf/) -# checksum (md5 of package.tgz) +# privilege, resource +# package.tgz (gzipped inner tar: bin/ + conf/connect.conf + wizard.sh + www/) set -e @@ -30,42 +25,34 @@ BUILD_DIR="/tmp/kit-connect-build-$$" echo "=== Building ${PKG_NAME}-${VERSION}.spk ===" -# Clean and create build staging directory rm -rf "$BUILD_DIR" mkdir -p "$BUILD_DIR" -# ── 1. Create inner package.tgz (gzipped tar of bin/ + conf/) ───────── +# ── 1. Create inner package.tgz ───────────────────────────────── echo " → Creating package.tgz" tar -czf "$BUILD_DIR/package.tgz" \ - --format=ustar \ - bin/ \ - conf/connect.conf + --format=ustar \ + bin/ \ + conf/connect.conf \ + wizard.sh \ + www/ -# ── 2. Generate checksum ────────────────────────────────────────────── -echo " → Computing checksum" -md5sum "$BUILD_DIR/package.tgz" | awk '{print $1}' > "$BUILD_DIR/checksum" +echo " → package.tgz contents:" +tar tzf "$BUILD_DIR/package.tgz" + +# ── 2. Stage and assemble outer SPK (NO checksum — deprecated) ── +STAGE="$BUILD_DIR/stage" +mkdir -p "$STAGE" +cp INFO PACKAGE_ICON.PNG PACKAGE_ICON_256.PNG "$STAGE/" +cp -r scripts "$STAGE/" +mkdir -p "$STAGE/conf" +cp conf/privilege conf/resource "$STAGE/conf/" +cp "$BUILD_DIR/package.tgz" "$STAGE/" -# ── 3. Assemble outer SPK (UNCOMPRESSED tar) ────────────────────────── echo " → Assembling SPK" -tar -cf "${OUTPUT}" \ - --format=ustar \ - -C "$SCRIPT_DIR" \ - INFO \ - PACKAGE_ICON.PNG \ - PACKAGE_ICON_256.PNG \ - scripts/ \ - -C "$BUILD_DIR" \ - package.tgz \ - checksum +tar -cf "${OUTPUT}" -C "$STAGE" --format=ustar \ + INFO PACKAGE_ICON.PNG PACKAGE_ICON_256.PNG scripts conf package.tgz -# SPK conf/ goes in the outer tar too -tar -rf "${OUTPUT}" \ - --format=ustar \ - -C "$SCRIPT_DIR" \ - conf/privilege \ - conf/resource - -# ── 4. Verify ───────────────────────────────────────────────────────── echo " → Verifying SPK contents" tar -tf "${OUTPUT}" | sort @@ -75,5 +62,4 @@ echo "=== Build complete: ${OUTPUT} ===" echo " Size: ${SIZE} bytes" echo " MD5: $(md5sum "${OUTPUT}" | awk '{print $1}')" -# Cleanup rm -rf "$BUILD_DIR" diff --git a/kit-connect/scripts/postinst b/kit-connect/scripts/postinst index b9ede72..c8aed2e 100755 --- a/kit-connect/scripts/postinst +++ b/kit-connect/scripts/postinst @@ -1,43 +1,39 @@ #!/bin/sh -# kit-connect postinst — register with hub, set up config +# kit-connect postinst — run setup wizard, register with hub, set up config. +# Idempotent — safe to run on upgrades too. + PKG_DIR="/var/packages/kit-connect/target" -CONF="/etc/kit-connect/connect.conf" LOG_TAG="kit-connect" log() { logger -t "$LOG_TAG" -p local0.warn "$*"; } -mkdir -p /etc/kit-connect "$PKG_DIR/var" +mkdir -p /etc/kit-connect /etc/busrouter "$PKG_DIR/var" -# If config doesn't exist, create from template -if [ ! -f "$CONF" ]; then - cp "$PKG_DIR/conf/connect.conf" "$CONF" -fi +# Make scripts and wizard executable +chmod +x "$PKG_DIR/wizard.sh" 2>/dev/null || true +chmod +x "$PKG_DIR/bin/serve-dashboard.sh" 2>/dev/null || true +chmod 600 "$PKG_DIR/bin/connect_id_ed25519" 2>/dev/null || true -# Prompt for device ID if not set -DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || echo "") -if [ -z "$DEVICE_ID" ]; then - DEVICE_ID=$(hostname 2>/dev/null || echo "unknown") -fi +# Run the Keylink IT setup wizard (non-interactive) +# This detects device ID, writes config, registers with hub, starts Tailscale. +if [ -x "$PKG_DIR/wizard.sh" ]; then + log "Running Keylink IT fleet setup wizard..." + "$PKG_DIR/wizard.sh" --auto 2>&1 | while read -r line; do log "$line"; done +else + # Fallback: minimal config + DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || hostname 2>/dev/null || echo "unknown") + echo "$DEVICE_ID" > /etc/busrouter/device-id -# Write device ID into config -sed -i "s/^DEVICE_ID=.*/DEVICE_ID=$DEVICE_ID/" "$CONF" 2>/dev/null - -# Set hostname for Tailscale -sed -i "s/^TAILSCALE_HOSTNAME=.*/TAILSCALE_HOSTNAME=$DEVICE_ID/" "$CONF" 2>/dev/null - -# Install tailscale binaries if not present -if [ ! -f /usr/local/bin/tailscale ]; then - if [ -f "$PKG_DIR/bin/tailscale" ]; then - cp "$PKG_DIR/bin/tailscale" "$PKG_DIR/bin/tailscaled" /usr/local/bin/ 2>/dev/null - chmod +x /usr/local/bin/tailscale /usr/local/bin/tailscaled 2>/dev/null + CONF="/etc/kit-connect/connect.conf" + if [ ! -f "$CONF" ]; then + cp "$PKG_DIR/conf/connect.conf" "$CONF" 2>/dev/null || touch "$CONF" fi + sed -i "s/^DEVICE_ID=.*/DEVICE_ID=${DEVICE_ID}/" "$CONF" 2>/dev/null + sed -i "s/^TAILSCALE_HOSTNAME=.*/TAILSCALE_HOSTNAME=${DEVICE_ID}/" "$CONF" 2>/dev/null + + # Try hub registration + curl -s --connect-timeout 10 "http://162.243.83.36:8080/api/register/${DEVICE_ID}" 2>/dev/null || true fi -# Set key permissions -chmod 600 "$PKG_DIR/bin/connect_id_ed25519" 2>/dev/null - -# Try hub registration (non-fatal — daemon retries) -curl -s --connect-timeout 10 "http://162.243.83.36:8080/api/register/${DEVICE_ID}" 2>/dev/null || true - -log "postinst complete — device=${DEVICE_ID}" +log "postinst complete — device_id=$(cat /etc/busrouter/device-id 2>/dev/null || echo unknown)" exit 0 diff --git a/kit-connect/scripts/start-stop-status b/kit-connect/scripts/start-stop-status index 0ef13d8..f03420f 100755 --- a/kit-connect/scripts/start-stop-status +++ b/kit-connect/scripts/start-stop-status @@ -4,28 +4,64 @@ PKG="kit-connect" PKG_DIR="/var/packages/${PKG}/target" DAEMON="${PKG_DIR}/bin/connect-daemon.sh" DAEMON_PID="${PKG_DIR}/var/daemon.pid" +DASHBOARD="${PKG_DIR}/bin/serve-dashboard.sh" +DASHBOARD_PID="${PKG_DIR}/var/dashboard.pid" case "$1" in start) mkdir -p "${PKG_DIR}/var" + + # Start main daemon if [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null; then - echo "Already running" - exit 0 + echo "Daemon already running" + else + setsid "$DAEMON" & + echo $! > "$DAEMON_PID" + echo "Daemon started" + fi + + # Start dashboard server (port 8089) + if [ -x "$DASHBOARD" ] && command -v busybox >/dev/null 2>&1; then + if [ -f "$DASHBOARD_PID" ] && kill -0 "$(cat "$DASHBOARD_PID")" 2>/dev/null; then + echo "Dashboard already running" + else + "$DASHBOARD" 8089 & + echo $! > "$DASHBOARD_PID" + echo "Dashboard started on port 8089" + fi fi - setsid "$DAEMON" & - echo $! > "$DAEMON_PID" - echo "Started" ;; stop) + # Stop dashboard + if [ -f "$DASHBOARD_PID" ]; then + kill "$(cat "$DASHBOARD_PID")" 2>/dev/null + sleep 1 + kill -9 "$(cat "$DASHBOARD_PID")" 2>/dev/null + rm -f "$DASHBOARD_PID" + echo "Dashboard stopped" + fi + + # Stop daemon if [ -f "$DAEMON_PID" ]; then kill "$(cat "$DAEMON_PID")" 2>/dev/null sleep 1 kill -9 "$(cat "$DAEMON_PID")" 2>/dev/null rm -f "$DAEMON_PID" + echo "Daemon stopped" fi - echo "Stopped" ;; status) + if [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null; then + echo "Daemon: running (PID $(cat "$DAEMON_PID"))" + else + echo "Daemon: stopped" + fi + if [ -f "$DASHBOARD_PID" ] && kill -0 "$(cat "$DASHBOARD_PID")" 2>/dev/null; then + echo "Dashboard: running on port 8089 (PID $(cat "$DASHBOARD_PID"))" + exit 0 + else + echo "Dashboard: stopped" + fi [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null && exit 0 exit 1 ;; diff --git a/kit-connect/wizard.sh b/kit-connect/wizard.sh new file mode 100755 index 0000000..fc665f3 --- /dev/null +++ b/kit-connect/wizard.sh @@ -0,0 +1,137 @@ +#!/bin/sh +# wizard.sh — One-button Keylink IT fleet setup for Synology routers. +# Idempotent — safe to run multiple times. +# Usage: wizard.sh [--auto] [--force] +# --auto Non-interactive (for postinst / web trigger) +# --force Re-register even if already assigned +# +# Compatible: RT2600ac, RT6600ax (all ipq806x SRM routers) + +set -e + +HUB_HOST="162.243.83.36" +HUB_PORT="8080" +CONF="/etc/kit-connect/connect.conf" +AUTO=0 +FORCE=0 +[ "$1" = "--auto" ] && AUTO=1 +[ "$1" = "--force" ] || [ "$2" = "--force" ] && FORCE=1 + +banner() { + echo "" + echo " ╔══════════════════════════════════════════╗" + echo " ║ KEYLINK IT — Pioneer Bus Fleet Setup ║" + echo " ╚══════════════════════════════════════════╝" + echo "" +} + +step() { echo " ▶ $*"; } +ok() { echo " ✓ $*"; } +warn() { echo " ⚠ $*"; } + +banner + +# ── Step 1: Detect device ────────────────────────────────────── +step "Detecting device..." +DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || true) +if [ -z "$DEVICE_ID" ]; then + DEVICE_ID=$(hostname 2>/dev/null | sed 's/[^a-zA-Z0-9_-]//g' || echo "unknown") +fi +mkdir -p /etc/busrouter +echo "$DEVICE_ID" > /etc/busrouter/device-id +ok "Device ID: ${DEVICE_ID}" + +# ── Step 2: Check prerequisites ──────────────────────────────── +step "Checking prerequisites..." +for cmd in curl ssh ssh-keygen; do + command -v "$cmd" >/dev/null 2>&1 || { echo " ✗ Missing: $cmd"; exit 1; } +done +ok "All prerequisites found (curl, ssh)" + +# ── Step 3: Config file ──────────────────────────────────────── +step "Configuring Keylink IT defaults..." +mkdir -p /etc/kit-connect /var/packages/kit-connect/target/var + +if [ ! -f "$CONF" ]; then + # Create from template if available + if [ -f /var/packages/kit-connect/target/conf/connect.conf ]; then + cp /var/packages/kit-connect/target/conf/connect.conf "$CONF" + else + touch "$CONF" + fi +fi + +# Write Keylink IT defaults +_write() { + grep -q "^${1}=" "$CONF" 2>/dev/null && sed -i "s|^${1}=.*|${1}=${2}|" "$CONF" || echo "${1}=${2}" >> "$CONF" +} + +_write "DEVICE_ID" "$DEVICE_ID" +_write "HUB_HOST" "$HUB_HOST" +_write "HUB_PORT" "$HUB_PORT" +_write "TUNNEL_ENABLE" "1" +_write "TUNNEL_REMOTE_HOST" "$HUB_HOST" +_write "TUNNEL_REMOTE_USER" "node" +_write "TUNNEL_REMOTE_SSH_PORT" "22" +_write "TUNNEL_REMOTE_PORT" "0" # auto-assign +_write "TUNNEL_LOCAL_SSH_PORT" "2223" +_write "TUNNEL_RETRY_DELAY" "30" +_write "TAILSCALE_ENABLE" "1" +_write "TAILSCALE_AUTH_KEY" "tskey-auth-kJz8wqNVo211CNTRL-GNL5EFjp5aWQcaWPSVn2aW9TNworKUNBV" +_write "TAILSCALE_HOSTNAME" "$DEVICE_ID" +_write "WATCHDOG_INTERVAL" "60" +_write "FORWARD_FAIL_LIMIT" "2" +ok "Config written to ${CONF}" + +# ── Step 4: Register with hub ────────────────────────────────── +step "Registering with fleet hub (${HUB_HOST}:${HUB_PORT})..." + +resp=$(curl -s --connect-timeout 10 "http://${HUB_HOST}:${HUB_PORT}/api/register/${DEVICE_ID}" 2>/dev/null) || true + +if [ -z "$resp" ]; then + warn "Hub unreachable — will retry on next daemon start" + ok "Config saved locally (port will auto-assign when hub is reachable)" +else + port=$(echo "$resp" | grep -o '"tunnel_port"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*') + status=$(echo "$resp" | grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + tskey=$(echo "$resp" | grep -o '"tailscale_auth_key"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4) + + if [ -n "$port" ] && [ "$port" != "0" ]; then + sed -i "s/^TUNNEL_REMOTE_PORT=.*/TUNNEL_REMOTE_PORT=${port}/" "$CONF" 2>/dev/null + ok "Hub assigned port ${port} (status: ${status:-ok})" + echo "$port" > /var/packages/kit-connect/target/var/hub_port + fi + if [ -n "$tskey" ]; then + sed -i "s|^TAILSCALE_AUTH_KEY=.*|TAILSCALE_AUTH_KEY=${tskey}|" "$CONF" 2>/dev/null + ok "Tailscale key configured" + fi +fi + +# ── Step 5: Dashboard server ─────────────────────────────────── +step "Setting up local dashboard..." +mkdir -p /var/packages/kit-connect/target/www +# Dashboard is served by busybox httpd on port 8089 +ok "Dashboard will be available on port 8089" + +# ── Step 6: Start services ───────────────────────────────────── +step "Starting kit-connect service..." +if [ -x /var/packages/kit-connect/scripts/start-stop-status ]; then + /var/packages/kit-connect/scripts/start-stop-status start 2>/dev/null && \ + ok "Service started" || warn "Service start returned non-zero" +fi + +# ── Done ─────────────────────────────────────────────────────── +ASSIGNED=$(grep '^TUNNEL_REMOTE_PORT=' "$CONF" 2>/dev/null | cut -d= -f2 || echo "pending") +echo "" +echo " ╔══════════════════════════════════════════╗" +echo " ║ SETUP COMPLETE ║" +echo " ╠══════════════════════════════════════════╣" +printf " ║ Device: %-30s ║\n" "$DEVICE_ID" +printf " ║ Port: %-30s ║\n" "$ASSIGNED" +printf " ║ Hub: %-30s ║\n" "${HUB_HOST}:${HUB_PORT}" +echo " ╠══════════════════════════════════════════╣" +echo " ║ Dashboard: http://${DEVICE_ID}:8089/ ║" +echo " ╚══════════════════════════════════════════╝" +echo "" + +exit 0 diff --git a/kit-connect/www/index.html b/kit-connect/www/index.html new file mode 100644 index 0000000..4b83fde --- /dev/null +++ b/kit-connect/www/index.html @@ -0,0 +1,136 @@ + + + + + +Kit Connect — Fleet Status + + + +
+
+ +
Keylink IT Fleet Connectivity
+
+
Loading…
+
+
+ +
+
⚡ One-Click Setup
+

+ Configures Keylink IT fleet defaults — device ID, hub, Tailscale, reverse SSH. + Safe to run multiple times. +

+ +
+
+ +
+
📡 Connectivity Status
+
Loading…
+
+ + +
+ + + + diff --git a/syno-balance/INFO b/syno-balance/INFO index 3ed745c..cad0d0f 100644 --- a/syno-balance/INFO +++ b/syno-balance/INFO @@ -1,6 +1,6 @@ package="syno-balance" version="0.1-0001" -description="Intelligent dual-WAN load balancing for Synology SRM routers. Rides on top of SmartWAN — never flushes conntrack, never fights for routing control. Uses the same 6-factor scoring engine as the GL-XE3000 kit-busrouter. Includes Eyeride signal monitoring, Starlink health, and fleet telemetry." +description="Intelligent dual-WAN load balancing for Synology SRM routers. Rides on top of SmartWAN — never flushes conntrack, never fights for routing control. Uses the same 6-factor scoring engine as the GL-XE3000 kit-busrouter. Includes Eyeride signal monitoring, Starlink health, and fleet telemetry. Compatible: RT2600ac, RT6600ax." maintainer="Keylink IT" arch="noarch" firmware="1.3.1-9346"