feat(routing): Centralize mitmproxy route management in secubox-core

Add centralized route registry (`secubox-route`) in secubox-core to eliminate
route management duplication across metablogizerctl, streamlitctl, and
mitmproxyctl.

New features:
- `/etc/config/secubox-routes` - UCI config for central route registry
- `/usr/sbin/secubox-route` - CLI for route management (add, remove, sync)
- Import routes from HAProxy, MetaBlogizer, Streamlit with source tracking
- Auto-sync to all mitmproxy instances on route changes
- Skip wildcard domains and LuCI (port 8081) routes

Updated services to use centralized registry:
- metablogizerctl: Use secubox-route add instead of mitmproxyctl sync
- streamlitctl: Use secubox-route add with domain/port params
- peertubectl: Use secubox-route add for emancipation
- vhost-manager/mitmproxy.sh: Prefer secubox-route when available
- mitmproxyctl: Delegate to secubox-route import-all for sync-routes

This prevents route mixups between services and provides a single
source of truth for all WAF routing configuration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-28 08:56:04 +01:00
parent 0389f93667
commit 19632e38e0
8 changed files with 667 additions and 38 deletions

View File

@ -864,36 +864,44 @@ _emancipate_haproxy() {
_emancipate_mitmproxy() {
local name="$1"
local domain="$2"
local port=$(uci_get site_${name}.port)
log_info "[WAF] Syncing mitmproxy routes from HAProxy config"
log_info "[WAF] Registering route in centralized registry"
# Use mitmproxyctl to sync routes from HAProxy UCI config
# This ensures all routes are in sync and mitmproxy will auto-reload
# Use centralized secubox-route for route management (preferred)
if command -v secubox-route >/dev/null 2>&1; then
secubox-route add "$domain" "127.0.0.1" "$port" "metablogizer" 2>&1 | while read -r line; do
log_info "[WAF] $line"
done
log_info "[WAF] Route registered in central registry"
return 0
fi
# Fallback: Use mitmproxyctl sync-routes
if command -v mitmproxyctl >/dev/null 2>&1; then
log_warn "[WAF] secubox-route not found, using mitmproxyctl"
mitmproxyctl sync-routes 2>&1 | while read -r line; do
log_info "[WAF] $line"
done
log_info "[WAF] Routes synced - mitmproxy will auto-reload on next request"
else
log_warn "[WAF] mitmproxyctl not found, manually adding route"
# Fallback: directly add route to the routes file
local port=$(uci_get site_${name}.port)
local routes_file="/srv/mitmproxy-in/haproxy-routes.json"
return 0
fi
if [ -f "$routes_file" ]; then
python3 -c "
# Last resort: Direct file manipulation
log_warn "[WAF] No route manager found, directly adding route"
local routes_file="/srv/mitmproxy-in/haproxy-routes.json"
if [ -f "$routes_file" ]; then
python3 -c "
import json
try:
with open('$routes_file') as f:
data = json.load(f)
data['$domain'] = ['192.168.255.1', $port]
data['$domain'] = ['127.0.0.1', $port]
with open('$routes_file', 'w') as f:
json.dump(data, f, indent=2)
print('[WAF] Route added: $domain -> 192.168.255.1:$port')
print('[WAF] Route added: $domain -> 127.0.0.1:$port')
except Exception as e:
print(f'[WAF] Error: {e}')
" 2>/dev/null
fi
fi
}

View File

@ -1574,7 +1574,35 @@ check_missing_routes() {
cmd_sync_routes() {
load_config
log_info "Syncing HAProxy backends to mitmproxy routes..."
log_info "Syncing routes to mitmproxy..."
# Use centralized secubox-route if available (preferred method)
if command -v secubox-route >/dev/null 2>&1; then
log_info "Using centralized route registry (secubox-route)"
# Import routes from all sources into central registry
secubox-route import-all 2>&1 | while read -r line; do
log_info " $line"
done
local routes_file="$data_path/haproxy-routes.json"
log_info "Routes synced to $routes_file"
# Check for missing routes
check_missing_routes "$routes_file"
return 0
fi
# Fallback: Legacy direct aggregation (for systems without secubox-route)
log_warn "secubox-route not found, using legacy sync method"
cmd_sync_routes_legacy
}
# Legacy sync method - retained for backwards compatibility
cmd_sync_routes_legacy() {
load_config
log_info "Syncing HAProxy backends to mitmproxy routes (legacy mode)..."
local routes_file="$data_path/haproxy-routes.json"
local fragments_file="/tmp/haproxy-routes-fragments.tmp"

View File

@ -944,8 +944,12 @@ cmd_emancipate() {
# Configure HAProxy
cmd_configure_haproxy
# Sync mitmproxy routes
if command -v mitmproxyctl >/dev/null 2>&1; then
# Register route in centralized registry
local port=$(uci_get port server || echo 9000)
if command -v secubox-route >/dev/null 2>&1; then
secubox-route add "$domain" "192.168.255.1" "$port" "peertube" 2>/dev/null
log_info "Route registered: $domain -> 192.168.255.1:$port"
elif command -v mitmproxyctl >/dev/null 2>&1; then
mitmproxyctl sync-routes 2>/dev/null
fi

View File

@ -1313,16 +1313,8 @@ _emancipate_haproxy() {
uci commit haproxy
# Add mitmproxy route for this domain
local routes_file="/srv/mitmproxy/haproxy-routes.json"
local routes_file_in="/srv/mitmproxy-in/haproxy-routes.json"
if [ -f "$routes_file" ]; then
# Add route: "domain": ["192.168.255.1", port]
sed -i "s/}$/,\"${domain}\":[\"192.168.255.1\",${port}]}/" "$routes_file" 2>/dev/null || true
fi
if [ -f "$routes_file_in" ]; then
sed -i "s/}$/,\"${domain}\":[\"192.168.255.1\",${port}]}/" "$routes_file_in" 2>/dev/null || true
fi
# Register route in centralized registry (route sync happens in _emancipate_mitmproxy)
# Note: Route registration moved to _emancipate_mitmproxy() for centralized management
# Generate HAProxy config
if command -v haproxyctl >/dev/null 2>&1; then
@ -1355,19 +1347,34 @@ _emancipate_ssl() {
}
_emancipate_mitmproxy() {
log_info "[MITMPROXY] Syncing routes to mitmproxy WAF"
local domain="$1"
local port="$2"
# Sync HAProxy backends to mitmproxy routes
log_info "[MITMPROXY] Registering route in centralized registry"
# Use centralized secubox-route for route management (preferred)
if command -v secubox-route >/dev/null 2>&1; then
if secubox-route add "$domain" "192.168.255.1" "$port" "streamlit" 2>&1; then
log_info "[MITMPROXY] Route registered: $domain -> 192.168.255.1:$port"
else
log_warn "[MITMPROXY] Failed to register route"
fi
return 0
fi
# Fallback: Sync via mitmproxyctl
if command -v mitmproxyctl >/dev/null 2>&1; then
log_warn "[MITMPROXY] secubox-route not found, using mitmproxyctl"
if mitmproxyctl sync-routes >/dev/null 2>&1; then
log_info "[MITMPROXY] Routes synced successfully"
else
log_warn "[MITMPROXY] Route sync failed - manual sync may be required"
log_warn "[MITMPROXY] Run: mitmproxyctl sync-routes"
fi
else
log_warn "[MITMPROXY] mitmproxyctl not found - routes not synced"
return 0
fi
log_warn "[MITMPROXY] No route manager found - routes not synced"
}
_emancipate_reload() {
@ -1586,8 +1593,8 @@ cmd_emancipate() {
# Step 6: Mesh P2P distribution to peers
_emancipate_mesh "$name" "$domain" "$port"
# Step 7: Sync mitmproxy routes (WAF routing)
_emancipate_mitmproxy
# Step 7: Register mitmproxy route (WAF routing)
_emancipate_mitmproxy "$domain" "$port"
# Step 8: Reload HAProxy
_emancipate_reload

View File

@ -1,5 +1,6 @@
#!/bin/sh
# SecuBox VHost Manager - Mitmproxy WAF Adapter
# Uses centralized secubox-route registry when available
ROUTES_FILE="/srv/mitmproxy/haproxy-routes.json"
@ -8,10 +9,17 @@ mitmproxy_add_route() {
local domain="$1"
local host="$2"
local port="$3"
local source="${4:-vhost-manager}"
# Prefer centralized route registry
if command -v secubox-route >/dev/null 2>&1; then
secubox-route add "$domain" "$host" "$port" "$source" 2>/dev/null
return $?
fi
# Fallback: Direct file manipulation
[ ! -f "$ROUTES_FILE" ] && echo '{}' > "$ROUTES_FILE"
# Use jsonfilter to update (or fallback to sed)
local temp_file="/tmp/routes_update_$$.json"
if command -v jq >/dev/null 2>&1; then
@ -19,13 +27,10 @@ mitmproxy_add_route() {
'.[$d] = [$h, $p]' "$ROUTES_FILE" > "$temp_file" && \
mv "$temp_file" "$ROUTES_FILE"
else
# Manual JSON update (basic)
local current=$(cat "$ROUTES_FILE")
if echo "$current" | grep -q "\"$domain\""; then
# Update existing
sed -i "s|\"$domain\": \\[[^]]*\\]|\"$domain\": [\"$host\", $port]|" "$ROUTES_FILE"
else
# Add new entry
if [ "$current" = "{}" ]; then
echo "{\"$domain\": [\"$host\", $port]}" > "$ROUTES_FILE"
else
@ -41,6 +46,13 @@ mitmproxy_add_route() {
mitmproxy_remove_route() {
local domain="$1"
# Prefer centralized route registry
if command -v secubox-route >/dev/null 2>&1; then
secubox-route remove "$domain" 2>/dev/null
return $?
fi
# Fallback: Direct file manipulation
[ ! -f "$ROUTES_FILE" ] && return 0
if command -v jq >/dev/null 2>&1; then
@ -48,7 +60,6 @@ mitmproxy_remove_route() {
jq --arg d "$domain" 'del(.[$d])' "$ROUTES_FILE" > "$temp_file" && \
mv "$temp_file" "$ROUTES_FILE"
else
# Basic removal (may leave trailing commas)
sed -i "/\"$domain\":/d" "$ROUTES_FILE"
fi
@ -57,6 +68,12 @@ mitmproxy_remove_route() {
# Sync all routes from vhosts config
mitmproxy_sync_routes() {
# Prefer centralized route registry
if command -v secubox-route >/dev/null 2>&1; then
secubox-route sync 2>/dev/null
return $?
fi
if command -v mitmproxyctl >/dev/null 2>&1; then
mitmproxyctl sync-routes 2>/dev/null
return $?

View File

@ -40,6 +40,7 @@ endef
define Package/secubox-core/conffiles
/etc/config/secubox
/etc/config/secubox-appstore
/etc/config/secubox-routes
/etc/secubox/profiles/
/etc/secubox/templates/
/etc/secubox/macros/
@ -52,6 +53,7 @@ define Package/secubox-core/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./root/etc/config/secubox $(1)/etc/config/
$(INSTALL_CONF) ./root/etc/config/secubox-appstore $(1)/etc/config/
$(INSTALL_CONF) ./root/etc/config/secubox-routes $(1)/etc/config/
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./root/etc/init.d/secubox-core $(1)/etc/init.d/
@ -88,6 +90,7 @@ define Package/secubox-core/install
$(INSTALL_BIN) ./root/usr/sbin/secubox-feedback $(1)/usr/sbin/
$(INSTALL_BIN) ./root/usr/sbin/secubox-tftp-recovery $(1)/usr/sbin/
$(INSTALL_BIN) ./root/usr/sbin/secubox-vhost $(1)/usr/sbin/
$(INSTALL_BIN) ./root/usr/sbin/secubox-route $(1)/usr/sbin/
$(INSTALL_BIN) ./root/usr/sbin/secubox-stats-persist $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/bin

View File

@ -0,0 +1,25 @@
#
# SecuBox Centralized Route Registry
# All services register their routes here for mitmproxy WAF routing
#
# Usage:
# secubox-route add <domain> <host> <port> <source>
# secubox-route remove <domain>
# secubox-route list
# secubox-route sync (generates mitmproxy routes file)
#
config registry 'main'
option enabled '1'
option auto_sync '1'
# Output file for mitmproxy routes
option routes_file '/srv/mitmproxy/haproxy-routes.json'
# Example route format:
# config route '<unique_id>'
# option domain 'app.example.com'
# option host '127.0.0.1'
# option port '8080'
# option source 'metablogizer' # Origin service: haproxy, metablogizer, streamlit, manual
# option enabled '1'

View File

@ -0,0 +1,537 @@
#!/bin/sh
#
# SecuBox Centralized Route Manager
# Single source of truth for mitmproxy WAF routing
#
# All services (metablogizer, streamlit, haproxy) register routes here.
# Routes are synced to mitmproxy instances on change.
#
. /lib/functions.sh
UCI_CONFIG="secubox-routes"
ROUTES_FILE="/srv/mitmproxy/haproxy-routes.json"
LOG_TAG="secubox-route"
# =============================================================================
# LOGGING
# =============================================================================
log_info() { logger -t "$LOG_TAG" -p daemon.info "$*"; echo "[INFO] $*" >&2; }
log_warn() { logger -t "$LOG_TAG" -p daemon.warn "$*"; echo "[WARN] $*" >&2; }
log_error() { logger -t "$LOG_TAG" -p daemon.err "$*"; echo "[ERROR] $*" >&2; }
# =============================================================================
# HELPERS
# =============================================================================
# Generate unique section name from domain
domain_to_section() {
echo "$1" | tr '.-' '__' | tr '[:upper:]' '[:lower:]'
}
# Check if route exists
route_exists() {
local domain="$1"
local section=$(domain_to_section "$domain")
uci -q get "$UCI_CONFIG.$section" >/dev/null 2>&1
}
# Get routes file path from config
get_routes_file() {
local file=$(uci -q get "$UCI_CONFIG.main.routes_file")
echo "${file:-$ROUTES_FILE}"
}
# =============================================================================
# ROUTE MANAGEMENT
# =============================================================================
cmd_add() {
local domain="$1"
local host="$2"
local port="$3"
local source="${4:-manual}"
if [ -z "$domain" ] || [ -z "$host" ] || [ -z "$port" ]; then
echo "Usage: secubox-route add <domain> <host> <port> [source]" >&2
echo " source: haproxy, metablogizer, streamlit, peertube, manual (default)" >&2
return 1
fi
# Skip wildcard domains
case "$domain" in
.*)
log_warn "Skipping wildcard domain: $domain"
return 0
;;
esac
local section=$(domain_to_section "$domain")
# Check if already exists with same values
if route_exists "$domain"; then
local existing_host=$(uci -q get "$UCI_CONFIG.$section.host")
local existing_port=$(uci -q get "$UCI_CONFIG.$section.port")
if [ "$existing_host" = "$host" ] && [ "$existing_port" = "$port" ]; then
log_info "Route already exists: $domain -> $host:$port"
return 0
fi
log_info "Updating route: $domain -> $host:$port ($source)"
else
log_info "Adding route: $domain -> $host:$port ($source)"
fi
# Add or update the route
uci set "$UCI_CONFIG.$section=route"
uci set "$UCI_CONFIG.$section.domain=$domain"
uci set "$UCI_CONFIG.$section.host=$host"
uci set "$UCI_CONFIG.$section.port=$port"
uci set "$UCI_CONFIG.$section.source=$source"
uci set "$UCI_CONFIG.$section.enabled=1"
uci commit "$UCI_CONFIG"
# Auto-sync if enabled
local auto_sync=$(uci -q get "$UCI_CONFIG.main.auto_sync")
if [ "$auto_sync" = "1" ]; then
cmd_sync
fi
return 0
}
cmd_remove() {
local domain="$1"
if [ -z "$domain" ]; then
echo "Usage: secubox-route remove <domain>" >&2
return 1
fi
local section=$(domain_to_section "$domain")
if ! route_exists "$domain"; then
log_warn "Route not found: $domain"
return 1
fi
log_info "Removing route: $domain"
uci delete "$UCI_CONFIG.$section"
uci commit "$UCI_CONFIG"
# Auto-sync if enabled
local auto_sync=$(uci -q get "$UCI_CONFIG.main.auto_sync")
if [ "$auto_sync" = "1" ]; then
cmd_sync
fi
return 0
}
cmd_list() {
local format="${1:-table}"
echo "SecuBox Route Registry:"
echo "========================"
local count=0
local routes_file="/tmp/routes_list_$$.tmp"
# Collect routes into temp file to avoid subshell variable issues
uci show "$UCI_CONFIG" 2>/dev/null | grep "=route$" | cut -d'=' -f1 | cut -d'.' -f2 > "$routes_file"
while read section; do
[ -z "$section" ] && continue
local domain=$(uci -q get "$UCI_CONFIG.$section.domain")
local host=$(uci -q get "$UCI_CONFIG.$section.host")
local port=$(uci -q get "$UCI_CONFIG.$section.port")
local source=$(uci -q get "$UCI_CONFIG.$section.source")
local enabled=$(uci -q get "$UCI_CONFIG.$section.enabled")
[ -z "$domain" ] && continue
local status="[+]"
[ "$enabled" != "1" ] && status="[-]"
if [ "$format" = "json" ]; then
echo " \"$domain\": [\"$host\", $port],"
else
printf "%s %-40s -> %s:%-5s (%s)\n" "$status" "$domain" "$host" "$port" "$source"
fi
count=$((count + 1))
done < "$routes_file"
rm -f "$routes_file"
echo "========================"
echo "Total: $count routes"
}
cmd_sync() {
local routes_file=$(get_routes_file)
local tmp_file="/tmp/routes_sync_$$.json"
local count=0
log_info "Syncing routes to $routes_file"
# Start JSON
echo "{" > "$tmp_file"
local first=1
local routes_list="/tmp/routes_sync_list_$$.tmp"
# Collect enabled routes
uci show "$UCI_CONFIG" 2>/dev/null | grep "=route$" | cut -d'=' -f1 | cut -d'.' -f2 > "$routes_list"
while read section; do
[ -z "$section" ] && continue
local enabled=$(uci -q get "$UCI_CONFIG.$section.enabled")
[ "$enabled" != "1" ] && continue
local domain=$(uci -q get "$UCI_CONFIG.$section.domain")
local host=$(uci -q get "$UCI_CONFIG.$section.host")
local port=$(uci -q get "$UCI_CONFIG.$section.port")
[ -z "$domain" ] || [ -z "$host" ] || [ -z "$port" ] && continue
# Skip LuCI routes (port 8081)
if [ "$port" = "8081" ]; then
log_warn "Skipping LuCI route: $domain -> $host:8081"
continue
fi
if [ $first -eq 1 ]; then
printf ' "%s": ["%s", %s]' "$domain" "$host" "$port" >> "$tmp_file"
first=0
else
printf ',\n "%s": ["%s", %s]' "$domain" "$host" "$port" >> "$tmp_file"
fi
count=$((count + 1))
done < "$routes_list"
rm -f "$routes_list"
# Close JSON
echo "" >> "$tmp_file"
echo "}" >> "$tmp_file"
# Install to target location
mkdir -p "$(dirname "$routes_file")"
mv "$tmp_file" "$routes_file"
chmod 644 "$routes_file"
log_info "Generated $routes_file with $count routes"
# Also sync to mitmproxy-in if it exists
local routes_file_in="/srv/mitmproxy-in/haproxy-routes.json"
if [ -d "/srv/mitmproxy-in" ]; then
cp "$routes_file" "$routes_file_in"
log_info "Synced to $routes_file_in"
fi
# Sync to all mitmproxy instances
if [ -f /etc/config/mitmproxy ]; then
local instances=$(uci show mitmproxy 2>/dev/null | grep '=instance$' | sed 's/mitmproxy\.\([^=]*\)=instance/\1/')
for inst in $instances; do
local inst_path=$(uci -q get mitmproxy.${inst}.data_path)
[ -z "$inst_path" ] && inst_path="/srv/mitmproxy-${inst}"
if [ -d "$inst_path" ]; then
cp "$routes_file" "$inst_path/haproxy-routes.json"
log_info "Synced to $inst_path"
fi
done
fi
echo "$count routes synced"
return 0
}
cmd_import_haproxy() {
log_info "Importing routes from HAProxy backends..."
local vhosts_file="/tmp/haproxy_import_$$.tmp"
local imported=0
# Get all vhosts using mitmproxy_inspector backend
uci show haproxy 2>/dev/null | grep "=vhost" | cut -d'=' -f1 | cut -d'.' -f2 > "$vhosts_file"
while read vhost; do
[ -z "$vhost" ] && continue
local domain=$(uci -q get haproxy.$vhost.domain)
local backend=$(uci -q get haproxy.$vhost.backend)
local enabled=$(uci -q get haproxy.$vhost.enabled)
[ -z "$domain" ] && continue
[ "$enabled" != "1" ] && continue
# Skip wildcards
case "$domain" in
.*) continue ;;
esac
# Get original backend if routed through mitmproxy
if [ "$backend" = "mitmproxy_inspector" ]; then
backend=$(uci -q get haproxy.$vhost.original_backend)
# If no original_backend, try to find backend by domain name pattern
if [ -z "$backend" ]; then
# Extract subdomain (e.g., "alerte" from "alerte.gk2.secubox.in")
local subdomain=$(echo "$domain" | cut -d'.' -f1)
# Try common patterns: streamlit_<name>_streamlit, metablog_<name>
for pattern in "streamlit_${subdomain}" "streamlit_${subdomain}_streamlit" "metablog_${subdomain}"; do
if uci -q get haproxy.$pattern >/dev/null 2>&1; then
backend="$pattern"
break
fi
done
fi
fi
# Skip special backends
case "$backend" in
fallback|luci|luci_default|mitmproxy_inspector|"") continue ;;
esac
# Find backend server info
local ip="" port=""
# Method 1: Check inline server field
local server=$(uci -q get haproxy.$backend.server)
if [ -n "$server" ]; then
local addr=$(echo "$server" | awk '{print $2}')
ip=$(echo "$addr" | cut -d':' -f1)
port=$(echo "$addr" | cut -d':' -f2)
[ "$ip" = "$port" ] && port="80"
fi
# Method 2: Check separate server section
if [ -z "$ip" ]; then
local server_section=$(uci show haproxy 2>/dev/null | grep "\.backend='$backend'" | grep "=server" | head -1 | cut -d'=' -f1 | cut -d'.' -f2)
if [ -z "$server_section" ]; then
server_section=$(uci show haproxy 2>/dev/null | grep "^haproxy\.${backend}_.*=server" | head -1 | cut -d'=' -f1 | cut -d'.' -f2)
fi
if [ -n "$server_section" ]; then
ip=$(uci -q get haproxy.$server_section.address)
port=$(uci -q get haproxy.$server_section.port)
fi
fi
if [ -n "$ip" ] && [ -n "$port" ]; then
cmd_add "$domain" "$ip" "$port" "haproxy"
imported=$((imported + 1))
fi
done < "$vhosts_file"
rm -f "$vhosts_file"
log_info "Imported $imported routes from HAProxy"
}
cmd_import_metablogizer() {
log_info "Importing routes from MetaBlogizer..."
[ -f /etc/config/metablogizer ] || {
log_warn "MetaBlogizer not installed"
return 0
}
local sites_file="/tmp/mb_import_$$.tmp"
local imported=0
uci show metablogizer 2>/dev/null | grep "=site$" | cut -d'=' -f1 | cut -d'.' -f2 > "$sites_file"
while read site; do
[ -z "$site" ] && continue
local domain=$(uci -q get metablogizer.$site.domain)
local port=$(uci -q get metablogizer.$site.port)
local enabled=$(uci -q get metablogizer.$site.enabled)
[ "$enabled" != "1" ] && continue
[ -z "$domain" ] || [ -z "$port" ] && continue
cmd_add "$domain" "127.0.0.1" "$port" "metablogizer"
imported=$((imported + 1))
done < "$sites_file"
rm -f "$sites_file"
log_info "Imported $imported routes from MetaBlogizer"
}
cmd_import_streamlit() {
log_info "Importing routes from Streamlit..."
[ -f /etc/config/streamlit ] || {
log_warn "Streamlit not installed"
return 0
}
local inst_file="/tmp/st_import_$$.tmp"
local imported=0
uci show streamlit 2>/dev/null | grep "=instance$" | cut -d'=' -f1 | cut -d'.' -f2 > "$inst_file"
while read inst; do
[ -z "$inst" ] && continue
local port=$(uci -q get streamlit.$inst.port)
local enabled=$(uci -q get streamlit.$inst.enabled)
[ "$enabled" != "1" ] && continue
[ -z "$port" ] && continue
# First check if instance has explicit domain field
local domain=$(uci -q get streamlit.$inst.domain)
# If no explicit domain, try pattern-based matching
if [ -z "$domain" ]; then
domain="${inst}.gk2.secubox.in"
fi
# Verify domain exists in HAProxy config
if uci show haproxy 2>/dev/null | grep -q "domain='$domain'"; then
cmd_add "$domain" "192.168.255.1" "$port" "streamlit"
imported=$((imported + 1))
fi
done < "$inst_file"
rm -f "$inst_file"
log_info "Imported $imported routes from Streamlit"
}
cmd_import_all() {
log_info "Importing all routes from services..."
# Disable auto-sync during bulk import
local auto_sync=$(uci -q get "$UCI_CONFIG.main.auto_sync")
uci set "$UCI_CONFIG.main.auto_sync=0"
cmd_import_haproxy
cmd_import_metablogizer
cmd_import_streamlit
# Restore auto-sync and do final sync
uci set "$UCI_CONFIG.main.auto_sync=$auto_sync"
uci commit "$UCI_CONFIG"
cmd_sync
}
cmd_clear() {
local source="${1:-all}"
if [ "$source" = "all" ]; then
log_warn "Clearing ALL routes from registry"
# Remove all route sections
local routes_file="/tmp/routes_clear_$$.tmp"
uci show "$UCI_CONFIG" 2>/dev/null | grep "=route$" | cut -d'=' -f1 | cut -d'.' -f2 > "$routes_file"
while read section; do
[ -z "$section" ] && continue
uci delete "$UCI_CONFIG.$section"
done < "$routes_file"
rm -f "$routes_file"
else
log_info "Clearing routes from source: $source"
local routes_file="/tmp/routes_clear_$$.tmp"
uci show "$UCI_CONFIG" 2>/dev/null | grep "=route$" | cut -d'=' -f1 | cut -d'.' -f2 > "$routes_file"
while read section; do
[ -z "$section" ] && continue
local src=$(uci -q get "$UCI_CONFIG.$section.source")
if [ "$src" = "$source" ]; then
uci delete "$UCI_CONFIG.$section"
fi
done < "$routes_file"
rm -f "$routes_file"
fi
uci commit "$UCI_CONFIG"
cmd_sync
}
cmd_status() {
local routes_file=$(get_routes_file)
local enabled=$(uci -q get "$UCI_CONFIG.main.enabled")
local auto_sync=$(uci -q get "$UCI_CONFIG.main.auto_sync")
echo "SecuBox Route Registry Status"
echo "=============================="
echo "Enabled: ${enabled:-1}"
echo "Auto-sync: ${auto_sync:-1}"
echo "Routes file: $routes_file"
if [ -f "$routes_file" ]; then
local count=$(grep -c '"' "$routes_file" 2>/dev/null || echo 0)
count=$((count / 2)) # Each route has 2 quoted strings
echo "Routes: $count"
echo "File size: $(stat -c%s "$routes_file" 2>/dev/null || echo "?") bytes"
echo "Modified: $(stat -c%y "$routes_file" 2>/dev/null | cut -d'.' -f1 || echo "?")"
else
echo "Routes: 0 (file not found)"
fi
# Count by source
echo ""
echo "Routes by Source:"
for src in haproxy metablogizer streamlit peertube manual; do
local count=$(uci show "$UCI_CONFIG" 2>/dev/null | grep "\.source='$src'" | wc -l)
[ "$count" -gt 0 ] && echo " $src: $count"
done
}
usage() {
cat <<EOF
SecuBox Centralized Route Manager
Usage: secubox-route <command> [args]
Commands:
add <domain> <host> <port> [source] Add or update a route
remove <domain> Remove a route
list [json] List all routes
sync Generate mitmproxy routes file
status Show registry status
Import Commands (for migration):
import-haproxy Import from HAProxy backends
import-metablogizer Import from MetaBlogizer sites
import-streamlit Import from Streamlit instances
import-all Import from all services
Maintenance:
clear [source] Clear routes (all or by source)
Sources: haproxy, metablogizer, streamlit, peertube, manual
Examples:
secubox-route add blog.example.com 127.0.0.1 4000 metablogizer
secubox-route remove blog.example.com
secubox-route list
secubox-route import-all
secubox-route sync
secubox-route clear metablogizer
EOF
}
# =============================================================================
# MAIN
# =============================================================================
case "$1" in
add) shift; cmd_add "$@" ;;
remove|rm|del) shift; cmd_remove "$@" ;;
list|ls) shift; cmd_list "$@" ;;
sync) shift; cmd_sync "$@" ;;
status) shift; cmd_status "$@" ;;
import-haproxy) shift; cmd_import_haproxy "$@" ;;
import-metablogizer) shift; cmd_import_metablogizer "$@" ;;
import-streamlit) shift; cmd_import_streamlit "$@" ;;
import-all) shift; cmd_import_all "$@" ;;
clear) shift; cmd_clear "$@" ;;
help|--help|-h|"") usage ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac