#!/usr/bin/env bash set -Eeuo pipefail # ============================================================================= # OPNsense NGINX Reverse Proxy Delete Script # ============================================================================= # Dieses Script löscht einen NGINX Reverse Proxy auf OPNsense # für eine n8n-Instanz über die OPNsense API. # ============================================================================= SCRIPT_VERSION="1.0.2" # Debug mode: 0 = nur JSON, 1 = Logs auf stderr DEBUG="${DEBUG:-0}" export DEBUG # Logging functions log_ts() { date "+[%F %T]"; } info() { [[ "$DEBUG" == "1" ]] && echo "$(log_ts) INFO: $*" >&2; return 0; } warn() { [[ "$DEBUG" == "1" ]] && echo "$(log_ts) WARN: $*" >&2; return 0; } die() { if [[ "$DEBUG" == "1" ]]; then echo "$(log_ts) ERROR: $*" >&2 else echo "{\"error\": \"$*\"}" fi exit 1 } # ============================================================================= # Default Configuration # ============================================================================= OPNSENSE_HOST="${OPNSENSE_HOST:-192.168.45.1}" OPNSENSE_PORT="${OPNSENSE_PORT:-4444}" OPNSENSE_API_KEY="${OPNSENSE_API_KEY:-cUUs80IDkQelMJVgAVK2oUoDHrQf+cQPwXoPKNd3KDIgiCiEyEfMq38UTXeY5/VO/yWtCC7k9Y9kJ0Pn}" OPNSENSE_API_SECRET="${OPNSENSE_API_SECRET:-2egxxFYCAUjBDp0OrgbJO3NBZmR4jpDm028jeS8Nq8OtCGu/0lAxt4YXWXbdZjcFVMS0Nrhru1I2R1si}" # ============================================================================= # Usage # ============================================================================= usage() { cat >&2 <<'EOF' Usage: bash delete_nginx_proxy.sh [options] Required options: --ctid Container ID (used to find components by description) Optional: --fqdn Full domain name (to find HTTP Server by servername) --opnsense-host OPNsense IP or hostname (default: 192.168.45.1) --opnsense-port OPNsense WebUI/API port (default: 4444) --dry-run Show what would be deleted without actually deleting --debug Enable debug mode --help Show this help Examples: # Delete proxy by CTID: bash delete_nginx_proxy.sh --ctid 768736636 # Delete proxy with debug output: bash delete_nginx_proxy.sh --debug --ctid 768736636 # Dry run (show what would be deleted): bash delete_nginx_proxy.sh --dry-run --ctid 768736636 # Delete by CTID and FQDN: bash delete_nginx_proxy.sh --ctid 768736636 --fqdn sb-1768736636.userman.de EOF } # ============================================================================= # Default values for arguments # ============================================================================= CTID="" FQDN="" DRY_RUN="0" # ============================================================================= # Argument parsing # ============================================================================= while [[ $# -gt 0 ]]; do case "$1" in --ctid) CTID="${2:-}"; shift 2 ;; --fqdn) FQDN="${2:-}"; shift 2 ;; --opnsense-host) OPNSENSE_HOST="${2:-}"; shift 2 ;; --opnsense-port) OPNSENSE_PORT="${2:-}"; shift 2 ;; --dry-run) DRY_RUN="1"; shift 1 ;; --debug) DEBUG="1"; export DEBUG; shift 1 ;; --help|-h) usage; exit 0 ;; *) die "Unknown option: $1 (use --help)" ;; esac done # ============================================================================= # API Base URL # ============================================================================= API_BASE="https://${OPNSENSE_HOST}:${OPNSENSE_PORT}/api" # ============================================================================= # API Helper Functions # ============================================================================= # Make API request to OPNsense api_request() { local method="$1" local endpoint="$2" local data="${3:-}" local url="${API_BASE}${endpoint}" local auth="${OPNSENSE_API_KEY}:${OPNSENSE_API_SECRET}" info "API ${method} ${url}" local response if [[ -n "$data" ]]; then response=$(curl -s -k -X "${method}" \ -u "${auth}" \ -H "Content-Type: application/json" \ -d "${data}" \ "${url}" 2>&1) else response=$(curl -s -k -X "${method}" \ -u "${auth}" \ "${url}" 2>&1) fi echo "$response" } # Search for items by description search_by_description() { local search_endpoint="$1" local description="$2" local response response=$(api_request "GET" "${search_endpoint}") info "Search response for ${search_endpoint}: ${response:0:500}..." # Extract all UUIDs where description matches local uuid uuid=$(echo "$response" | python3 -c " import json, sys desc = sys.argv[1] if len(sys.argv) > 1 else '' try: data = json.load(sys.stdin) rows = data.get('rows', []) for row in rows: row_desc = row.get('description', '') if row_desc == desc: print(row.get('uuid', '')) sys.exit(0) except Exception as e: print(f'Error: {e}', file=sys.stderr) " "${description}" 2>/dev/null || true) info "Found UUID for description '${description}': ${uuid:-none}" echo "$uuid" } # Search for HTTP Server by servername search_http_server_by_servername() { local servername="$1" local response response=$(api_request "GET" "/nginx/settings/searchHttpServer") info "HTTP Server search response: ${response:0:500}..." # Extract UUID where servername matches local uuid uuid=$(echo "$response" | python3 -c " import json, sys sname = sys.argv[1] if len(sys.argv) > 1 else '' try: data = json.load(sys.stdin) rows = data.get('rows', []) for row in rows: row_sname = row.get('servername', '') if row_sname == sname: print(row.get('uuid', '')) sys.exit(0) except Exception as e: print(f'Error: {e}', file=sys.stderr) " "${servername}" 2>/dev/null || true) info "Found HTTP Server UUID for servername '${servername}': ${uuid:-none}" echo "$uuid" } # ============================================================================= # Delete Functions # ============================================================================= delete_item() { local item_type="$1" local uuid="$2" local endpoint="$3" if [[ -z "$uuid" ]]; then info "No ${item_type} found to delete" return 0 fi if [[ "$DRY_RUN" == "1" ]]; then info "[DRY-RUN] Would delete ${item_type}: ${uuid}" echo "dry-run" return 0 fi info "Deleting ${item_type}: ${uuid}" local response response=$(api_request "POST" "${endpoint}/${uuid}") local result result=$(echo "$response" | python3 -c "import json,sys; print(json.load(sys.stdin).get('result','unknown'))" 2>/dev/null || echo "unknown") if [[ "$result" == "deleted" ]]; then info "${item_type} deleted successfully" echo "deleted" else warn "Failed to delete ${item_type}: ${response}" echo "failed" fi } # ============================================================================= # Validation # ============================================================================= [[ -n "$CTID" ]] || die "--ctid is required" info "Script Version: ${SCRIPT_VERSION}" info "Configuration:" info " CTID: ${CTID}" info " FQDN: ${FQDN:-auto-detect}" info " OPNsense: ${OPNSENSE_HOST}:${OPNSENSE_PORT}" info " Dry Run: ${DRY_RUN}" # ============================================================================= # Main # ============================================================================= main() { info "Starting NGINX Reverse Proxy deletion for CTID ${CTID}..." local description="${CTID}" local deleted_count=0 local failed_count=0 # Results tracking local http_server_result="not_found" local location_result="not_found" local upstream_result="not_found" local upstream_server_result="not_found" # Step 1: Find and delete HTTP Server info "Step 1: Finding HTTP Server..." local http_server_uuid="" # Try to find by FQDN first if [[ -n "$FQDN" ]]; then http_server_uuid=$(search_http_server_by_servername "${FQDN}") fi # If not found by FQDN, try common patterns if [[ -z "$http_server_uuid" ]]; then # Try sb-.userman.de pattern http_server_uuid=$(search_http_server_by_servername "sb-${CTID}.userman.de") fi if [[ -z "$http_server_uuid" ]]; then # Try sb-1.userman.de pattern (with leading 1) http_server_uuid=$(search_http_server_by_servername "sb-1${CTID}.userman.de") fi if [[ -n "$http_server_uuid" ]]; then http_server_result=$(delete_item "HTTP Server" "$http_server_uuid" "/nginx/settings/delHttpServer") if [[ "$http_server_result" == "deleted" || "$http_server_result" == "dry-run" ]]; then deleted_count=$((deleted_count + 1)) else failed_count=$((failed_count + 1)) fi else info "No HTTP Server found for CTID ${CTID}" fi # Step 2: Find and delete Location info "Step 2: Finding Location..." local location_uuid location_uuid=$(search_by_description "/nginx/settings/searchLocation" "${description}") if [[ -n "$location_uuid" ]]; then location_result=$(delete_item "Location" "$location_uuid" "/nginx/settings/delLocation") if [[ "$location_result" == "deleted" || "$location_result" == "dry-run" ]]; then deleted_count=$((deleted_count + 1)) else failed_count=$((failed_count + 1)) fi else info "No Location found for CTID ${CTID}" fi # Step 3: Find and delete Upstream info "Step 3: Finding Upstream..." local upstream_uuid upstream_uuid=$(search_by_description "/nginx/settings/searchUpstream" "${description}") if [[ -n "$upstream_uuid" ]]; then upstream_result=$(delete_item "Upstream" "$upstream_uuid" "/nginx/settings/delUpstream") if [[ "$upstream_result" == "deleted" || "$upstream_result" == "dry-run" ]]; then deleted_count=$((deleted_count + 1)) else failed_count=$((failed_count + 1)) fi else info "No Upstream found for CTID ${CTID}" fi # Step 4: Find and delete Upstream Server info "Step 4: Finding Upstream Server..." local upstream_server_uuid upstream_server_uuid=$(search_by_description "/nginx/settings/searchUpstreamServer" "${description}") if [[ -n "$upstream_server_uuid" ]]; then upstream_server_result=$(delete_item "Upstream Server" "$upstream_server_uuid" "/nginx/settings/delUpstreamServer") if [[ "$upstream_server_result" == "deleted" || "$upstream_server_result" == "dry-run" ]]; then deleted_count=$((deleted_count + 1)) else failed_count=$((failed_count + 1)) fi else info "No Upstream Server found for CTID ${CTID}" fi # Step 5: Apply configuration (if not dry-run and something was deleted) local reconfigure_result="skipped" if [[ "$DRY_RUN" != "1" && $deleted_count -gt 0 ]]; then info "Step 5: Applying NGINX configuration..." local response response=$(api_request "POST" "/nginx/service/reconfigure" "{}") local status status=$(echo "$response" | python3 -c "import json,sys; print(json.load(sys.stdin).get('status',''))" 2>/dev/null || echo "unknown") if [[ "$status" == "ok" ]]; then info "NGINX configuration applied successfully" reconfigure_result="ok" else warn "NGINX reconfigure status: ${status}" reconfigure_result="failed" fi elif [[ "$DRY_RUN" == "1" ]]; then info "[DRY-RUN] Would apply NGINX configuration" reconfigure_result="dry-run" fi # Output result as JSON local success="true" [[ $failed_count -gt 0 ]] && success="false" local result result=$(cat </dev/null || echo "$result" fi } main