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:
parent
0389f93667
commit
19632e38e0
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 $?
|
||||
|
||||
@ -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
|
||||
|
||||
25
package/secubox/secubox-core/root/etc/config/secubox-routes
Normal file
25
package/secubox/secubox-core/root/etc/config/secubox-routes
Normal 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'
|
||||
|
||||
537
package/secubox/secubox-core/root/usr/sbin/secubox-route
Normal file
537
package/secubox/secubox-core/root/usr/sbin/secubox-route
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user