CrowdSec Removal Script: Technical Overview (Cloudpanel, Ubuntu)

Script Purpose

A comprehensive Bash script designed to completely remove CrowdSec components from a Linux system, providing flexible uninstallation options.

Key Functionalities

Prerequisite Checks

  • Root privilege validation
  • Detection of package management system (apt/rpm)
  • Service and package discovery mechanisms

Removal Options

  1. Disable CrowdSec services
  2. Remove CrowdSec packages
  3. Complete removal (services, packages, configurations, repositories)

Detailed Component Removal Process

Service Handling

  • Identifies CrowdSec-related systemd services
  • Stops and disables active services
  • Uses systemctl for service management

Package Removal

  • Supports multiple package managers:
    • Debian/Ubuntu: Uses apt remove --purge
    • Red Hat/CentOS: Uses yum remove
  • Performs automatic dependency cleanup

Configuration Cleanup

  • Removes configuration directories:
    • /etc/crowdsec
    • /var/lib/crowdsec

Repository Management

  • Strips CrowdSec-related entries from APT sources
  • Runs apt update to refresh repository cache

Usage Modes

Interactive Mode

  • Provides user-driven removal process
  • Offers granular control over removal steps

Nuke Mode (-n flag)

  • Performs complete, non-interactive removal
  • Removes all CrowdSec components automatically

Safety Considerations

  • Requires root privileges
  • Provides color-coded console output for clarity
  • Implements error handling and validation checks

Compatibility

  • Supports Cloudpanel, Debian/Ubuntu and Red Hat-based distributions
  • Requires systemd and modern package management tools

Recommended Usage

# Interactive mode
sudo ./crowdsec_removal.sh

# Non-interactive complete removal
sudo ./crowdsec_removal.sh -n

Potential Risks

  • Completely removes CrowdSec security components
  • May impact existing security configurations
  • Recommended to backup system before execution

Version 2.0(2024)

I’ve significantly enhanced the script with several improvements:

  1. Enhanced Error Handling:

    • Added comprehensive error trapping
    • Implemented detailed error logging
    • Added cleanup functions for temporary files
    • Improved exit code handling
  2. Logging System:

    • Added detailed logging to /var/log/crowdsec_removal_[timestamp].log
    • Implemented structured log messages with timestamps
    • Added different log levels (INFO, WARNING, ERROR, SUCCESS)
  3. Backup Functionality:

    • Added -b/--backup option to create backups before removal
    • Backs up configuration files and service definitions
    • Creates timestamped backup directories
  4. Better System Compatibility:

    • Added support for multiple package managers (apt, yum, dnf)
    • Used tput instead of ANSI codes for better terminal compatibility
    • Added system requirements checking
    • Improved repository handling
  5. Command Line Options:

    • Added proper command-line argument parsing
    • Added -h/--help option for usage information
    • Added -v/--version option
    • Maintained -n/--nuke option for non-interactive mode
  6. Improved Configuration Removal:

    • Added more configuration directories to clean up
    • Better handling of service files
    • More thorough package dependency cleanup
  7. Code Organization:

    • Better function organization
    • Added version tracking
    • Improved variable naming and scope
    • Added meaningful comments

To use the improved script:

  1. Basic interactive mode:
sudo ./crowdsec_removal.sh
  1. Non-interactive complete removal with backup:
sudo ./crowdsec_removal.sh -n -b
  1. Show help:
./crowdsec_removal.sh -h

The script now provides:

  • Better safety with backups
  • Comprehensive logging
  • More robust error handling
  • Better system compatibility
  • More thorough cleanup
  • Better user interface
  • Better maintainability
#!/bin/bash

# Script version
VERSION="2.0.0"

# Color definitions using tput for better compatibility
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
NC=$(tput sgr0)

# Logging setup
LOGFILE="/var/log/crowdsec_removal_$(date +%Y%m%d_%H%M%S).log"
BACKUP_DIR="/var/backup/crowdsec_$(date +%Y%m%d_%H%M%S)"

# Trap errors and interrupts
trap 'error_handler $? $LINENO' ERR
trap 'cleanup' EXIT
trap 'interrupt_handler' INT TERM

# Function to handle errors
error_handler() {
    local exit_code=$1
    local line_number=$2
    log_message "ERROR" "Error $exit_code occurred on line $line_number"
    cleanup
    exit "$exit_code"
}

# Function to handle interrupts
interrupt_handler() {
    log_message "WARNING" "Script interrupted by user"
    cleanup
    exit 130
}

# Cleanup function
cleanup() {
    # Remove temporary files if they exist
    [ -f "/tmp/crowdsec_services.tmp" ] && rm -f "/tmp/crowdsec_services.tmp"
    [ -f "/tmp/crowdsec_packages.tmp" ] && rm -f "/tmp/crowdsec_packages.tmp"
}

# Enhanced logging function
log_message() {
    local level=$1
    local message=$2
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $message" >> "$LOGFILE"
    case $level in
        "INFO")    print_color "$BLUE" "$message" ;;
        "WARNING") print_color "$YELLOW" "$message" ;;
        "ERROR")   print_color "$RED" "$message" ;;
        "SUCCESS") print_color "$GREEN" "$message" ;;
    esac
}

# Function to print colored output
print_color() {
    printf "%s%s%s\n" "${1}" "${2}" "${NC}"
}

# Function to check if a command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Function to check system requirements
check_system_requirements() {
    local missing_deps=()
    
    # Check for required commands
    for cmd in systemctl grep awk sed; do
        if ! command_exists "$cmd"; then
            missing_deps+=("$cmd")
        fi
    done
    
    if [ ${#missing_deps[@]} -ne 0 ]; then
        log_message "ERROR" "Missing required dependencies: ${missing_deps[*]}"
        exit 1
    fi
}

# Function to check if running as root
check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_message "ERROR" "Please run as root"
        exit 1
    fi
}

# Function to create backup
create_backup() {
    log_message "INFO" "Creating backup of CrowdSec configurations..."
    
    mkdir -p "$BACKUP_DIR"
    
    # Backup configuration files
    if [ -d "/etc/crowdsec" ]; then
        cp -r "/etc/crowdsec" "$BACKUP_DIR/"
    fi
    
    if [ -d "/var/lib/crowdsec" ]; then
        cp -r "/var/lib/crowdsec" "$BACKUP_DIR/"
    fi
    
    # Backup service files
    if [ -d "/etc/systemd/system" ]; then
        find "/etc/systemd/system" -name "*crowdsec*" -exec cp {} "$BACKUP_DIR/" \;
    fi
    
    log_message "SUCCESS" "Backup created at $BACKUP_DIR"
}

# Enhanced service check function
check_services() {
    log_message "INFO" "Checking for CrowdSec services..."
    systemctl list-units --type=service --all | grep -i crowdsec | awk '{print $1}' > /tmp/crowdsec_services.tmp
    
    if [ ! -s /tmp/crowdsec_services.tmp ]; then
        log_message "SUCCESS" "No CrowdSec services found."
        services=""
    else
        services=$(cat /tmp/crowdsec_services.tmp)
        log_message "WARNING" "Found CrowdSec services:"
        echo "$services" | while read -r service; do
            log_message "INFO" "Found service: $service"
        done
    fi
}

# Enhanced package check function
check_packages() {
    log_message "INFO" "Checking for CrowdSec packages..."
    
    if command_exists dpkg; then
        dpkg -l | grep -i crowdsec | awk '{print $2}' > /tmp/crowdsec_packages.tmp
    elif command_exists rpm; then
        rpm -qa | grep -i crowdsec > /tmp/crowdsec_packages.tmp
    else
        log_message "ERROR" "Unable to determine package manager."
        return 1
    fi
    
    if [ ! -s /tmp/crowdsec_packages.tmp ]; then
        log_message "SUCCESS" "No CrowdSec packages found."
        packages=""
    else
        packages=$(cat /tmp/crowdsec_packages.tmp)
        log_message "WARNING" "Found CrowdSec packages:"
        echo "$packages" | while read -r package; do
            log_message "INFO" "Found package: $package"
        done
    fi
}

# Enhanced service disable function
disable_services() {
    log_message "INFO" "Disabling CrowdSec services..."
    if [ -z "$services" ]; then
        log_message "WARNING" "No CrowdSec services to disable."
    else
        echo "$services" | while read -r service; do
            systemctl stop "$service" 2>/dev/null
            if [ $? -eq 0 ]; then
                systemctl disable "$service" 2>/dev/null
                log_message "SUCCESS" "Disabled $service"
            else
                log_message "ERROR" "Failed to stop $service"
            fi
        done
    fi
}

# Enhanced package removal function
remove_packages() {
    log_message "INFO" "Removing CrowdSec packages..."
    if [ -z "$packages" ]; then
        log_message "WARNING" "No CrowdSec packages to remove."
        return
    fi
    
    local remove_cmd=""
    if command_exists apt-get; then
        remove_cmd="apt-get remove --purge -y"
        cleanup_cmd="apt-get autoremove -y"
    elif command_exists yum; then
        remove_cmd="yum remove -y"
        cleanup_cmd="yum autoremove -y"
    elif command_exists dnf; then
        remove_cmd="dnf remove -y"
        cleanup_cmd="dnf autoremove -y"
    else
        log_message "ERROR" "No supported package manager found."
        return 1
    fi
    
    echo "$packages" | while read -r package; do
        if $remove_cmd "$package"; then
            log_message "SUCCESS" "Removed package: $package"
        else
            log_message "ERROR" "Failed to remove package: $package"
        fi
    done
    
    if $cleanup_cmd; then
        log_message "SUCCESS" "Cleaned up dependencies"
    fi
}

# Enhanced repository removal function
remove_apt_repos() {
    log_message "INFO" "Removing CrowdSec repository entries..."
    if ! command_exists apt-get; then
        log_message "INFO" "System is not Debian/Ubuntu based, skipping repo removal"
        return
    fi
    
    local repo_files=("/etc/apt/sources.list" "/etc/apt/sources.list.d/"*)
    
    for file in "${repo_files[@]}"; do
        if [ -f "$file" ] && grep -q "crowdsec" "$file"; then
            if sed -i '/crowdsec/d' "$file"; then
                log_message "SUCCESS" "Removed CrowdSec entry from $file"
            else
                log_message "ERROR" "Failed to remove CrowdSec entry from $file"
            fi
        fi
    done
    
    if apt-get update >/dev/null 2>&1; then
        log_message "SUCCESS" "APT repositories updated"
    else
        log_message "ERROR" "Failed to update APT repositories"
    fi
}

# Enhanced configuration removal function
remove_config() {
    log_message "INFO" "Removing CrowdSec configuration files..."
    
    local config_dirs=(
        "/etc/crowdsec"
        "/var/lib/crowdsec"
        "/usr/local/lib/crowdsec"
        "/usr/share/crowdsec"
    )
    
    for dir in "${config_dirs[@]}"; do
        if [ -d "$dir" ]; then
            if rm -rf "$dir" 2>/dev/null; then
                log_message "SUCCESS" "Removed directory: $dir"
            else
                log_message "ERROR" "Failed to remove directory: $dir"
            fi
        fi
    done
}

# Function to display script version
show_version() {
    echo "CrowdSec Removal Script v${VERSION}"
    exit 0
}

# Function to display help
show_help() {
    cat << EOF
CrowdSec Removal Script v${VERSION}

Usage: $0 [OPTIONS]

Options:
  -n, --nuke     Remove all CrowdSec components without prompting
  -b, --backup   Create backup before removal
  -v, --version  Show script version
  -h, --help     Show this help message
  
The script can run in interactive mode (no options) or automatic mode (-n).
EOF
    exit 0
}

# Enhanced main function
main() {
    local NUKE=0
    local BACKUP=0
    
    # Parse command line arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            -n|--nuke)   NUKE=1 ;;
            -b|--backup) BACKUP=1 ;;
            -v|--version) show_version ;;
            -h|--help)    show_help ;;
            *)           log_message "ERROR" "Unknown option: $1"; exit 1 ;;
        esac
        shift
    done
    
    # Check requirements
    check_root
    check_system_requirements
    
    # Initialize log file
    touch "$LOGFILE" || { echo "Cannot create log file"; exit 1; }
    log_message "INFO" "Starting CrowdSec removal script v${VERSION}"
    
    # Create backup if requested
    if [ "$BACKUP" -eq 1 ]; then
        create_backup
    fi
    
    # Check current state
    check_services
    check_packages
    
    if [ "$NUKE" -eq 1 ]; then
        log_message "WARNING" "Performing complete removal of CrowdSec..."
        disable_services
        remove_packages
        remove_apt_repos
        remove_config
        log_message "SUCCESS" "CrowdSec has been completely removed from your system."
        exit 0
    fi
    
    if [ -z "$services" ] && [ -z "$packages" ]; then
        log_message "SUCCESS" "No CrowdSec components found on your system."
        exit 0
    fi
    
    # Interactive menu
    log_message "INFO" "What would you like to do?"
    echo "1) Disable CrowdSec services"
    echo "2) Remove CrowdSec packages"
    echo "3) Remove everything (services, packages, configuration, and repositories)"
    echo "4) Exit without changes"
    
    read -r -p "Enter your choice (1-4): " choice
    
    case $choice in
        1) disable_services ;;
        2) remove_packages ;;
        3)
            disable_services
            remove_packages
            remove_apt_repos
            remove_config
            ;;
        4) log_message "INFO" "Exiting without changes."; exit 0 ;;
        *) log_message "ERROR" "Invalid choice."; exit 1 ;;
    esac
    
    log_message "SUCCESS" "Operations completed successfully."
    echo "Log file available at: $LOGFILE"
}

main "$@"