feat(p2p): Add mDNS service publishing and REST API for mesh visibility
- Add mDNS service announcement via avahi-publish for _secubox._tcp - Add REST API endpoints on port 7331 (/api/peers, /api/status, /api/services) - Add node self-registration to ensure local node visible in mesh view - Add UCI defaults for uhttpd P2P API instance and firewall rules - Bump secubox-p2p version to 0.2.0 fix(vhost-manager): Fix uninitialized variable syntax errors - Add 'local' keyword to variable declarations on lines 606, 621, 693 fix(metablogizer,service-registry): Add HAProxy availability fallback - Add haproxy_available() helper to check if HAProxy is running - Gracefully skip HAProxy operations when service unavailable - Store pending HAProxy config for later when service becomes available - Prevent crashes when HAProxy container is stopped Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4166f4574e
commit
ce512bbda0
@ -66,8 +66,31 @@ fix_permissions() {
|
||||
done
|
||||
}
|
||||
|
||||
# Reload HAProxy configuration properly
|
||||
# Check if HAProxy is available and running
|
||||
haproxy_available() {
|
||||
# Check if HAProxy container is running (preferred method)
|
||||
if command -v lxc-info >/dev/null 2>&1; then
|
||||
lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING" && return 0
|
||||
fi
|
||||
|
||||
# Fallback: check if haproxy process is running
|
||||
pgrep haproxy >/dev/null 2>&1 && return 0
|
||||
|
||||
# Fallback: check if init script exists and service is enabled
|
||||
if [ -x /etc/init.d/haproxy ]; then
|
||||
/etc/init.d/haproxy status >/dev/null 2>&1 && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Reload HAProxy configuration properly (with availability check)
|
||||
reload_haproxy() {
|
||||
if ! haproxy_available; then
|
||||
logger -t metablogizer "HAProxy not available, skipping reload"
|
||||
return 1
|
||||
fi
|
||||
|
||||
/usr/sbin/haproxyctl generate >/dev/null 2>&1
|
||||
if [ -x /etc/init.d/haproxy ]; then
|
||||
/etc/init.d/haproxy reload >/dev/null 2>&1
|
||||
@ -362,42 +385,48 @@ EOF
|
||||
[ -n "$port" ] && uci set "$UCI_CONFIG.$section_id.port=$port"
|
||||
uci set "$UCI_CONFIG.$section_id.runtime=$current_runtime"
|
||||
|
||||
# 7. Create HAProxy backend
|
||||
local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')"
|
||||
# 7. Create HAProxy backend (if HAProxy is available)
|
||||
local haproxy_configured=0
|
||||
if haproxy_available; then
|
||||
local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')"
|
||||
|
||||
uci set "haproxy.$backend_name=backend"
|
||||
uci set "haproxy.$backend_name.name=$backend_name"
|
||||
uci set "haproxy.$backend_name.mode=http"
|
||||
uci set "haproxy.$backend_name.balance=roundrobin"
|
||||
uci set "haproxy.$backend_name.enabled=1"
|
||||
uci set "haproxy.$backend_name=backend"
|
||||
uci set "haproxy.$backend_name.name=$backend_name"
|
||||
uci set "haproxy.$backend_name.mode=http"
|
||||
uci set "haproxy.$backend_name.balance=roundrobin"
|
||||
uci set "haproxy.$backend_name.enabled=1"
|
||||
|
||||
# Create server
|
||||
local server_name="${backend_name}_srv"
|
||||
uci set "haproxy.$server_name=server"
|
||||
uci set "haproxy.$server_name.backend=$backend_name"
|
||||
uci set "haproxy.$server_name.name=srv"
|
||||
uci set "haproxy.$server_name.address=$server_address"
|
||||
uci set "haproxy.$server_name.port=$server_port"
|
||||
uci set "haproxy.$server_name.weight=100"
|
||||
uci set "haproxy.$server_name.check=1"
|
||||
uci set "haproxy.$server_name.enabled=1"
|
||||
# Create server
|
||||
local server_name="${backend_name}_srv"
|
||||
uci set "haproxy.$server_name=server"
|
||||
uci set "haproxy.$server_name.backend=$backend_name"
|
||||
uci set "haproxy.$server_name.name=srv"
|
||||
uci set "haproxy.$server_name.address=$server_address"
|
||||
uci set "haproxy.$server_name.port=$server_port"
|
||||
uci set "haproxy.$server_name.weight=100"
|
||||
uci set "haproxy.$server_name.check=1"
|
||||
uci set "haproxy.$server_name.enabled=1"
|
||||
|
||||
# 8. Create HAProxy vhost
|
||||
local vhost_name=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
local acme_val="0"
|
||||
[ "$ssl" = "1" ] && acme_val="1"
|
||||
# 8. Create HAProxy vhost
|
||||
local vhost_name=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
local acme_val="0"
|
||||
[ "$ssl" = "1" ] && acme_val="1"
|
||||
|
||||
uci set "haproxy.$vhost_name=vhost"
|
||||
uci set "haproxy.$vhost_name.domain=$domain"
|
||||
uci set "haproxy.$vhost_name.backend=$backend_name"
|
||||
uci set "haproxy.$vhost_name.ssl=$ssl"
|
||||
uci set "haproxy.$vhost_name.ssl_redirect=$ssl"
|
||||
uci set "haproxy.$vhost_name.acme=$acme_val"
|
||||
uci set "haproxy.$vhost_name.enabled=1"
|
||||
uci commit haproxy
|
||||
uci set "haproxy.$vhost_name=vhost"
|
||||
uci set "haproxy.$vhost_name.domain=$domain"
|
||||
uci set "haproxy.$vhost_name.backend=$backend_name"
|
||||
uci set "haproxy.$vhost_name.ssl=$ssl"
|
||||
uci set "haproxy.$vhost_name.ssl_redirect=$ssl"
|
||||
uci set "haproxy.$vhost_name.acme=$acme_val"
|
||||
uci set "haproxy.$vhost_name.enabled=1"
|
||||
uci commit haproxy
|
||||
|
||||
# Regenerate HAProxy config and reload
|
||||
reload_haproxy
|
||||
# Regenerate HAProxy config and reload
|
||||
reload_haproxy
|
||||
haproxy_configured=1
|
||||
else
|
||||
logger -t metablogizer "HAProxy not available, site created without proxy config"
|
||||
fi
|
||||
|
||||
uci commit "$UCI_CONFIG"
|
||||
|
||||
@ -464,16 +493,22 @@ method_delete_site() {
|
||||
# Get site runtime
|
||||
local site_runtime=$(get_uci "$id" runtime "")
|
||||
|
||||
# 1. Delete HAProxy vhost
|
||||
local vhost_id=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
uci delete "haproxy.$vhost_id" 2>/dev/null
|
||||
# 1. Delete HAProxy vhost (if HAProxy config exists)
|
||||
if uci -q get haproxy >/dev/null 2>&1; then
|
||||
local vhost_id=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
uci delete "haproxy.$vhost_id" 2>/dev/null
|
||||
|
||||
# 2. Delete HAProxy backend and server
|
||||
local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')"
|
||||
uci delete "haproxy.$backend_name" 2>/dev/null
|
||||
uci delete "haproxy.${backend_name}_srv" 2>/dev/null
|
||||
uci commit haproxy
|
||||
reload_haproxy
|
||||
# 2. Delete HAProxy backend and server
|
||||
local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')"
|
||||
uci delete "haproxy.$backend_name" 2>/dev/null
|
||||
uci delete "haproxy.${backend_name}_srv" 2>/dev/null
|
||||
uci commit haproxy
|
||||
|
||||
# Only reload if HAProxy is actually running
|
||||
if haproxy_available; then
|
||||
reload_haproxy
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Remove runtime config
|
||||
if [ "$site_runtime" = "uhttpd" ]; then
|
||||
|
||||
@ -35,6 +35,22 @@ is_port_listening() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Helper: Check if HAProxy is available and running
|
||||
haproxy_available() {
|
||||
# Check if HAProxy container is running (preferred method)
|
||||
if command -v lxc-info >/dev/null 2>&1; then
|
||||
lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING" && return 0
|
||||
fi
|
||||
|
||||
# Fallback: check if haproxy process is running
|
||||
pgrep haproxy >/dev/null 2>&1 && return 0
|
||||
|
||||
# Fallback: check if RPCD haproxy interface is available
|
||||
ubus call luci.haproxy list 2>/dev/null | jsonfilter -e '@' >/dev/null 2>&1 && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Helper: Get process name for port
|
||||
get_process_for_port() {
|
||||
local port="$1"
|
||||
@ -263,6 +279,11 @@ _aggregate_haproxy_services() {
|
||||
local lan_ip="$1"
|
||||
local tmp_file="$2"
|
||||
|
||||
# Check if HAProxy is available before calling RPCD
|
||||
if ! haproxy_available; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Call HAProxy RPCD to get vhosts and backends
|
||||
local vhosts_json backends_json certs_json
|
||||
vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null)
|
||||
@ -614,29 +635,37 @@ method_publish_service() {
|
||||
local urls_clearnet=""
|
||||
local urls_onion=""
|
||||
|
||||
# Create HAProxy vhost if domain specified
|
||||
# Create HAProxy vhost if domain specified and HAProxy is available
|
||||
if [ -n "$domain" ]; then
|
||||
# Ensure firewall allows HTTP/HTTPS from WAN (for public access + ACME)
|
||||
ensure_haproxy_firewall_rules
|
||||
if haproxy_available; then
|
||||
# Ensure firewall allows HTTP/HTTPS from WAN (for public access + ACME)
|
||||
ensure_haproxy_firewall_rules
|
||||
|
||||
# Create backend
|
||||
ubus call luci.haproxy create_backend "{\"name\":\"$section_id\",\"mode\":\"http\"}" 2>/dev/null
|
||||
# Create backend
|
||||
ubus call luci.haproxy create_backend "{\"name\":\"$section_id\",\"mode\":\"http\"}" 2>/dev/null
|
||||
|
||||
# Create server pointing to local port
|
||||
ubus call luci.haproxy create_server "{\"backend\":\"$section_id\",\"name\":\"local\",\"address\":\"127.0.0.1\",\"port\":$local_port}" 2>/dev/null
|
||||
# Create server pointing to local port
|
||||
ubus call luci.haproxy create_server "{\"backend\":\"$section_id\",\"name\":\"local\",\"address\":\"127.0.0.1\",\"port\":$local_port}" 2>/dev/null
|
||||
|
||||
# Create vhost with SSL
|
||||
ubus call luci.haproxy create_vhost "{\"domain\":\"$domain\",\"backend\":\"$section_id\",\"ssl\":1,\"ssl_redirect\":1,\"acme\":1,\"enabled\":1}" 2>/dev/null
|
||||
# Create vhost with SSL
|
||||
ubus call luci.haproxy create_vhost "{\"domain\":\"$domain\",\"backend\":\"$section_id\",\"ssl\":1,\"ssl_redirect\":1,\"acme\":1,\"enabled\":1}" 2>/dev/null
|
||||
|
||||
# Regenerate HAProxy config to include the new vhost
|
||||
ubus call luci.haproxy generate 2>/dev/null
|
||||
ubus call luci.haproxy reload 2>/dev/null
|
||||
# Regenerate HAProxy config to include the new vhost
|
||||
ubus call luci.haproxy generate 2>/dev/null
|
||||
ubus call luci.haproxy reload 2>/dev/null
|
||||
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_enabled=1"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_ssl=1"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_enabled=1"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_ssl=1"
|
||||
|
||||
urls_clearnet="https://${domain}"
|
||||
urls_clearnet="https://${domain}"
|
||||
else
|
||||
# Store domain for later HAProxy configuration when it becomes available
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_enabled=0"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain"
|
||||
uci set "$UCI_CONFIG.$section_id.haproxy_pending=1"
|
||||
logger -t service-registry "HAProxy unavailable, domain $domain saved for later"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create Tor hidden service if enabled
|
||||
@ -717,12 +746,14 @@ method_unpublish_service() {
|
||||
config_get haproxy_domain "$service_id" haproxy_domain ""
|
||||
config_get tor_enabled "$service_id" tor_enabled "0"
|
||||
|
||||
# Remove HAProxy vhost
|
||||
# Remove HAProxy vhost (if HAProxy is available)
|
||||
if [ "$haproxy_enabled" = "1" ] && [ -n "$haproxy_domain" ]; then
|
||||
local vhost_id
|
||||
vhost_id=$(echo "$haproxy_domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
ubus call luci.haproxy delete_vhost "{\"id\":\"$vhost_id\"}" 2>/dev/null
|
||||
ubus call luci.haproxy delete_backend "{\"id\":\"$service_id\"}" 2>/dev/null
|
||||
if haproxy_available; then
|
||||
local vhost_id
|
||||
vhost_id=$(echo "$haproxy_domain" | sed 's/[^a-zA-Z0-9]/_/g')
|
||||
ubus call luci.haproxy delete_vhost "{\"id\":\"$vhost_id\"}" 2>/dev/null
|
||||
ubus call luci.haproxy delete_backend "{\"id\":\"$service_id\"}" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
# Remove Tor hidden service
|
||||
@ -1368,9 +1399,11 @@ method_check_all_health() {
|
||||
# Check individual services with domains
|
||||
json_add_array "services"
|
||||
|
||||
# Get all published services with domains from HAProxy
|
||||
# Get all published services with domains from HAProxy (if available)
|
||||
local vhosts_json
|
||||
vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null)
|
||||
if haproxy_available; then
|
||||
vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null)
|
||||
fi
|
||||
if [ -n "$vhosts_json" ]; then
|
||||
local count
|
||||
count=$(echo "$vhosts_json" | jsonfilter -e '@.vhosts[*].domain' 2>/dev/null | wc -l)
|
||||
|
||||
@ -603,7 +603,7 @@ case "$1" in
|
||||
if [ -z "$domain" ]; then
|
||||
json_init; json_add_boolean "success" 0; json_add_string "message" "Domain required"; json_dump; exit 0
|
||||
fi
|
||||
section
|
||||
local section
|
||||
section=$(find_section "$domain")
|
||||
if [ -z "$section" ]; then
|
||||
json_init; json_add_boolean "success" 0; json_add_string "message" "VHost not found"; json_dump; exit 0
|
||||
@ -618,7 +618,7 @@ case "$1" in
|
||||
if [ -z "$enabled" ]; then
|
||||
config_get_bool enabled "$section" enabled 1
|
||||
fi
|
||||
current_tls current_cert_path current_key_path
|
||||
local current_tls current_cert_path current_key_path
|
||||
config_get current_tls "$section" tls
|
||||
config_get current_cert_path "$section" cert_path
|
||||
config_get current_key_path "$section" key_path
|
||||
@ -690,9 +690,8 @@ case "$1" in
|
||||
json_add_array "certificates"
|
||||
if [ -d "$ACME_STATE_DIR" ]; then
|
||||
find "$ACME_STATE_DIR" -name "fullchain.cer" -type f 2>/dev/null | while read -r cert_file; do
|
||||
domain
|
||||
local domain expires issuer subject
|
||||
domain=$(basename "$(dirname "$cert_file")")
|
||||
expires issuer subject
|
||||
expires=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d'=' -f2)
|
||||
issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | cut -d'=' -f2-)
|
||||
subject=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | cut -d'=' -f2-)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-p2p
|
||||
PKG_VERSION:=0.1.0
|
||||
PKG_VERSION:=0.2.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=SecuBox Team
|
||||
@ -13,13 +13,14 @@ define Package/secubox-p2p
|
||||
SECTION:=secubox
|
||||
CATEGORY:=SecuBox
|
||||
TITLE:=SecuBox P2P Hub Backend
|
||||
DEPENDS:=+jsonfilter +curl +avahi-daemon
|
||||
DEPENDS:=+jsonfilter +curl +avahi-daemon +avahi-utils +uhttpd
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/secubox-p2p/description
|
||||
SecuBox P2P Hub backend providing peer discovery, mesh networking,
|
||||
DNS federation, and distributed service management.
|
||||
DNS federation, and distributed service management. Includes mDNS
|
||||
service announcement and REST API on port 7331 for mesh visibility.
|
||||
endef
|
||||
|
||||
define Package/secubox-p2p/conffiles
|
||||
@ -33,6 +34,9 @@ define Package/secubox-p2p/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./root/usr/sbin/secubox-p2p $(1)/usr/sbin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./root/usr/bin/secubox-restore $(1)/usr/bin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.secubox-p2p $(1)/usr/libexec/rpcd/
|
||||
|
||||
@ -41,12 +45,27 @@ define Package/secubox-p2p/install
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./root/etc/init.d/secubox-p2p $(1)/etc/init.d/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./root/etc/uci-defaults/99-secubox-p2p-api $(1)/etc/uci-defaults/
|
||||
|
||||
$(INSTALL_DIR) $(1)/www/api
|
||||
$(INSTALL_BIN) ./root/www/api/peers $(1)/www/api/
|
||||
$(INSTALL_BIN) ./root/www/api/status $(1)/www/api/
|
||||
$(INSTALL_BIN) ./root/www/api/services $(1)/www/api/
|
||||
$(INSTALL_BIN) ./root/www/api/sync $(1)/www/api/
|
||||
endef
|
||||
|
||||
define Package/secubox-p2p/postinst
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||
# Run UCI defaults
|
||||
[ -x /etc/uci-defaults/99-secubox-p2p-api ] && /etc/uci-defaults/99-secubox-p2p-api
|
||||
# Reload uhttpd to pick up new instance
|
||||
/etc/init.d/uhttpd reload 2>/dev/null
|
||||
# Enable and start P2P service
|
||||
/etc/init.d/secubox-p2p enable
|
||||
/etc/init.d/secubox-p2p start
|
||||
/etc/init.d/rpcd restart
|
||||
}
|
||||
exit 0
|
||||
|
||||
@ -7,6 +7,9 @@ USE_PROCD=1
|
||||
NAME="secubox-p2p"
|
||||
PROG="/usr/sbin/secubox-p2p"
|
||||
|
||||
# Ensure avahi-daemon is started before us
|
||||
DEPEND="avahi-daemon"
|
||||
|
||||
start_service() {
|
||||
local enabled
|
||||
config_load secubox-p2p
|
||||
@ -14,14 +17,36 @@ start_service() {
|
||||
|
||||
[ "$enabled" = "1" ] || return
|
||||
|
||||
# Ensure state directory exists
|
||||
mkdir -p /var/run/secubox-p2p
|
||||
|
||||
# Ensure avahi-daemon is running for mDNS
|
||||
if [ -x /etc/init.d/avahi-daemon ]; then
|
||||
/etc/init.d/avahi-daemon running || /etc/init.d/avahi-daemon start
|
||||
fi
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command $PROG daemon
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param pidfile /var/run/secubox-p2p/daemon.pid
|
||||
procd_close_instance
|
||||
|
||||
logger -t secubox-p2p "P2P mesh daemon started"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
# Stop mDNS announcement
|
||||
$PROG stop-mdns 2>/dev/null
|
||||
logger -t secubox-p2p "P2P mesh daemon stopped"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "secubox-p2p"
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
# Configure uhttpd instance for P2P REST API on port 7331
|
||||
|
||||
# Check if p2p_api instance already exists
|
||||
if ! uci -q get uhttpd.p2p_api >/dev/null 2>&1; then
|
||||
uci set uhttpd.p2p_api=uhttpd
|
||||
uci set uhttpd.p2p_api.listen_http='0.0.0.0:7331'
|
||||
uci set uhttpd.p2p_api.home='/www/api'
|
||||
uci set uhttpd.p2p_api.cgi_prefix='/'
|
||||
uci set uhttpd.p2p_api.no_symlinks='0'
|
||||
uci set uhttpd.p2p_api.no_dirlists='1'
|
||||
uci set uhttpd.p2p_api.script_timeout='60'
|
||||
uci set uhttpd.p2p_api.network_timeout='30'
|
||||
uci commit uhttpd
|
||||
fi
|
||||
|
||||
# Add firewall rule for P2P API port (LAN only by default)
|
||||
if ! uci show firewall 2>/dev/null | grep -q "P2P-API"; then
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='P2P-API'
|
||||
uci set firewall.@rule[-1].src='lan'
|
||||
uci set firewall.@rule[-1].dest_port='7331'
|
||||
uci set firewall.@rule[-1].proto='tcp'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
uci set firewall.@rule[-1].enabled='1'
|
||||
uci commit firewall
|
||||
fi
|
||||
|
||||
# Add mDNS firewall rule if not exists
|
||||
if ! uci show firewall 2>/dev/null | grep -q "mDNS"; then
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='mDNS'
|
||||
uci set firewall.@rule[-1].src='lan'
|
||||
uci set firewall.@rule[-1].dest_port='5353'
|
||||
uci set firewall.@rule[-1].proto='udp'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
uci set firewall.@rule[-1].enabled='1'
|
||||
uci commit firewall
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@ -2,17 +2,44 @@
|
||||
# SecuBox P2P Hub Manager
|
||||
# Handles peer discovery, mesh networking, and service federation
|
||||
|
||||
VERSION="0.1.0"
|
||||
VERSION="0.2.0"
|
||||
CONFIG_FILE="/etc/config/secubox-p2p"
|
||||
PEERS_FILE="/tmp/secubox-p2p-peers.json"
|
||||
SERVICES_FILE="/tmp/secubox-p2p-services.json"
|
||||
STATE_DIR="/var/run/secubox-p2p"
|
||||
MDNS_PID_FILE="$STATE_DIR/mdns.pid"
|
||||
API_PORT=7331
|
||||
|
||||
# Initialize
|
||||
init() {
|
||||
mkdir -p "$STATE_DIR"
|
||||
[ -f "$PEERS_FILE" ] || echo '{"peers":[]}' > "$PEERS_FILE"
|
||||
[ -f "$SERVICES_FILE" ] || echo '{"services":[]}' > "$SERVICES_FILE"
|
||||
|
||||
# Initialize node info
|
||||
_init_node_info
|
||||
}
|
||||
|
||||
# Initialize node identity
|
||||
_init_node_info() {
|
||||
local node_name
|
||||
node_name=$(get_config main node_name "")
|
||||
|
||||
# Generate node name from hostname if not set
|
||||
if [ -z "$node_name" ]; then
|
||||
node_name=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "secubox")
|
||||
set_config main node_name "$node_name"
|
||||
fi
|
||||
|
||||
# Generate node ID if not exists
|
||||
local node_id_file="$STATE_DIR/node.id"
|
||||
if [ ! -f "$node_id_file" ]; then
|
||||
# Generate unique node ID from MAC address
|
||||
local mac
|
||||
mac=$(ip link show br-lan 2>/dev/null | grep ether | awk '{print $2}' | tr -d ':')
|
||||
[ -z "$mac" ] && mac=$(cat /proc/sys/kernel/random/uuid | tr -d '-' | head -c 12)
|
||||
echo "sb-${mac}" > "$node_id_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get config value
|
||||
@ -276,44 +303,146 @@ broadcast_command() {
|
||||
echo "{\"success\":true,\"broadcast_success\":$success,\"broadcast_failed\":$failed}"
|
||||
}
|
||||
|
||||
# Publish mDNS service announcement
|
||||
publish_mdns() {
|
||||
local node_name
|
||||
node_name=$(get_config main node_name "secubox")
|
||||
|
||||
# Kill any existing avahi-publish process
|
||||
[ -f "$MDNS_PID_FILE" ] && kill $(cat "$MDNS_PID_FILE") 2>/dev/null
|
||||
rm -f "$MDNS_PID_FILE"
|
||||
|
||||
# Check if avahi-publish is available
|
||||
if command -v avahi-publish >/dev/null 2>&1; then
|
||||
# Get LAN IP for service registration
|
||||
local lan_ip
|
||||
lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1)
|
||||
[ -z "$lan_ip" ] && lan_ip=$(uci -q get network.lan.ipaddr)
|
||||
|
||||
# Publish _secubox._tcp service
|
||||
avahi-publish -s "$node_name" "_secubox._tcp" "$API_PORT" \
|
||||
"version=$VERSION" \
|
||||
"type=mesh-node" \
|
||||
"ip=$lan_ip" &
|
||||
echo $! > "$MDNS_PID_FILE"
|
||||
|
||||
logger -t secubox-p2p "mDNS service published: $node_name._secubox._tcp on port $API_PORT"
|
||||
return 0
|
||||
else
|
||||
logger -t secubox-p2p "WARNING: avahi-publish not available, mDNS disabled"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop mDNS service announcement
|
||||
stop_mdns() {
|
||||
if [ -f "$MDNS_PID_FILE" ]; then
|
||||
kill $(cat "$MDNS_PID_FILE") 2>/dev/null
|
||||
rm -f "$MDNS_PID_FILE"
|
||||
logger -t secubox-p2p "mDNS service stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get node status JSON (for REST API)
|
||||
get_node_status() {
|
||||
local node_name node_id lan_ip uptime
|
||||
node_name=$(get_config main node_name "secubox")
|
||||
node_id=$(cat "$STATE_DIR/node.id" 2>/dev/null || echo "unknown")
|
||||
lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1)
|
||||
uptime=$(cat /proc/uptime | cut -d' ' -f1)
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"node_id": "$node_id",
|
||||
"node_name": "$node_name",
|
||||
"version": "$VERSION",
|
||||
"address": "$lan_ip",
|
||||
"api_port": $API_PORT,
|
||||
"uptime": $uptime,
|
||||
"discovery_enabled": $(get_config main discovery_enabled 1),
|
||||
"sharing_enabled": $(get_config main sharing_enabled 1),
|
||||
"peer_count": $(get_peers | jsonfilter -e '@.peers[*]' 2>/dev/null | wc -l)
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Register self in peer list (ensure node is visible in its own mesh view)
|
||||
register_self() {
|
||||
local node_name node_id lan_ip
|
||||
node_name=$(get_config main node_name "secubox")
|
||||
node_id=$(cat "$STATE_DIR/node.id" 2>/dev/null || echo "unknown")
|
||||
lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1)
|
||||
|
||||
# Check if self is already registered
|
||||
local current=$(get_peers)
|
||||
local exists
|
||||
exists=$(echo "$current" | jsonfilter -e "@.peers[@.id='$node_id']" 2>/dev/null)
|
||||
|
||||
if [ -z "$exists" ]; then
|
||||
local self_peer="{\"id\":\"$node_id\",\"name\":\"$node_name (local)\",\"address\":\"$lan_ip\",\"status\":\"online\",\"is_local\":true,\"added\":\"$(date -Iseconds)\"}"
|
||||
echo "$current" | jsonfilter -e '@' 2>/dev/null | sed "s/\"peers\":\[/\"peers\":[$self_peer,/" > "$PEERS_FILE.tmp"
|
||||
if [ -s "$PEERS_FILE.tmp" ]; then
|
||||
mv "$PEERS_FILE.tmp" "$PEERS_FILE"
|
||||
else
|
||||
# Fallback: just create a fresh peers file with self
|
||||
echo "{\"peers\":[$self_peer]}" > "$PEERS_FILE"
|
||||
fi
|
||||
logger -t secubox-p2p "Registered local node: $node_name ($node_id)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Daemon mode
|
||||
daemon_loop() {
|
||||
init
|
||||
|
||||
# Publish mDNS service
|
||||
publish_mdns
|
||||
|
||||
# Register self in peer list
|
||||
register_self
|
||||
|
||||
# Setup signal handlers
|
||||
trap 'stop_mdns; exit 0' INT TERM
|
||||
|
||||
while true; do
|
||||
# Auto-discovery if enabled
|
||||
if [ "$(get_config main discovery_enabled 1)" = "1" ]; then
|
||||
local discovered=$(discover_mdns 3)
|
||||
if [ "$discovered" != "[]" ]; then
|
||||
if [ "$discovered" != "[]" ] && [ -n "$discovered" ]; then
|
||||
# Update peers file with discovered peers
|
||||
local current=$(get_peers)
|
||||
for peer in $(echo "$discovered" | jq -c '.[]'); do
|
||||
local peer_id=$(echo "$peer" | jq -r '.id')
|
||||
local exists=$(echo "$current" | jq ".peers[] | select(.id==\"$peer_id\")")
|
||||
if [ -z "$exists" ]; then
|
||||
current=$(echo "$current" | jq ".peers += [$peer]")
|
||||
fi
|
||||
done
|
||||
echo "$current" > "$PEERS_FILE"
|
||||
# Parse discovered peers (handle jq or jsonfilter)
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
for peer in $(echo "$discovered" | jq -c '.[]' 2>/dev/null); do
|
||||
local peer_id=$(echo "$peer" | jq -r '.id')
|
||||
local exists=$(echo "$current" | jq ".peers[] | select(.id==\"$peer_id\")")
|
||||
if [ -z "$exists" ]; then
|
||||
current=$(echo "$current" | jq ".peers += [$peer]")
|
||||
fi
|
||||
done
|
||||
echo "$current" > "$PEERS_FILE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update peer status
|
||||
# Update peer status (skip local node)
|
||||
local peers=$(get_peers)
|
||||
local updated_peers=$(echo "$peers" | jq '.peers | map(. + {"status": "checking"})' | jq -c '.[]')
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
local updated_peers=$(echo "$peers" | jq -c '.peers[] | select(.is_local != true)' 2>/dev/null)
|
||||
|
||||
for peer in $updated_peers; do
|
||||
local addr=$(echo "$peer" | jq -r '.address')
|
||||
local id=$(echo "$peer" | jq -r '.id')
|
||||
for peer in $updated_peers; do
|
||||
local addr=$(echo "$peer" | jq -r '.address')
|
||||
local id=$(echo "$peer" | jq -r '.id')
|
||||
|
||||
if ping -c1 -W1 "$addr" >/dev/null 2>&1; then
|
||||
peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"online\"")
|
||||
else
|
||||
peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"offline\"")
|
||||
fi
|
||||
done
|
||||
if ping -c1 -W1 "$addr" >/dev/null 2>&1; then
|
||||
peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"online\"")
|
||||
else
|
||||
peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"offline\"")
|
||||
fi
|
||||
done
|
||||
|
||||
echo "$peers" > "$PEERS_FILE"
|
||||
echo "$peers" > "$PEERS_FILE"
|
||||
fi
|
||||
|
||||
# Sleep interval
|
||||
local interval=$(get_config main sync_interval 60)
|
||||
@ -359,8 +488,21 @@ case "$1" in
|
||||
version)
|
||||
echo "$VERSION"
|
||||
;;
|
||||
status)
|
||||
get_node_status
|
||||
;;
|
||||
publish-mdns)
|
||||
publish_mdns
|
||||
;;
|
||||
stop-mdns)
|
||||
stop_mdns
|
||||
;;
|
||||
register-self)
|
||||
init
|
||||
register_self
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {daemon|discover|peers|add-peer|remove-peer|settings|set-settings|services|shared-services|sync|broadcast|version}"
|
||||
echo "Usage: $0 {daemon|discover|peers|add-peer|remove-peer|settings|set-settings|services|shared-services|sync|broadcast|version|status|publish-mdns|stop-mdns|register-self}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
10
package/secubox/secubox-p2p/root/www/api/peers
Normal file
10
package/secubox/secubox-p2p/root/www/api/peers
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# P2P API - List peers endpoint
|
||||
# Responds to requests on port 7331
|
||||
|
||||
echo "Content-Type: application/json"
|
||||
echo "Access-Control-Allow-Origin: *"
|
||||
echo ""
|
||||
|
||||
# Get peers from secubox-p2p
|
||||
/usr/sbin/secubox-p2p peers 2>/dev/null || echo '{"peers":[],"error":"daemon_unavailable"}'
|
||||
10
package/secubox/secubox-p2p/root/www/api/services
Normal file
10
package/secubox/secubox-p2p/root/www/api/services
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# P2P API - List services endpoint
|
||||
# Responds to requests on port 7331
|
||||
|
||||
echo "Content-Type: application/json"
|
||||
echo "Access-Control-Allow-Origin: *"
|
||||
echo ""
|
||||
|
||||
# Get services from secubox-p2p
|
||||
/usr/sbin/secubox-p2p services 2>/dev/null || echo '{"services":[],"error":"daemon_unavailable"}'
|
||||
10
package/secubox/secubox-p2p/root/www/api/status
Normal file
10
package/secubox/secubox-p2p/root/www/api/status
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# P2P API - Node status endpoint
|
||||
# Responds to requests on port 7331
|
||||
|
||||
echo "Content-Type: application/json"
|
||||
echo "Access-Control-Allow-Origin: *"
|
||||
echo ""
|
||||
|
||||
# Get node status from secubox-p2p
|
||||
/usr/sbin/secubox-p2p status 2>/dev/null || echo '{"error":"daemon_unavailable"}'
|
||||
10
package/secubox/secubox-p2p/root/www/api/sync
Normal file
10
package/secubox/secubox-p2p/root/www/api/sync
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# P2P API - Trigger sync endpoint
|
||||
# Responds to requests on port 7331
|
||||
|
||||
echo "Content-Type: application/json"
|
||||
echo "Access-Control-Allow-Origin: *"
|
||||
echo ""
|
||||
|
||||
# Trigger catalog sync
|
||||
/usr/sbin/secubox-p2p sync 2>/dev/null || echo '{"success":false,"error":"daemon_unavailable"}'
|
||||
Loading…
Reference in New Issue
Block a user