feat(exposure): Add SecuBox Service Exposure Manager
New unified tool for service exposure management: - Port conflict detection and resolution (scan, conflicts, fix-port) - Dynamic Tor hidden service management (tor add/list/remove) - HAProxy SSL reverse proxy configuration (ssl add/list/remove) Commands: secubox-exposure scan # List listening services secubox-exposure conflicts # Detect port collisions secubox-exposure tor add gitea # Create .onion for service secubox-exposure ssl add svc domain # Add HAProxy SSL backend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c3c0c131d
commit
b198bb754a
42
package/secubox/secubox-app-exposure/Makefile
Normal file
42
package/secubox/secubox-app-exposure/Makefile
Normal file
@ -0,0 +1,42 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-exposure
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=SecuBox Team <contact@secubox.dev>
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/secubox-app-exposure
|
||||
SECTION:=secubox
|
||||
CATEGORY:=SecuBox
|
||||
TITLE:=SecuBox Service Exposure Manager
|
||||
DEPENDS:=+secubox-core +tor +haproxy
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/secubox-app-exposure/description
|
||||
Unified service exposure manager for SecuBox.
|
||||
- Port conflict detection and resolution
|
||||
- Dynamic Tor hidden service management
|
||||
- HAProxy SSL reverse proxy configuration
|
||||
endef
|
||||
|
||||
define Package/secubox-app-exposure/conffiles
|
||||
/etc/config/secubox-exposure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/secubox-app-exposure/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/secubox-exposure $(1)/etc/config/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/secubox-exposure $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,secubox-app-exposure))
|
||||
@ -0,0 +1,63 @@
|
||||
# SecuBox Service Exposure Manager Configuration
|
||||
|
||||
config settings 'main'
|
||||
option enabled '1'
|
||||
option tor_enabled '1'
|
||||
option ssl_enabled '1'
|
||||
option haproxy_config '/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg'
|
||||
option haproxy_certs '/srv/lxc/haproxy/rootfs/etc/haproxy/certs'
|
||||
option tor_hidden_dir '/var/lib/tor/hidden_services'
|
||||
option tor_config '/etc/tor/torrc'
|
||||
|
||||
# Port ranges for auto-assignment
|
||||
config ports 'ranges'
|
||||
option app_start '8100'
|
||||
option app_end '8199'
|
||||
option monitoring_start '8200'
|
||||
option monitoring_end '8299'
|
||||
|
||||
# Known service definitions with default ports
|
||||
config known 'gitea'
|
||||
option default_port '3000'
|
||||
option config_path 'gitea.main.http_port'
|
||||
option category 'app'
|
||||
|
||||
config known 'streamlit'
|
||||
option default_port '8501'
|
||||
option config_path 'streamlit.main.port'
|
||||
option category 'app'
|
||||
|
||||
config known 'hexojs'
|
||||
option default_port '4000'
|
||||
option config_path 'hexojs.main.port'
|
||||
option category 'app'
|
||||
|
||||
config known 'cyberfeed'
|
||||
option default_port '8082'
|
||||
option config_path 'cyberfeed.main.port'
|
||||
option category 'app'
|
||||
|
||||
config known 'crowdsec'
|
||||
option default_port '6060'
|
||||
option config_file '/etc/crowdsec/config.yaml'
|
||||
option category 'security'
|
||||
|
||||
config known 'netifyd'
|
||||
option default_port '8086'
|
||||
option config_path 'netifyd.main.port'
|
||||
option category 'monitoring'
|
||||
|
||||
config known 'domoticz'
|
||||
option default_port '8080'
|
||||
option config_type 'docker'
|
||||
option category 'app'
|
||||
|
||||
# Service exposure entries (dynamically managed)
|
||||
# Example:
|
||||
# config service 'gitea'
|
||||
# option port '3000'
|
||||
# option local '1'
|
||||
# option tor '1'
|
||||
# option tor_onion 'abc123xyz.onion'
|
||||
# option ssl '1'
|
||||
# option ssl_domain 'git.example.com'
|
||||
657
package/secubox/secubox-app-exposure/files/usr/sbin/secubox-exposure
Executable file
657
package/secubox/secubox-app-exposure/files/usr/sbin/secubox-exposure
Executable file
@ -0,0 +1,657 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# SecuBox Service Exposure Manager
|
||||
# Unified tool for port management, Tor hidden services, and HAProxy SSL
|
||||
#
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
CONFIG_NAME="secubox-exposure"
|
||||
HAPROXY_CONFIG=""
|
||||
HAPROXY_CERTS=""
|
||||
TOR_HIDDEN_DIR=""
|
||||
TOR_CONFIG=""
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_err() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
load_config() {
|
||||
config_load "$CONFIG_NAME"
|
||||
config_get HAPROXY_CONFIG main haproxy_config "/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg"
|
||||
config_get HAPROXY_CERTS main haproxy_certs "/srv/lxc/haproxy/rootfs/etc/haproxy/certs"
|
||||
config_get TOR_HIDDEN_DIR main tor_hidden_dir "/var/lib/tor/hidden_services"
|
||||
config_get TOR_CONFIG main tor_config "/etc/tor/torrc"
|
||||
config_get APP_PORT_START ranges app_start "8100"
|
||||
config_get APP_PORT_END ranges app_end "8199"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PORT SCANNING & CONFLICT DETECTION
|
||||
# ============================================================================
|
||||
|
||||
get_listening_ports() {
|
||||
# Returns: port address process
|
||||
netstat -tlnp 2>/dev/null | grep LISTEN | awk '{
|
||||
split($4, a, ":")
|
||||
port = a[length(a)]
|
||||
if (!seen[port]++) {
|
||||
split($7, p, "/")
|
||||
proc = p[2]
|
||||
if (proc == "") proc = "unknown"
|
||||
print port, $4, proc
|
||||
}
|
||||
}' | sort -n
|
||||
}
|
||||
|
||||
cmd_scan() {
|
||||
log_info "Scanning listening services..."
|
||||
echo ""
|
||||
printf "%-6s %-20s %-15s %-10s\n" "PORT" "ADDRESS" "PROCESS" "STATUS"
|
||||
printf "%-6s %-20s %-15s %-10s\n" "------" "--------------------" "---------------" "----------"
|
||||
|
||||
get_listening_ports | while read port addr proc; do
|
||||
# Determine if external
|
||||
case "$addr" in
|
||||
*0.0.0.0*|*::*) status="${GREEN}external${NC}" ;;
|
||||
*127.0.0.1*|*::1*) status="${YELLOW}local${NC}" ;;
|
||||
*) status="${CYAN}bound${NC}" ;;
|
||||
esac
|
||||
printf "%-6s %-20s %-15s " "$port" "$addr" "$proc"
|
||||
echo -e "$status"
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
cmd_conflicts() {
|
||||
log_info "Checking for port conflicts..."
|
||||
echo ""
|
||||
|
||||
local conflicts=0
|
||||
local TMP_PORTS="/tmp/ports_$$"
|
||||
|
||||
# Get all configured ports from UCI
|
||||
> "$TMP_PORTS"
|
||||
|
||||
# Check known services
|
||||
check_known_service() {
|
||||
local section="$1"
|
||||
local default_port config_path
|
||||
config_get default_port "$section" default_port
|
||||
config_get config_path "$section" config_path
|
||||
|
||||
if [ -n "$config_path" ]; then
|
||||
# Extract UCI config and option
|
||||
local uci_config=$(echo "$config_path" | cut -d'.' -f1)
|
||||
local uci_option=$(echo "$config_path" | cut -d'.' -f2-)
|
||||
local actual_port=$(uci -q get "$config_path" 2>/dev/null)
|
||||
[ -z "$actual_port" ] && actual_port="$default_port"
|
||||
echo "$actual_port $section" >> "$TMP_PORTS"
|
||||
fi
|
||||
}
|
||||
config_foreach check_known_service known
|
||||
|
||||
# Find duplicates
|
||||
sort "$TMP_PORTS" | uniq -d -w5 | while read port svc; do
|
||||
log_warn "Port $port is configured for multiple services!"
|
||||
grep "^$port " "$TMP_PORTS" | while read p s; do
|
||||
echo " - $s"
|
||||
done
|
||||
conflicts=$((conflicts + 1))
|
||||
done
|
||||
|
||||
# Check against actually listening ports
|
||||
get_listening_ports | while read port addr proc; do
|
||||
if grep -q "^$port " "$TMP_PORTS"; then
|
||||
local configured_svc=$(grep "^$port " "$TMP_PORTS" | head -1 | cut -d' ' -f2)
|
||||
# Check if process matches expected
|
||||
case "$configured_svc" in
|
||||
gitea) [ "$proc" != "gitea" ] && log_warn "Port $port: expected gitea, found $proc" ;;
|
||||
streamlit) echo "$proc" | grep -qv "python\|streamlit" && log_warn "Port $port: expected streamlit, found $proc" ;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f "$TMP_PORTS"
|
||||
|
||||
if [ "$conflicts" -eq 0 ]; then
|
||||
log_ok "No port conflicts detected"
|
||||
fi
|
||||
}
|
||||
|
||||
find_free_port() {
|
||||
local start="$1"
|
||||
local end="$2"
|
||||
local port="$start"
|
||||
|
||||
while [ "$port" -le "$end" ]; do
|
||||
if ! netstat -tlnp 2>/dev/null | grep -q ":$port "; then
|
||||
echo "$port"
|
||||
return 0
|
||||
fi
|
||||
port=$((port + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
cmd_fix_port() {
|
||||
local service="$1"
|
||||
local new_port="$2"
|
||||
|
||||
if [ -z "$service" ]; then
|
||||
log_err "Usage: secubox-exposure fix-port <service> [new_port]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
# Get service config
|
||||
local config_path default_port
|
||||
config_get config_path "$service" config_path
|
||||
config_get default_port "$service" default_port
|
||||
|
||||
if [ -z "$config_path" ]; then
|
||||
log_err "Unknown service: $service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Find free port if not specified
|
||||
if [ -z "$new_port" ]; then
|
||||
new_port=$(find_free_port "$APP_PORT_START" "$APP_PORT_END")
|
||||
if [ -z "$new_port" ]; then
|
||||
log_err "No free ports available in range $APP_PORT_START-$APP_PORT_END"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if new port is free
|
||||
if netstat -tlnp 2>/dev/null | grep -q ":$new_port "; then
|
||||
log_err "Port $new_port is already in use"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Changing $service port to $new_port"
|
||||
|
||||
# Update UCI
|
||||
if uci set "$config_path=$new_port" && uci commit; then
|
||||
log_ok "UCI config updated"
|
||||
|
||||
# Restart service if it has an init script
|
||||
if [ -x "/etc/init.d/$service" ]; then
|
||||
log_info "Restarting $service..."
|
||||
/etc/init.d/"$service" restart
|
||||
fi
|
||||
|
||||
log_ok "$service now listening on port $new_port"
|
||||
else
|
||||
log_err "Failed to update UCI config"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TOR HIDDEN SERVICES
|
||||
# ============================================================================
|
||||
|
||||
cmd_tor_add() {
|
||||
local service="$1"
|
||||
local local_port="$2"
|
||||
local onion_port="${3:-80}"
|
||||
|
||||
if [ -z "$service" ]; then
|
||||
log_err "Usage: secubox-exposure tor add <service> [local_port] [onion_port]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
# Get local port from config if not specified
|
||||
if [ -z "$local_port" ]; then
|
||||
config_get local_port "$service" default_port
|
||||
if [ -z "$local_port" ]; then
|
||||
log_err "Cannot determine local port for $service"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local hidden_dir="$TOR_HIDDEN_DIR/$service"
|
||||
|
||||
# Create hidden service directory
|
||||
mkdir -p "$hidden_dir"
|
||||
chmod 700 "$hidden_dir"
|
||||
chown tor:tor "$hidden_dir" 2>/dev/null || chown debian-tor:debian-tor "$hidden_dir" 2>/dev/null
|
||||
|
||||
# Check if already configured in torrc
|
||||
if grep -q "HiddenServiceDir $hidden_dir" "$TOR_CONFIG" 2>/dev/null; then
|
||||
log_warn "Hidden service for $service already exists"
|
||||
local onion=$(cat "$hidden_dir/hostname" 2>/dev/null)
|
||||
[ -n "$onion" ] && log_info "Onion address: $onion"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add to torrc
|
||||
log_info "Adding hidden service for $service (127.0.0.1:$local_port -> :$onion_port)"
|
||||
|
||||
cat >> "$TOR_CONFIG" << EOF
|
||||
|
||||
# Hidden service for $service (added by secubox-exposure)
|
||||
HiddenServiceDir $hidden_dir
|
||||
HiddenServicePort $onion_port 127.0.0.1:$local_port
|
||||
EOF
|
||||
|
||||
# Restart Tor
|
||||
log_info "Restarting Tor..."
|
||||
/etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null
|
||||
|
||||
# Wait for onion address
|
||||
log_info "Waiting for onion address generation..."
|
||||
local tries=0
|
||||
while [ ! -f "$hidden_dir/hostname" ] && [ "$tries" -lt 30 ]; do
|
||||
sleep 1
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
|
||||
if [ -f "$hidden_dir/hostname" ]; then
|
||||
local onion=$(cat "$hidden_dir/hostname")
|
||||
log_ok "Hidden service created!"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Service:${NC} $service"
|
||||
echo -e " ${CYAN}Onion:${NC} $onion"
|
||||
echo -e " ${CYAN}Port:${NC} $onion_port -> 127.0.0.1:$local_port"
|
||||
echo ""
|
||||
|
||||
# Save to UCI
|
||||
uci set "${CONFIG_NAME}.${service}=service"
|
||||
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
||||
uci set "${CONFIG_NAME}.${service}.tor=1"
|
||||
uci set "${CONFIG_NAME}.${service}.tor_onion=$onion"
|
||||
uci set "${CONFIG_NAME}.${service}.tor_port=$onion_port"
|
||||
uci commit "$CONFIG_NAME"
|
||||
else
|
||||
log_err "Failed to generate onion address"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_tor_list() {
|
||||
load_config
|
||||
|
||||
log_info "Tor Hidden Services:"
|
||||
echo ""
|
||||
printf "%-15s %-62s %-10s\n" "SERVICE" "ONION ADDRESS" "PORT"
|
||||
printf "%-15s %-62s %-10s\n" "---------------" "--------------------------------------------------------------" "----------"
|
||||
|
||||
# List from filesystem
|
||||
if [ -d "$TOR_HIDDEN_DIR" ]; then
|
||||
for dir in "$TOR_HIDDEN_DIR"/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
local svc=$(basename "$dir")
|
||||
local onion=""
|
||||
[ -f "$dir/hostname" ] && onion=$(cat "$dir/hostname")
|
||||
|
||||
# Get port from torrc
|
||||
local port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{print $2}')
|
||||
|
||||
if [ -n "$onion" ]; then
|
||||
printf "%-15s %-62s %-10s\n" "$svc" "$onion" "${port:-80}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
cmd_tor_remove() {
|
||||
local service="$1"
|
||||
|
||||
if [ -z "$service" ]; then
|
||||
log_err "Usage: secubox-exposure tor remove <service>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
local hidden_dir="$TOR_HIDDEN_DIR/$service"
|
||||
|
||||
if [ ! -d "$hidden_dir" ]; then
|
||||
log_err "No hidden service found for $service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Removing hidden service for $service"
|
||||
|
||||
# Remove from torrc (remove the block)
|
||||
sed -i "/# Hidden service for $service/,/HiddenServicePort/d" "$TOR_CONFIG"
|
||||
|
||||
# Remove directory
|
||||
rm -rf "$hidden_dir"
|
||||
|
||||
# Update UCI
|
||||
uci delete "${CONFIG_NAME}.${service}.tor" 2>/dev/null
|
||||
uci delete "${CONFIG_NAME}.${service}.tor_onion" 2>/dev/null
|
||||
uci delete "${CONFIG_NAME}.${service}.tor_port" 2>/dev/null
|
||||
uci commit "$CONFIG_NAME"
|
||||
|
||||
# Restart Tor
|
||||
/etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null
|
||||
|
||||
log_ok "Hidden service removed"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# HAPROXY SSL BACKENDS
|
||||
# ============================================================================
|
||||
|
||||
cmd_ssl_add() {
|
||||
local service="$1"
|
||||
local domain="$2"
|
||||
local local_port="$3"
|
||||
|
||||
if [ -z "$service" ] || [ -z "$domain" ]; then
|
||||
log_err "Usage: secubox-exposure ssl add <service> <domain> [local_port]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
# Get local port from config if not specified
|
||||
if [ -z "$local_port" ]; then
|
||||
config_get local_port "$service" default_port
|
||||
# Try to get from service UCI
|
||||
local config_path
|
||||
config_get config_path "$service" config_path
|
||||
if [ -n "$config_path" ]; then
|
||||
local configured_port=$(uci -q get "$config_path")
|
||||
[ -n "$configured_port" ] && local_port="$configured_port"
|
||||
fi
|
||||
if [ -z "$local_port" ]; then
|
||||
log_err "Cannot determine local port for $service. Specify it manually."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if HAProxy config exists
|
||||
if [ ! -f "$HAPROXY_CONFIG" ]; then
|
||||
log_err "HAProxy config not found: $HAPROXY_CONFIG"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if already configured
|
||||
if grep -q "backend ${service}_backend" "$HAPROXY_CONFIG"; then
|
||||
log_warn "Backend for $service already exists in HAProxy config"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Adding SSL backend for $service ($domain -> 127.0.0.1:$local_port)"
|
||||
|
||||
# Create backend config
|
||||
local backend_config="
|
||||
# Backend for $service (added by secubox-exposure)
|
||||
backend ${service}_backend
|
||||
mode http
|
||||
option httpchk GET /
|
||||
http-request set-header X-Forwarded-Proto https
|
||||
server ${service} 127.0.0.1:$local_port check
|
||||
"
|
||||
|
||||
# Add ACL to https frontend
|
||||
local acl_line=" acl host_${service} hdr(host) -i $domain"
|
||||
local use_line=" use_backend ${service}_backend if host_${service}"
|
||||
|
||||
# Check if https-in frontend exists
|
||||
if grep -q "frontend https-in" "$HAPROXY_CONFIG"; then
|
||||
# Add ACL and use_backend before the default_backend line
|
||||
sed -i "/frontend https-in/,/default_backend/ {
|
||||
/default_backend/ i\\
|
||||
$acl_line\\
|
||||
$use_line
|
||||
}" "$HAPROXY_CONFIG"
|
||||
else
|
||||
log_warn "No https-in frontend found. Adding basic HTTPS frontend."
|
||||
cat >> "$HAPROXY_CONFIG" << EOF
|
||||
|
||||
frontend https-in
|
||||
bind *:443 ssl crt $HAPROXY_CERTS/
|
||||
mode http
|
||||
option httplog
|
||||
$acl_line
|
||||
$use_line
|
||||
default_backend default_backend
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add backend at end of file
|
||||
echo "$backend_config" >> "$HAPROXY_CONFIG"
|
||||
|
||||
# Save to UCI
|
||||
uci set "${CONFIG_NAME}.${service}=service"
|
||||
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
||||
uci set "${CONFIG_NAME}.${service}.ssl=1"
|
||||
uci set "${CONFIG_NAME}.${service}.ssl_domain=$domain"
|
||||
uci commit "$CONFIG_NAME"
|
||||
|
||||
log_ok "HAProxy backend added for $service"
|
||||
log_info "Domain: $domain -> 127.0.0.1:$local_port"
|
||||
log_warn "Note: You need to add SSL certificate for $domain to $HAPROXY_CERTS/"
|
||||
log_info "Reloading HAProxy..."
|
||||
|
||||
# Reload HAProxy (in LXC container)
|
||||
if [ -x "/usr/sbin/haproxyctl" ]; then
|
||||
/usr/sbin/haproxyctl reload
|
||||
else
|
||||
lxc-attach -n haproxy -- /etc/init.d/haproxy reload 2>/dev/null || \
|
||||
/etc/init.d/haproxy reload 2>/dev/null
|
||||
fi
|
||||
|
||||
log_ok "SSL backend configured"
|
||||
}
|
||||
|
||||
cmd_ssl_list() {
|
||||
load_config
|
||||
|
||||
log_info "HAProxy SSL Backends:"
|
||||
echo ""
|
||||
printf "%-15s %-30s %-20s\n" "SERVICE" "DOMAIN" "BACKEND"
|
||||
printf "%-15s %-30s %-20s\n" "---------------" "------------------------------" "--------------------"
|
||||
|
||||
# Parse from HAProxy config
|
||||
if [ -f "$HAPROXY_CONFIG" ]; then
|
||||
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" | while read line; do
|
||||
local backend=$(echo "$line" | awk '{print $2}')
|
||||
local service=$(echo "$backend" | sed 's/_backend$//')
|
||||
|
||||
# Get domain from ACL
|
||||
local domain=$(grep "acl host_${service} " "$HAPROXY_CONFIG" | awk '{print $NF}')
|
||||
|
||||
# Get server line
|
||||
local server=$(grep -A5 "backend $backend" "$HAPROXY_CONFIG" | grep "server " | awk '{print $3}')
|
||||
|
||||
printf "%-15s %-30s %-20s\n" "$service" "${domain:-N/A}" "${server:-N/A}"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
cmd_ssl_remove() {
|
||||
local service="$1"
|
||||
|
||||
if [ -z "$service" ]; then
|
||||
log_err "Usage: secubox-exposure ssl remove <service>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
if [ ! -f "$HAPROXY_CONFIG" ]; then
|
||||
log_err "HAProxy config not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! grep -q "backend ${service}_backend" "$HAPROXY_CONFIG"; then
|
||||
log_err "No backend found for $service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Removing SSL backend for $service"
|
||||
|
||||
# Remove ACL and use_backend lines
|
||||
sed -i "/acl host_${service} /d" "$HAPROXY_CONFIG"
|
||||
sed -i "/use_backend ${service}_backend/d" "$HAPROXY_CONFIG"
|
||||
|
||||
# Remove backend block
|
||||
sed -i "/# Backend for $service/,/^$/d" "$HAPROXY_CONFIG"
|
||||
sed -i "/^backend ${service}_backend$/,/^$/d" "$HAPROXY_CONFIG"
|
||||
|
||||
# Update UCI
|
||||
uci delete "${CONFIG_NAME}.${service}.ssl" 2>/dev/null
|
||||
uci delete "${CONFIG_NAME}.${service}.ssl_domain" 2>/dev/null
|
||||
uci commit "$CONFIG_NAME"
|
||||
|
||||
# Reload HAProxy
|
||||
if [ -x "/usr/sbin/haproxyctl" ]; then
|
||||
/usr/sbin/haproxyctl reload
|
||||
else
|
||||
lxc-attach -n haproxy -- /etc/init.d/haproxy reload 2>/dev/null || \
|
||||
/etc/init.d/haproxy reload 2>/dev/null
|
||||
fi
|
||||
|
||||
log_ok "SSL backend removed"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# STATUS & HELP
|
||||
# ============================================================================
|
||||
|
||||
cmd_status() {
|
||||
load_config
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${CYAN} SecuBox Service Exposure Status${NC}"
|
||||
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# Count services
|
||||
local total_services=$(get_listening_ports | wc -l)
|
||||
local external_services=$(get_listening_ports | grep -E "0\.0\.0\.0|::" | wc -l)
|
||||
|
||||
echo -e "${BLUE}Services:${NC}"
|
||||
echo " Total listening: $total_services"
|
||||
echo " External (0.0.0.0): $external_services"
|
||||
echo ""
|
||||
|
||||
# Tor status
|
||||
local tor_services=0
|
||||
[ -d "$TOR_HIDDEN_DIR" ] && tor_services=$(ls -1 "$TOR_HIDDEN_DIR" 2>/dev/null | wc -l)
|
||||
echo -e "${BLUE}Tor Hidden Services:${NC} $tor_services"
|
||||
if [ "$tor_services" -gt 0 ]; then
|
||||
for dir in "$TOR_HIDDEN_DIR"/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
local svc=$(basename "$dir")
|
||||
local onion=$(cat "$dir/hostname" 2>/dev/null)
|
||||
[ -n "$onion" ] && echo " - $svc: ${onion:0:16}..."
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# HAProxy backends
|
||||
local ssl_backends=0
|
||||
[ -f "$HAPROXY_CONFIG" ] && ssl_backends=$(grep -c "^backend.*_backend$" "$HAPROXY_CONFIG" 2>/dev/null || echo 0)
|
||||
echo -e "${BLUE}HAProxy SSL Backends:${NC} $ssl_backends"
|
||||
if [ "$ssl_backends" -gt 0 ] && [ -f "$HAPROXY_CONFIG" ]; then
|
||||
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" | while read line; do
|
||||
local backend=$(echo "$line" | awk '{print $2}' | sed 's/_backend$//')
|
||||
echo " - $backend"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
cmd_help() {
|
||||
cat << EOF
|
||||
SecuBox Service Exposure Manager
|
||||
|
||||
Usage: secubox-exposure <command> [options]
|
||||
|
||||
COMMANDS:
|
||||
scan Scan all listening services
|
||||
conflicts Detect port conflicts
|
||||
fix-port <svc> [port] Change service port (auto-assigns if no port given)
|
||||
status Show exposure status summary
|
||||
|
||||
tor add <svc> [port] Create Tor hidden service
|
||||
tor list List hidden services
|
||||
tor remove <svc> Remove hidden service
|
||||
|
||||
ssl add <svc> <domain> Add HAProxy SSL backend
|
||||
ssl list List SSL backends
|
||||
ssl remove <svc> Remove SSL backend
|
||||
|
||||
EXAMPLES:
|
||||
secubox-exposure scan
|
||||
secubox-exposure conflicts
|
||||
secubox-exposure fix-port domoticz 8180
|
||||
|
||||
secubox-exposure tor add gitea
|
||||
secubox-exposure tor add streamlit 8501 80
|
||||
secubox-exposure tor list
|
||||
|
||||
secubox-exposure ssl add gitea git.example.com
|
||||
secubox-exposure ssl add streamlit app.example.com 8501
|
||||
secubox-exposure ssl list
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MAIN
|
||||
# ============================================================================
|
||||
|
||||
case "$1" in
|
||||
scan)
|
||||
cmd_scan
|
||||
;;
|
||||
conflicts)
|
||||
load_config
|
||||
cmd_conflicts
|
||||
;;
|
||||
fix-port)
|
||||
cmd_fix_port "$2" "$3"
|
||||
;;
|
||||
status)
|
||||
cmd_status
|
||||
;;
|
||||
tor)
|
||||
case "$2" in
|
||||
add) cmd_tor_add "$3" "$4" "$5" ;;
|
||||
list) cmd_tor_list ;;
|
||||
remove) cmd_tor_remove "$3" ;;
|
||||
*) log_err "Usage: secubox-exposure tor {add|list|remove}"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
ssl)
|
||||
case "$2" in
|
||||
add) cmd_ssl_add "$3" "$4" "$5" ;;
|
||||
list) cmd_ssl_list ;;
|
||||
remove) cmd_ssl_remove "$3" ;;
|
||||
*) log_err "Usage: secubox-exposure ssl {add|list|remove}"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
help|--help|-h)
|
||||
cmd_help
|
||||
;;
|
||||
*)
|
||||
cmd_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Loading…
Reference in New Issue
Block a user