From 6f9dd3aa17268584950ccb2c0179b02e452587c7 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Thu, 26 Mar 2026 15:23:55 +0100 Subject: [PATCH] feat(luci-app-masterlink): Add sbx-mesh-invite and improve join script New sbx-mesh-invite script for master nodes: - Generates secure invite tokens with auto-approve option - Outputs copy-pasteable join URL and CLI command - Supports --ip flag to specify master address - Prefers 192.168.x.x addresses in auto-detection Improvements to sbx-mesh-join: - Creates UCI config file if missing (touch + set) - Properly initializes config section before setting options - Handles fresh installs without existing masterlink config Workflow: Master: sbx-mesh-invite --ip 192.168.255.200 Peer: sbx-mesh-join 192.168.255.200 Co-Authored-By: Claude Opus 4.5 --- .../root/usr/bin/sbx-mesh-invite | 158 ++++++++++++++++++ .../root/usr/bin/sbx-mesh-join | 5 + 2 files changed, 163 insertions(+) create mode 100755 package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-invite diff --git a/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-invite b/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-invite new file mode 100755 index 00000000..88e16f29 --- /dev/null +++ b/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-invite @@ -0,0 +1,158 @@ +#!/bin/sh +# SecuBox Mesh Invite Generator +# Generates a join URL that can be shared with peer nodes +# Usage: sbx-mesh-invite [--auto-approve] [--ttl SECONDS] + +set -e + +TOKENS_FILE="/var/lib/secubox/p2p/master-link/tokens.json" +AUTO_APPROVE="true" +TTL=3600 +MASTER_IP="" + +# Parse arguments +while [ $# -gt 0 ]; do + case "$1" in + --auto-approve|-a) + AUTO_APPROVE="true" + shift + ;; + --manual-approve|-m) + AUTO_APPROVE="false" + shift + ;; + --ttl|-t) + TTL="$2" + shift 2 + ;; + --ip|-i) + MASTER_IP="$2" + shift 2 + ;; + --help|-h) + cat << 'EOF' +SecuBox Mesh Invite Generator + +Usage: + sbx-mesh-invite [options] + +Options: + -a, --auto-approve Auto-approve joining nodes (default) + -m, --manual-approve Require manual approval + -t, --ttl SECONDS Token validity in seconds (default: 3600) + -i, --ip ADDRESS Master IP address for the invite URL + -h, --help Show this help + +Output: + Prints a join URL that can be copy-pasted to peer nodes. + On the peer, run: sbx-mesh-join +EOF + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +# Ensure tokens directory exists +mkdir -p "$(dirname "$TOKENS_FILE")" + +# Generate random token (32 hex chars) +if command -v openssl >/dev/null; then + TOKEN=$(openssl rand -hex 16) +elif [ -r /dev/urandom ]; then + TOKEN=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') +else + # Fallback: use date + random + TOKEN=$(date +%s%N | sha256sum | head -c 32) +fi + +# Calculate hash +if command -v sha256sum >/dev/null; then + HASH=$(printf '%s' "$TOKEN" | sha256sum | cut -d' ' -f1) +elif command -v openssl >/dev/null; then + HASH=$(printf '%s' "$TOKEN" | openssl dgst -sha256 | awk '{print $2}') +else + echo "Error: No sha256 tool available" >&2 + exit 1 +fi + +# Get timestamps +NOW=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S) +NOW_TS=$(date +%s) +EXPIRES_TS=$((NOW_TS + TTL)) +EXPIRES=$(date -Iseconds -d "@$EXPIRES_TS" 2>/dev/null || date -r "$EXPIRES_TS" -Iseconds 2>/dev/null || echo "$EXPIRES_TS") + +# Get local IP for URL +LOCAL_IP="$MASTER_IP" +if [ -z "$LOCAL_IP" ]; then + # Try common LAN interfaces first, prefer 192.168.x.x addresses + for iface in br-lan eth0 enp0s8 enp0s3 ens3; do + candidate=$(ip -4 addr show "$iface" 2>/dev/null | grep -oE 'inet 192\.168\.[0-9.]+' | awk '{print $2}' | head -1) + if [ -n "$candidate" ]; then + LOCAL_IP="$candidate" + break + fi + done + # Fallback to any non-loopback, non-10.x address + if [ -z "$LOCAL_IP" ]; then + LOCAL_IP=$(ip -4 addr show 2>/dev/null | grep -oE 'inet [0-9.]+' | grep -v '127.0.0' | grep -v 'inet 10\.' | awk '{print $2}' | head -1) + fi + [ -z "$LOCAL_IP" ] && LOCAL_IP=$(hostname -I 2>/dev/null | awk '{print $1}') + [ -z "$LOCAL_IP" ] && LOCAL_IP="" +fi + +# Create new token entry +NEW_TOKEN="{ + \"hash\": \"$HASH\", + \"type\": \"join\", + \"created\": \"$NOW\", + \"expires\": \"$EXPIRES\", + \"expires_ts\": $EXPIRES_TS, + \"ttl\": $TTL, + \"status\": \"active\", + \"auto_approve\": $AUTO_APPROVE +}" + +# Add to tokens file +if [ -f "$TOKENS_FILE" ]; then + # Append to existing array using Python or jq + if command -v python3 >/dev/null; then + python3 << PYEOF +import json +with open("$TOKENS_FILE", "r") as f: + tokens = json.load(f) +tokens.append(json.loads('''$NEW_TOKEN''')) +with open("$TOKENS_FILE", "w") as f: + json.dump(tokens, f, indent=2) +PYEOF + elif command -v jq >/dev/null; then + tmp=$(mktemp) + jq ". + [$NEW_TOKEN]" "$TOKENS_FILE" > "$tmp" && mv "$tmp" "$TOKENS_FILE" + else + # Manual append (risky but works for simple cases) + sed -i 's/]$/,'"$(echo "$NEW_TOKEN" | tr -d '\n')"']/' "$TOKENS_FILE" + fi +else + echo "[$NEW_TOKEN]" > "$TOKENS_FILE" +fi + +# Output the invite URL +echo "" +echo "Mesh Invite Generated" +echo "=====================" +echo "" +echo "Token: $TOKEN" +echo "Expires: $EXPIRES" +echo "Auto-approve: $AUTO_APPROVE" +echo "" +echo "Join URL (copy this to the peer node):" +echo "" +echo " https://${LOCAL_IP}/master-link/?token=${TOKEN}" +echo "" +echo "Or run on the peer:" +echo "" +echo " sbx-mesh-join ${LOCAL_IP} ${TOKEN}" +echo "" diff --git a/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-join b/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-join index e72e394f..2cf7fe54 100644 --- a/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-join +++ b/package/secubox/luci-app-masterlink/root/usr/bin/sbx-mesh-join @@ -190,6 +190,9 @@ main() { # Save to UCI if available if command -v uci >/dev/null; then log_step "Saving configuration..." + # Ensure config file and section exist + [ -f /etc/config/masterlink ] || touch /etc/config/masterlink + uci -q get masterlink.settings >/dev/null || uci set masterlink.settings='mesh' uci set masterlink.settings.enabled='1' uci set masterlink.settings.role='peer' uci set masterlink.settings.status='approved' @@ -207,6 +210,8 @@ main() { # Save pending state if command -v uci >/dev/null; then + [ -f /etc/config/masterlink ] || touch /etc/config/masterlink + uci -q get masterlink.settings >/dev/null || uci set masterlink.settings='mesh' uci set masterlink.settings.enabled='1' uci set masterlink.settings.role='peer' uci set masterlink.settings.status='pending'