#!/bin/bash # # CrowdSec UniFi Suite - Interactive Installer # One-command installer for the complete CrowdSec + UniFi security stack # # Usage: # curl -sSL https://raw.githubusercontent.com/wolffcatskyy/crowdsec-unifi-suite/main/install.sh | bash # # Components: # ON UNIFI DEVICE: # - CrowdSec Engine # - UniFi Parser Collection (wolffcatskyy/unifi) # - UniFi Bouncer (firewall ipset enforcement) # # ON SERVER (Docker): # - Sidecar Proxy (decision prioritization + capping) # - Blocklist Import (external threat feed importer) # - AbuseIPDB Reporter (optional abuse reporting) # # --------------------------------------------------------------------------- # curl-pipe stdin fix # When invoked via `curl | bash`, stdin is the curl stream, not the terminal. # We save the script to a temp file and re-exec with /dev/tty as stdin. # --------------------------------------------------------------------------- if [ ! -t 0 ]; then SELF_URL="https://raw.githubusercontent.com/wolffcatskyy/crowdsec-unifi-suite/main/install.sh" TMPFILE=$(mktemp /tmp/unifi-suite-install.XXXXXX.sh) trap 'rm -f "$TMPFILE"' EXIT cat > "$TMPFILE" exec bash "$TMPFILE" "$@" /dev/null echo "" >> "$LOG_FILE" echo "=======================================" >> "$LOG_FILE" echo "Session started: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE" echo "=======================================" >> "$LOG_FILE" } log() { echo "[$(date '+%H:%M:%S')] $1" >> "$LOG_FILE" 2>/dev/null } # --------------------------------------------------------------------------- # Display Helpers # --------------------------------------------------------------------------- print_line() { echo -e "${DIM}_______________________________________________________________${NC}" } print_status() { local label="$1" local status="$2" case "$status" in installed) echo -e " ${GREEN}+${NC} ${label} ${GREEN}installed${NC}" ;; partial) echo -e " ${YELLOW}~${NC} ${label} ${YELLOW}partial${NC}" ;; missing) echo -e " ${RED}-${NC} ${label} ${DIM}not installed${NC}" ;; enabled) echo -e " ${GREEN}+${NC} ${label} ${GREEN}enabled${NC}" ;; disabled) echo -e " ${DIM}-${NC} ${label} ${DIM}disabled${NC}" ;; esac } msg_info() { echo -e " ${BLUE}i${NC} $1" } msg_ok() { echo -e " ${GREEN}+${NC} $1" } msg_warn() { echo -e " ${YELLOW}!${NC} $1" } msg_err() { echo -e " ${RED}x${NC} $1" } msg_dim() { echo -e " ${DIM}$1${NC}" } press_any_key() { echo "" echo -e " ${DIM}Press any key to continue...${NC}" read -n 1 -s Decide -> Enforce${NC}" print_line echo "" } # --------------------------------------------------------------------------- # Environment Detection # --------------------------------------------------------------------------- detect_environment() { log "Starting environment detection" # Architecture ARCH=$(uname -m) if [ "$ARCH" = "mips64" ]; then show_banner msg_err "USG devices (mips64) are not supported." msg_info "This suite requires UniFi OS 2.x+ (arm64/amd64)." echo "" exit 1 fi log "Architecture: $ARCH" # UniFi device detection if [ -f /proc/ubnthal/system.info ] || command -v ubnt-device-info >/dev/null 2>&1 || [ -d /data/unifi-core ]; then IS_UNIFI=true log "UniFi device detected" fi # Device model if command -v ubnt-device-info >/dev/null 2>&1; then DEVICE_MODEL=$(ubnt-device-info model 2>/dev/null) elif [ -f /proc/ubnthal/system.info ]; then DEVICE_MODEL=$(grep -i shortname /proc/ubnthal/system.info 2>/dev/null | cut -d= -f2 | tr -d ' ') fi log "Device model: ${DEVICE_MODEL:-unknown}" # ipset capacity by model case "$DEVICE_MODEL" in EFG|UXG-Enterprise) IPSET_CAPACITY=80000; DEVICE_TIER="enterprise" ;; UDM-SE|UDMSE|UDM-Pro*|UDMPRO*|UDW|UCG-*|UXG-*) IPSET_CAPACITY=50000; DEVICE_TIER="pro" ;; UDM|UDR|UDR7|UX7) IPSET_CAPACITY=15000; DEVICE_TIER="consumer" ;; *) IPSET_CAPACITY=10000; DEVICE_TIER="unknown" ;; esac log "Device tier: $DEVICE_TIER, ipset capacity: $IPSET_CAPACITY" # Docker host detection if command -v docker >/dev/null 2>&1; then if docker info >/dev/null 2>&1; then IS_DOCKER_HOST=true log "Docker available" fi fi # Component detection if command -v cscli >/dev/null 2>&1; then CS_INSTALLED=true log "CrowdSec engine found" fi if [ -f /etc/crowdsec/hub/collections/wolffcatskyy/unifi.yaml ] || \ cscli collections list 2>/dev/null | grep -q "wolffcatskyy/unifi"; then PARSER_INSTALLED=true log "UniFi parser collection found" fi if [ -f /data/crowdsec-bouncer/crowdsec-firewall-bouncer ]; then BOUNCER_INSTALLED=true log "UniFi bouncer found" fi if docker ps 2>/dev/null | grep -q crowdsec-sidecar; then SIDECAR_INSTALLED=true log "Sidecar proxy running" fi if docker ps -a 2>/dev/null | grep -q crowdsec-blocklist-import; then BLOCKLIST_INSTALLED=true log "Blocklist import found" fi # AbuseIPDB check if [ "$SIDECAR_INSTALLED" = true ]; then if docker inspect crowdsec-sidecar 2>/dev/null | grep -q '"ABUSEIPDB_REPORT_ENABLED=true"'; then ABUSEIPDB_ENABLED=true log "AbuseIPDB reporting enabled" fi fi } # --------------------------------------------------------------------------- # Root Check # --------------------------------------------------------------------------- check_root() { if [ "$(id -u)" -ne 0 ]; then msg_err "This installer must be run as root." msg_info "Try: sudo bash install.sh" echo "" exit 1 fi } # --------------------------------------------------------------------------- # Main Menu # --------------------------------------------------------------------------- show_main_menu() { echo -e " ${BOLD}Main Menu${NC}" echo "" echo -e " ${BOLD}[1]${NC} Install Full Suite ${DIM}(recommended)${NC}" echo -e " ${BOLD}[2]${NC} Install Individual Components" echo -e " ${BOLD}[3]${NC} Check Installation Status" echo -e " ${BOLD}[4]${NC} Update Components" echo -e " ${BOLD}[5]${NC} Uninstall Components" echo "" print_line echo -e " ${BOLD}[0]${NC} Exit" echo "" read -n 1 -s choice /dev/null 2>&1; then msg_ok "Service: running" else msg_warn "Service: stopped" fi local ipset_count ipset_count=$(ipset list crowdsec-blacklists 2>/dev/null | grep -c "^[0-9]" || echo "0") msg_info "Blocked IPs: ${ipset_count}" echo "" fi print_line echo -e " ${BOLD}[R]${NC} Refresh ${BOLD}[B]${NC} Back" echo "" read -n 1 -s choice /dev/null | awk '{print $1}'):8084/" echo "" fi echo -e " ${DIM}Documentation: ${GITHUB_BASE}/crowdsec-unifi-suite${NC}" echo "" } # --------------------------------------------------------------------------- # Install: CrowdSec Engine # --------------------------------------------------------------------------- install_crowdsec() { if [ "$CS_INSTALLED" = true ]; then msg_ok "CrowdSec is already installed." local cs_ver cs_ver=$(cscli version 2>/dev/null | grep -oP 'version:\s*\Kv[\d.]+' || echo "unknown") msg_info "Version: ${cs_ver}" return 0 fi msg_info "Installing CrowdSec Engine..." log "Installing CrowdSec Engine" # The official installer configures the package repository; package # installation is a separate step. Capture each pipeline's real status so # tee cannot mask a failed curl, shell, or package-manager command. curl -fsSL https://install.crowdsec.net | bash 2>&1 | tee -a "$LOG_FILE" if [ "${PIPESTATUS[0]}" -ne 0 ] || [ "${PIPESTATUS[1]}" -ne 0 ]; then msg_err "CrowdSec repository setup failed." log "CrowdSec repository setup failed" return 1 fi if command -v apt-get >/dev/null 2>&1; then apt-get update 2>&1 | tee -a "$LOG_FILE" [ "${PIPESTATUS[0]}" -eq 0 ] || return 1 apt-get install -y crowdsec 2>&1 | tee -a "$LOG_FILE" [ "${PIPESTATUS[0]}" -eq 0 ] || return 1 elif command -v yum >/dev/null 2>&1; then yum install -y crowdsec 2>&1 | tee -a "$LOG_FILE" [ "${PIPESTATUS[0]}" -eq 0 ] || return 1 elif command -v apk >/dev/null 2>&1; then apk add crowdsec 2>&1 | tee -a "$LOG_FILE" [ "${PIPESTATUS[0]}" -eq 0 ] || return 1 else msg_err "No supported package manager found (apt-get, yum, or apk)." return 1 fi hash -r # Verify installation if ! command -v cscli >/dev/null 2>&1; then msg_err "CrowdSec binary not found after installation." return 1 fi CS_INSTALLED=true msg_ok "CrowdSec Engine installed." # Offer console enrollment echo "" echo -e " ${BOLD}Enroll in CrowdSec Console? [y/N]${NC} " read -n 1 -s enroll &1 | tee -a "$LOG_FILE"; then msg_ok "Enrolled in CrowdSec Console." else msg_warn "Enrollment failed. You can enroll later with:" msg_dim " cscli console enroll " fi fi fi # Install base collections msg_info "Installing base collections..." cscli collections install crowdsecurity/linux 2>&1 | tee -a "$LOG_FILE" || true cscli collections install crowdsecurity/sshd 2>&1 | tee -a "$LOG_FILE" || true return 0 } # --------------------------------------------------------------------------- # Install: UniFi Parser Collection # --------------------------------------------------------------------------- install_parser() { if [ "$PARSER_INSTALLED" = true ]; then msg_ok "UniFi Parser Collection is already installed." return 0 fi if [ "$CS_INSTALLED" = false ]; then msg_err "CrowdSec Engine is required but not installed." msg_info "Install CrowdSec first (option 1)." return 1 fi msg_info "Installing UniFi Parser Collection..." log "Installing UniFi Parser Collection" # Try Hub install first if cscli collections install wolffcatskyy/unifi 2>&1 | tee -a "$LOG_FILE"; then PARSER_INSTALLED=true msg_ok "Parser collection installed from CrowdSec Hub." # Reload CrowdSec to pick up new parsers msg_info "Reloading CrowdSec..." systemctl reload crowdsec 2>/dev/null || cscli hub update 2>/dev/null || true return 0 fi # Fallback: install from GitHub msg_warn "Hub install failed. Trying direct install from GitHub..." if curl -sSL "${GITHUB_RAW}/crowdsec-unifi-parser/main/install.sh" | bash 2>&1 | tee -a "$LOG_FILE"; then PARSER_INSTALLED=true msg_ok "Parser collection installed from GitHub." return 0 fi msg_err "Parser collection installation failed." return 1 } # --------------------------------------------------------------------------- # Install: UniFi Bouncer # --------------------------------------------------------------------------- install_bouncer() { if [ "$BOUNCER_INSTALLED" = true ]; then msg_ok "UniFi Bouncer is already installed." return 0 fi msg_info "Installing UniFi Bouncer..." log "Installing UniFi Bouncer" # Delegate to the bouncer's own bootstrap script if ! curl -sSL "${GITHUB_RAW}/crowdsec-unifi-bouncer/main/bootstrap.sh" | bash 2>&1 | tee -a "$LOG_FILE"; then msg_err "Bouncer bootstrap failed." return 1 fi # Prompt for LAPI configuration echo "" msg_info "Configure bouncer connection to CrowdSec LAPI." echo "" local default_api_url="http://192.168.1.1:8080/" echo -n " LAPI URL [${default_api_url}]: " read -r api_url /dev/null 2>&1; then msg_info "Starting bouncer service..." systemctl restart crowdsec-firewall-bouncer 2>&1 | tee -a "$LOG_FILE" || true else msg_info "Enabling bouncer service..." systemctl enable crowdsec-firewall-bouncer 2>&1 | tee -a "$LOG_FILE" || true systemctl start crowdsec-firewall-bouncer 2>&1 | tee -a "$LOG_FILE" || true fi # Verify sleep 2 if systemctl is-active crowdsec-firewall-bouncer >/dev/null 2>&1; then msg_ok "Bouncer service is running." else msg_warn "Bouncer service may not have started. Check with:" msg_dim " systemctl status crowdsec-firewall-bouncer" fi BOUNCER_INSTALLED=true return 0 } # --------------------------------------------------------------------------- # Install: Sidecar Proxy # --------------------------------------------------------------------------- install_sidecar() { if [ "$SIDECAR_INSTALLED" = true ]; then msg_ok "Sidecar Proxy is already running." return 0 fi if [ "$IS_DOCKER_HOST" = false ]; then msg_err "Docker is required for the Sidecar Proxy." msg_info "Install Docker first or run this on a Docker-capable host." return 1 fi msg_info "Installing Sidecar Proxy..." log "Installing Sidecar Proxy" local install_dir="/opt/crowdsec-suite" mkdir -p "$install_dir" # Download compose and config files msg_info "Downloading configuration files..." local suite_raw="${GITHUB_RAW}/crowdsec-unifi-suite/main" if ! curl -sSL "${suite_raw}/docker-compose.yml" -o "${install_dir}/docker-compose.yml"; then msg_err "Failed to download docker-compose.yml" return 1 fi if ! curl -sSL "${suite_raw}/sidecar-config.yaml" -o "${install_dir}/sidecar-config.yaml"; then msg_err "Failed to download sidecar-config.yaml" return 1 fi # Prompt for bouncer API key echo "" msg_info "The sidecar needs a CrowdSec bouncer API key." msg_dim " Generate one with: docker exec crowdsec cscli bouncers add crowdsec-sidecar" echo "" echo -n " Sidecar Bouncer API Key: " read -r sidecar_key "${install_dir}/.env" <&1 | tee -a "$LOG_FILE"; then sleep 3 if docker ps | grep -q crowdsec-sidecar; then SIDECAR_INSTALLED=true msg_ok "Sidecar Proxy is running on port 8084." else msg_warn "Container started but may not be healthy yet." msg_dim " Check with: docker logs crowdsec-sidecar" fi else msg_err "Failed to start Sidecar Proxy." return 1 fi return 0 } # --------------------------------------------------------------------------- # Install: Blocklist Import # --------------------------------------------------------------------------- install_blocklist() { if [ "$BLOCKLIST_INSTALLED" = true ]; then msg_ok "Blocklist Import is already installed." return 0 fi msg_info "Installing Blocklist Import..." log "Installing Blocklist Import" if [ "$IS_DOCKER_HOST" = true ]; then # Docker install local install_dir="/opt/crowdsec-suite" mkdir -p "$install_dir" # Make sure compose file exists if [ ! -f "${install_dir}/docker-compose.yml" ]; then msg_info "Downloading docker-compose.yml..." curl -sSL "${GITHUB_RAW}/crowdsec-unifi-suite/main/docker-compose.yml" \ -o "${install_dir}/docker-compose.yml" || { msg_err "Failed to download docker-compose.yml" return 1 } fi # Create .env if missing if [ ! -f "${install_dir}/.env" ]; then msg_warn "No .env file found." msg_info "You need CrowdSec machine credentials for blocklist-import." echo "" echo -n " Machine ID [blocklist-import]: " read -r bl_machine_id "${install_dir}/.env" <&1 | tee -a "$LOG_FILE"; then sleep 3 if docker ps -a | grep -q crowdsec-blocklist-import; then BLOCKLIST_INSTALLED=true msg_ok "Blocklist Import container started." else msg_warn "Container may not have started correctly." msg_dim " Check with: docker logs crowdsec-blocklist-import" fi else msg_err "Failed to start Blocklist Import." return 1 fi else # Non-Docker: use the standalone installer msg_info "Installing via standalone installer..." if curl -sSL "${GITHUB_RAW}/crowdsec-blocklist-import/main/install.sh" | bash 2>&1 | tee -a "$LOG_FILE"; then BLOCKLIST_INSTALLED=true msg_ok "Blocklist Import installed." else msg_err "Blocklist Import installation failed." return 1 fi fi return 0 } # --------------------------------------------------------------------------- # Configure: AbuseIPDB Reporter # --------------------------------------------------------------------------- configure_abuseipdb() { if [ "$SIDECAR_INSTALLED" = false ]; then msg_err "Sidecar Proxy must be installed first." msg_info "AbuseIPDB reporting is a feature of the Sidecar Proxy." return 1 fi msg_info "Configuring AbuseIPDB Reporter..." log "Configuring AbuseIPDB" echo "" msg_info "Get a free API key at: ${CYAN}https://www.abuseipdb.com/account/api${NC}" msg_dim " Free tier: 100 reports/day" echo "" echo -n " AbuseIPDB API Key: " read -r abuse_key &1 | tee -a "$LOG_FILE"; then ABUSEIPDB_ENABLED=true msg_ok "AbuseIPDB Reporter enabled." msg_info "Reports will be sent for locally-detected malicious IPs." else msg_err "Failed to restart Sidecar Proxy." return 1 fi return 0 } # --------------------------------------------------------------------------- # Update Menu # --------------------------------------------------------------------------- show_update_menu() { show_banner echo -e " ${BOLD}Update Components${NC}" echo "" if [ "$CS_INSTALLED" = true ]; then echo -e " ${BOLD}[1]${NC} CrowdSec Engine ${DIM}(apt/package manager)${NC}" fi if [ "$PARSER_INSTALLED" = true ]; then echo -e " ${BOLD}[2]${NC} UniFi Parser Collection ${DIM}(cscli upgrade)${NC}" fi if [ "$BOUNCER_INSTALLED" = true ]; then echo -e " ${BOLD}[3]${NC} UniFi Bouncer ${DIM}(re-run bootstrap)${NC}" fi if [ "$SIDECAR_INSTALLED" = true ]; then echo -e " ${BOLD}[4]${NC} Sidecar Proxy ${DIM}(docker pull)${NC}" fi if [ "$BLOCKLIST_INSTALLED" = true ]; then echo -e " ${BOLD}[5]${NC} Blocklist Import ${DIM}(docker pull)${NC}" fi echo "" echo -e " ${BOLD}[A]${NC} Update All" print_line echo -e " ${BOLD}[B]${NC} Back to main menu" echo "" read -n 1 -s choice /dev/null 2>&1; then apt-get update -qq && apt-get install --only-upgrade -y crowdsec 2>&1 | tee -a "$LOG_FILE" elif command -v yum >/dev/null 2>&1; then yum update -y crowdsec 2>&1 | tee -a "$LOG_FILE" else msg_warn "Unknown package manager. Update CrowdSec manually." return 1 fi cscli hub update 2>&1 | tee -a "$LOG_FILE" || true msg_ok "CrowdSec Engine updated." } update_parser() { msg_info "Updating UniFi Parser Collection..." if cscli collections upgrade wolffcatskyy/unifi 2>&1 | tee -a "$LOG_FILE"; then systemctl reload crowdsec 2>/dev/null || true msg_ok "Parser collection updated." else msg_warn "Update failed. Trying fresh install..." cscli collections install wolffcatskyy/unifi --force 2>&1 | tee -a "$LOG_FILE" || true fi } update_bouncer() { msg_info "Updating UniFi Bouncer..." msg_info "Re-running bootstrap installer..." if curl -sSL "${GITHUB_RAW}/crowdsec-unifi-bouncer/main/bootstrap.sh" | bash 2>&1 | tee -a "$LOG_FILE"; then systemctl restart crowdsec-firewall-bouncer 2>/dev/null || true msg_ok "Bouncer updated." else msg_err "Bouncer update failed." return 1 fi } update_sidecar() { msg_info "Updating Sidecar Proxy..." local install_dir="/opt/crowdsec-suite" if [ -f "${install_dir}/docker-compose.yml" ]; then cd "$install_dir" docker compose pull crowdsec-sidecar 2>&1 | tee -a "$LOG_FILE" docker compose up -d crowdsec-sidecar 2>&1 | tee -a "$LOG_FILE" msg_ok "Sidecar Proxy updated." else msg_err "Compose file not found at ${install_dir}/docker-compose.yml" return 1 fi } update_blocklist() { msg_info "Updating Blocklist Import..." local install_dir="/opt/crowdsec-suite" if [ -f "${install_dir}/docker-compose.yml" ]; then cd "$install_dir" docker compose pull blocklist-import 2>&1 | tee -a "$LOG_FILE" docker compose up -d blocklist-import 2>&1 | tee -a "$LOG_FILE" msg_ok "Blocklist Import updated." else msg_err "Compose file not found at ${install_dir}/docker-compose.yml" return 1 fi } update_all() { show_banner echo -e " ${BOLD}Updating All Components${NC}" echo "" [ "$CS_INSTALLED" = true ] && update_crowdsec echo "" [ "$PARSER_INSTALLED" = true ] && update_parser echo "" [ "$BOUNCER_INSTALLED" = true ] && update_bouncer echo "" [ "$SIDECAR_INSTALLED" = true ] && update_sidecar echo "" [ "$BLOCKLIST_INSTALLED" = true ] && update_blocklist echo "" msg_ok "All components updated." } # --------------------------------------------------------------------------- # Uninstall Menu # --------------------------------------------------------------------------- show_uninstall_menu() { show_banner echo -e " ${BOLD}Uninstall Components${NC}" echo "" echo -e " ${RED}These actions are destructive and cannot be undone.${NC}" echo "" if [ "$BLOCKLIST_INSTALLED" = true ]; then echo -e " ${BOLD}[5]${NC} Blocklist Import" fi if [ "$SIDECAR_INSTALLED" = true ]; then echo -e " ${BOLD}[4]${NC} Sidecar Proxy" fi if [ "$BOUNCER_INSTALLED" = true ]; then echo -e " ${BOLD}[3]${NC} UniFi Bouncer" fi if [ "$PARSER_INSTALLED" = true ]; then echo -e " ${BOLD}[2]${NC} UniFi Parser Collection" fi if [ "$CS_INSTALLED" = true ]; then echo -e " ${BOLD}[1]${NC} CrowdSec Engine" fi echo "" echo -e " ${BOLD}[A]${NC} ${RED}Uninstall Everything${NC}" print_line echo -e " ${BOLD}[B]${NC} Back to main menu" echo "" read -n 1 -s choice /dev/null || true systemctl disable crowdsec 2>/dev/null || true if command -v apt-get >/dev/null 2>&1; then apt-get remove -y crowdsec 2>&1 | tee -a "$LOG_FILE" || true elif command -v yum >/dev/null 2>&1; then yum remove -y crowdsec 2>&1 | tee -a "$LOG_FILE" || true elif command -v apk >/dev/null 2>&1; then apk del crowdsec 2>&1 | tee -a "$LOG_FILE" || true else msg_warn "Unknown package manager. Remove CrowdSec packages manually." fi CS_INSTALLED=false PARSER_INSTALLED=false msg_ok "CrowdSec Engine uninstalled." } uninstall_parser() { msg_info "Uninstalling UniFi Parser Collection..." log "Uninstalling parser collection" if command -v cscli >/dev/null 2>&1; then cscli collections remove wolffcatskyy/unifi --force 2>&1 | tee -a "$LOG_FILE" || true systemctl reload crowdsec 2>/dev/null || true fi PARSER_INSTALLED=false msg_ok "UniFi Parser Collection removed." } uninstall_bouncer() { msg_info "Uninstalling UniFi Bouncer..." log "Uninstalling bouncer" # Stop service systemctl stop crowdsec-firewall-bouncer 2>/dev/null || true systemctl disable crowdsec-firewall-bouncer 2>/dev/null || true # Remove systemd units rm -f /etc/systemd/system/crowdsec-firewall-bouncer.service 2>/dev/null rm -f /etc/systemd/system/crowdsec-unifi-metrics.service 2>/dev/null systemctl daemon-reload 2>/dev/null || true # Clean ipsets msg_info "Cleaning up ipsets..." ipset destroy crowdsec-blacklists 2>/dev/null || true ipset destroy crowdsec6-blacklists 2>/dev/null || true # Clean iptables rules iptables -D INPUT -m set --match-set crowdsec-blacklists src -j DROP 2>/dev/null || true ip6tables -D INPUT -m set --match-set crowdsec6-blacklists src -j DROP 2>/dev/null || true iptables -D FORWARD -m set --match-set crowdsec-blacklists src -j DROP 2>/dev/null || true ip6tables -D FORWARD -m set --match-set crowdsec6-blacklists src -j DROP 2>/dev/null || true # Remove bouncer directory rm -rf /data/crowdsec-bouncer 2>/dev/null BOUNCER_INSTALLED=false msg_ok "UniFi Bouncer uninstalled and ipsets cleaned." } uninstall_sidecar() { msg_info "Uninstalling Sidecar Proxy..." log "Uninstalling sidecar" local install_dir="/opt/crowdsec-suite" if [ -f "${install_dir}/docker-compose.yml" ]; then cd "$install_dir" docker compose down crowdsec-sidecar 2>&1 | tee -a "$LOG_FILE" || true else docker stop crowdsec-sidecar 2>/dev/null || true docker rm crowdsec-sidecar 2>/dev/null || true fi SIDECAR_INSTALLED=false ABUSEIPDB_ENABLED=false msg_ok "Sidecar Proxy removed." } uninstall_blocklist() { msg_info "Uninstalling Blocklist Import..." log "Uninstalling blocklist import" local install_dir="/opt/crowdsec-suite" if [ -f "${install_dir}/docker-compose.yml" ]; then cd "$install_dir" docker compose down blocklist-import 2>&1 | tee -a "$LOG_FILE" || true else docker stop crowdsec-blocklist-import 2>/dev/null || true docker rm crowdsec-blocklist-import 2>/dev/null || true fi BLOCKLIST_INSTALLED=false msg_ok "Blocklist Import removed." } uninstall_all() { msg_info "Uninstalling all CrowdSec UniFi Suite components..." log "Uninstalling all components" # Reverse order: dependents first [ "$BLOCKLIST_INSTALLED" = true ] && uninstall_blocklist [ "$SIDECAR_INSTALLED" = true ] && uninstall_sidecar [ "$BOUNCER_INSTALLED" = true ] && uninstall_bouncer [ "$PARSER_INSTALLED" = true ] && uninstall_parser [ "$CS_INSTALLED" = true ] && uninstall_crowdsec # Clean up suite directory if [ -d "/opt/crowdsec-suite" ]; then echo "" echo -e " ${BOLD}Remove /opt/crowdsec-suite directory? [y/N]${NC} " read -n 1 -s rm_dir /dev/null echo "" msg_ok "All CrowdSec UniFi Suite components have been removed." } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- main() { log_init check_root detect_environment while true; do show_banner show_main_menu done } main "$@"