diff --git a/install.sh b/install.sh index 0a869dc..49aec32 100644 --- a/install.sh +++ b/install.sh @@ -18,7 +18,10 @@ cat << "EOF" |_|_| |_|\__\___|\__, |_| \__,_|\__|_|\___/|_| |_| |___/ - >> Made by sefinek.net || Last update: 18.05.2025 << +πŸ“© Author : Sefinek (https://sefinek.net) +πŸ”΅ Discord : https://discord.gg/RVH8UXgmzs +😺 GitHub Issues : https://github.com/sefinek/UFW-AbuseIPDB-Reporter/issues +πŸ“¦ Last update : 20.12.2025 This installer will configure UFW-AbuseIPDB-Reporter, a tool that analyzes UFW logs and reports to AbuseIPDB the IP addresses that have violated firewall rules. Join my Discord @@ -75,7 +78,7 @@ check_dependencies() { if "$dependency" --version &> /dev/null; then "$dependency" --version | head -n 1 else - echo "ℹ️ Version information for $dependency is unavailable" + echo " Version information for $dependency is unavailable" fi fi done @@ -98,6 +101,82 @@ check_dependencies() { # Check dependencies before proceeding check_dependencies curl node git +# Function to extract value from config.js +extract_config_value() { + local key=$1 + local config_file="${2:-config.js}" + + if [[ ! -f $config_file ]]; then + echo "" + return + fi + + local value + value=$(grep -oP "^\s*${key}:\s*\K.*?(?=,\s*(//|$))" "$config_file" | sed "s/^['\"]//;s/['\"]$//" | sed 's/[[:space:]]*$//') + [[ $value == "null" ]] && value="" + echo "$value" +} + +# Function to ask for configuration with validation +ask_config() { + local prompt=$1 + local default=$2 + local validation_type=${3:-""} + local result + + while true; do + read -e -i "$default" -r -p "$prompt" result + result="${result:-$default}" + + case $validation_type in + bool) + if [[ $result =~ ^(true|false)$ ]]; then + echo "$result" + return 0 + else + echo "❌ Must be 'true' or 'false'" + fi + ;; + number) + if [[ $result =~ ^[0-9]+\.?[0-9]*$ ]]; then + echo "$result" + return 0 + else + echo "❌ Must be a valid number" + fi + ;; + url) + if [[ -z $result ]] || [[ $result =~ ^https?:// ]]; then + echo "$result" + return 0 + else + echo "❌ Must be a valid URL starting with http:// or https://" + fi + ;; + cron) + if [[ $result =~ ^[0-9*,/-]+\ [0-9*,/-]+\ [0-9*,/-]+\ [0-9*,/-]+\ [0-9*,/-]+$ ]]; then + echo "$result" + return 0 + else + echo "❌ Invalid cron format. Expected: 'minute hour day month weekday'" + fi + ;; + static_dynamic) + if [[ $result =~ ^(static|dynamic)$ ]]; then + echo "$result" + return 0 + else + echo "❌ Must be 'static' or 'dynamic'" + fi + ;; + *) + echo "$result" + return 0 + ;; + esac + done +} + # Function to validate AbuseIPDB API key validate_token() { local api_key=$1 @@ -114,10 +193,10 @@ validate_token() { fi if [[ $response -eq 200 ]]; then - echo "βœ… Yay! Token is valid." + echo "βœ… API token validated successfully." return 0 else - echo "❌ Invalid token! Please try again." + echo "❌ Invalid token. Please try again." return 1 fi } @@ -147,43 +226,6 @@ else echo "βœ… UFW logging is already enabled" fi -# Check for UFW log file -if [[ ! -f /var/log/ufw.log ]]; then - read -r -p "πŸ” /var/log/ufw.log not found. Please enter the path to your log file: " ufw_log_path - if [[ -f $ufw_log_path ]]; then - echo "βœ… Log file found at $ufw_log_path" - else - echo "❌ Provided log file path does not exist. Exiting..." - exit 1 - fi -else - ufw_log_path="/var/log/ufw.log" - echo "βœ… /var/log/ufw.log exists" -fi - -# Prompt for API token -while true; do - read -r -p "πŸ”‘ Please enter your AbuseIPDB API token: " api_token - if validate_token "$api_token"; then - break - fi - continue -done - -# Prompt for server ID -while true; do - read -r -p "πŸ–₯️ Enter the server ID (e.g., homeserver1). Leave blank if you do not wish to provide one: " server_id - if [[ -z $server_id ]]; then - server_id=null - break - elif [[ $server_id =~ ^[A-Za-z0-9]{1,16}$ ]]; then - server_id="'$server_id'" - break - else - echo "❌ It must be 1-16 characters long, contain only letters and numbers, and have no spaces or special characters." - fi -done - # Prompt for system update and upgrade if yes_no_prompt "πŸ› οΈ Do you want the script to run apt update and apt upgrade for you?"; then echo "πŸ”§ Updating and upgrading the system..." @@ -191,26 +233,38 @@ if yes_no_prompt "πŸ› οΈ Do you want the script to run apt update and apt upgra fi # Clone repository & set permissions -if [ ! -d "/opt" ]; then - mkdir -p /opt - echo "πŸ“‚ '/opt' has been created" -else - echo "βœ… '/opt' directory already exists" -fi - +echo "πŸ“‚ Ensuring /opt directory exists..." +mkdir -p /opt cd /opt || { echo "❌ Failed to change directory to '/opt'. Exiting..."; exit 1; } -if [ ! -d "UFW-AbuseIPDB-Reporter" ]; then - echo "πŸ“₯ Cloning the UFW-AbuseIPDB-Reporter repository..." - sudo git clone https://github.com/sefinek/UFW-AbuseIPDB-Reporter.git --recurse-submodules || { echo "❌ Failed to clone the repository. Exiting..."; exit 1; } -else - echo "✨ The UFW-AbuseIPDB-Reporter repository already exists" +# Migrate from old directory name if exists +if [ -d "UFW-AbuseIPDB-Reporter" ] && [ ! -d "ufw-abuseipdb-reporter" ]; then + echo "πŸ“¦ Migrating from old directory name (UFW-AbuseIPDB-Reporter β†’ ufw-abuseipdb-reporter)..." + + # Stop UFW-AbuseIPDB reporter process if running + if command -v pm2 &>/dev/null; then + if pm2 list | grep -q "ufw-abuseipdb"; then + echo "⏸️ Stopping ufw-abuseipdb process..." + pm2 stop ufw-abuseipdb &>/dev/null || true + pm2 delete ufw-abuseipdb &>/dev/null || true + fi + fi + + sudo mv UFW-AbuseIPDB-Reporter ufw-abuseipdb-reporter + echo "βœ… Migration completed" fi -sudo chown "$USER":"$USER" /opt/UFW-AbuseIPDB-Reporter -R +if [ ! -d "ufw-abuseipdb-reporter" ]; then + echo "πŸ“₯ Cloning the repository..." + sudo git clone https://github.com/sefinek/UFW-AbuseIPDB-Reporter.git ufw-abuseipdb-reporter --recurse-submodules || { echo "❌ Failed to clone the repository. Exiting..."; exit 1; } +else + echo "βœ… The repository already exists" +fi + +sudo chown "$USER":"$USER" /opt/ufw-abuseipdb-reporter -R echo "πŸ“₯ Pulling latest changes..." -cd UFW-AbuseIPDB-Reporter || { echo "❌ Failed to change directory to 'UFW-AbuseIPDB-Reporter'. Exiting..."; exit 1; } +cd ufw-abuseipdb-reporter || { echo "❌ Failed to change directory to 'ufw-abuseipdb-reporter'. Exiting..."; exit 1; } git pull || { echo "❌ Failed to pull the latest changes. Exiting..."; exit 1; } # Install npm dependencies @@ -225,34 +279,202 @@ else cp config.default.js config.js fi -# Update config.js with API token, Server ID, and UFW log path -config_file="config.js" -if [[ -f $config_file ]]; then - echo "πŸ”§ Updating $PWD/$config_file..." - sed -i "s|UFW_LOG_FILE: .*|UFW_LOG_FILE: '$ufw_log_path',|" "$config_file" - sed -i "s|SERVER_ID: .*|SERVER_ID: $server_id,|" "$config_file" - sed -i "s|ABUSEIPDB_API_KEY: .*|ABUSEIPDB_API_KEY: '$api_token',|" "$config_file" +# Check for UFW log file +existing_log_path=$(extract_config_value "UFW_LOG_FILE") +default_log_path="${existing_log_path:-/var/log/ufw.log}" + +if [[ -f $default_log_path ]]; then + echo "βœ… $default_log_path exists" + ufw_log_path="$default_log_path" else - echo "❌ $config_file not found. Make sure the repository was cloned and initialized correctly." + while true; do + read -e -i "$default_log_path" -r -p "> πŸ” Please enter the path to your UFW log file: " ufw_log_path + + if [[ -f $ufw_log_path ]]; then + echo "βœ… Log file found at $ufw_log_path" + break + else + echo "❌ Provided log file path does not exist. Please try again." + fi + done +fi + +echo -e "\n==============================================================================================\n" + +# Prompt for API token +existing_api_token=$(extract_config_value "ABUSEIPDB_API_KEY") + +while true; do + read -e -i "$existing_api_token" -r -p "> πŸ”‘ Please enter your AbuseIPDB API token: " api_token + + if [[ -z $api_token ]]; then + echo "❌ API token cannot be empty. Please try again." + continue + fi + + if validate_token "$api_token"; then + break + fi +done + +# Prompt for server ID +echo "" +echo " Server ID identifies this machine in AbuseIPDB reports and Discord notifications." +echo " Examples: 'homeserver1', 'vps-de-01', 'production'. Leave blank if not needed." +existing_server_id=$(extract_config_value "SERVER_ID") + +while true; do + read -e -i "$existing_server_id" -r -p "> πŸ–₯️ Enter the server ID (1-16 characters, or leave blank): " server_id + + if [[ -z $server_id ]]; then + server_id=null + break + elif [[ $server_id =~ ^[A-Za-z0-9]{1,16}$ ]]; then + server_id="'$server_id'" + break + else + echo "❌ Must be 1-16 characters long, only letters and numbers (a-z, A-Z, 0-9)." + fi +done + +# Extended logs +echo -e "\n Extended logs show additional debugging information in console output." +existing_extended_logs=$(extract_config_value "EXTENDED_LOGS") +extended_logs=$(ask_config "> πŸ“Š Enable extended logs? (true/false): " "${existing_extended_logs:-false}" "bool") + +# Cache file path +echo -e "\n Cache file stores reported IPs to prevent duplicate reports within cooldown period." +existing_cache_file=$(extract_config_value "CACHE_FILE") +cache_file=$(ask_config "> πŸ’Ύ Cache file path: " "${existing_cache_file:-./tmp/ufw-abuseipdb-reporter.cache}" "") + +# IP assignment type +echo -e "\n IP assignment: 'dynamic' if your ISP changes your IP periodically, 'static' if it never changes." +echo " Most home/small business connections are 'dynamic'. VPS/dedicated servers are usually 'static'." +existing_ip_assignment=$(extract_config_value "IP_ASSIGNMENT") +ip_assignment=$(ask_config "> 🌐 IP assignment type (static/dynamic): " "${existing_ip_assignment:-dynamic}" "static_dynamic") + +# IP refresh schedule (only if dynamic) +if [[ $ip_assignment == "dynamic" ]]; then + echo -e "\n How often to check your public IP to prevent reporting yourself (cron format)." + echo " Default '0 */6 * * *' = every 6 hours. Format: minute hour day month weekday" + existing_ip_refresh=$(extract_config_value "IP_REFRESH_SCHEDULE") + ip_refresh_schedule=$(ask_config "> ⏰ IP refresh schedule (cron): " "${existing_ip_refresh:-0 */6 * * *}" "cron") +else + ip_refresh_schedule="0 */6 * * *" +fi + +# IPv6 support +echo -e "\n Enable if your server has a public IPv6 address assigned by your ISP." +echo " Check with: ip -6 addr | grep 'scope global'" +existing_ipv6=$(extract_config_value "IPv6_SUPPORT") +ipv6_support=$(ask_config "> πŸ”’ Enable IPv6 support? (true/false): " "${existing_ipv6:-false}" "bool") + +# IP report cooldown +echo -e "\n Minimum time (in hours) before reporting the same IP again." +echo " Recommended: 12 hours. Minimum: 0.25 (15 minutes) due to AbuseIPDB rate limits." +existing_cooldown=$(extract_config_value "IP_REPORT_COOLDOWN") +default_cooldown_hours="12" +if [[ -n $existing_cooldown ]]; then + default_cooldown_hours=$(awk "BEGIN {printf \"%.2f\", $existing_cooldown / 3600000}") +fi + +while true; do + cooldown_hours=$(ask_config "> ⏱️ IP report cooldown (hours): " "$default_cooldown_hours" "number") + # Check if >= 0.25 (15 minutes) + if awk "BEGIN {exit !($cooldown_hours >= 0.25)}"; then + break + else + echo "❌ Minimum cooldown is 0.25 hours (15 minutes)" + fi +done + +ip_report_cooldown=$(awk "BEGIN {printf \"%.0f\", $cooldown_hours * 3600000}") + +# Auto-update +echo -e "\n Automatically update script via 'git pull' on schedule. May cause issues with breaking changes." +existing_auto_update=$(extract_config_value "AUTO_UPDATE_ENABLED") +auto_update_enabled=$(ask_config "> πŸ”„ Enable automatic updates? (true/false): " "${existing_auto_update:-false}" "bool") + +# Auto-update schedule (only if enabled) +if [[ $auto_update_enabled == "true" ]]; then + echo -e "\n When to check for updates (cron format)." + echo " Default '0 14,16,20 * * *' = daily at 14:00, 16:00, 20:00" + existing_update_schedule=$(extract_config_value "AUTO_UPDATE_SCHEDULE") + auto_update_schedule=$(ask_config "> πŸ“… Auto-update schedule (cron): " "${existing_update_schedule:-0 14,16,20 * * *}" "cron") +else + auto_update_schedule="0 14,16,20 * * *" +fi + +# Discord webhook +echo -e "\n Receive Discord notifications about important events, daily summaries of blocked IPs, and errors." +existing_discord_enabled=$(extract_config_value "DISCORD_WEBHOOK_ENABLED") +discord_enabled=$(ask_config "> πŸ’¬ Enable Discord webhooks? (true/false): " "${existing_discord_enabled:-false}" "bool") + +# Discord webhook URL (only if enabled) +if [[ $discord_enabled == "true" ]]; then + echo -e "\n Edit Channel β†’ Integrations β†’ Create Webhook β†’ Click on created webhook β†’ Copy Webhook URL" + discord_url=$(ask_config "> πŸ”— Discord webhook URL: " "$(extract_config_value "DISCORD_WEBHOOK_URL")" "url") + + echo -e "\n Username shown in Discord messages. 'SERVER_ID' will use your server ID value." + existing_discord_username=$(extract_config_value "DISCORD_WEBHOOK_USERNAME") + discord_username=$(ask_config "> πŸ‘€ Discord webhook username: " "${existing_discord_username:-SERVER_ID}" "") + [[ -z $discord_username ]] && discord_username="SERVER_ID" + + echo -e "\n Your Discord user ID for @mentions in critical notifications." + echo " User Settings β†’ Advanced β†’ Developer Mode" + echo " Esc β†’ Right-click on your profile β†’ Copy User ID. Leave blank to skip." + discord_user_id=$(ask_config "> πŸ†” Discord user ID (optional): " "$(extract_config_value "DISCORD_USER_ID")" "") +else + discord_url="" + discord_username="SERVER_ID" + discord_user_id="" +fi + +echo -e "\n==============================================================================================\n" + +# Update config.js with all parameters +if [[ ! -f config.js ]]; then + echo "❌ config.js not found. Installation may have failed." exit 1 fi +echo "πŸ”§ Updating $PWD/config.js..." +sed -i \ + -e "s|UFW_LOG_FILE: .*|UFW_LOG_FILE: '$ufw_log_path',|" \ + -e "s|SERVER_ID: .*|SERVER_ID: $server_id,|" \ + -e "s|EXTENDED_LOGS: .*|EXTENDED_LOGS: $extended_logs,|" \ + -e "s|CACHE_FILE: .*|CACHE_FILE: '$cache_file',|" \ + -e "s|IP_ASSIGNMENT: .*|IP_ASSIGNMENT: '$ip_assignment',|" \ + -e "s|IP_REFRESH_SCHEDULE: .*|IP_REFRESH_SCHEDULE: '$ip_refresh_schedule',|" \ + -e "s|IPv6_SUPPORT: .*|IPv6_SUPPORT: $ipv6_support,|" \ + -e "s|ABUSEIPDB_API_KEY: .*|ABUSEIPDB_API_KEY: '$api_token',|" \ + -e "s|IP_REPORT_COOLDOWN: .*|IP_REPORT_COOLDOWN: $ip_report_cooldown,|" \ + -e "s|AUTO_UPDATE_ENABLED: .*|AUTO_UPDATE_ENABLED: $auto_update_enabled,|" \ + -e "s|AUTO_UPDATE_SCHEDULE: .*|AUTO_UPDATE_SCHEDULE: '$auto_update_schedule',|" \ + -e "s|DISCORD_WEBHOOK_ENABLED: .*|DISCORD_WEBHOOK_ENABLED: $discord_enabled,|" \ + -e "s|DISCORD_WEBHOOK_URL: .*|DISCORD_WEBHOOK_URL: '$discord_url',|" \ + -e "s|DISCORD_WEBHOOK_USERNAME: .*|DISCORD_WEBHOOK_USERNAME: '$discord_username',|" \ + -e "s|DISCORD_USER_ID: .*|DISCORD_USER_ID: '$discord_user_id',|" \ + config.js + # Create directories & set permissions echo "πŸ“‚ Creating directories and setting permissions..." sudo mkdir -p /var/log/ufw-abuseipdb sudo chown -R "$USER":"$USER" /var/log/ufw-abuseipdb # Change permissions for UFW log file -echo "πŸ”’ Changing permissions for $ufw_log_path..." -sudo chown syslog:"$USER" "$ufw_log_path" -sudo chmod 640 "$ufw_log_path" - +if [[ -f $ufw_log_path ]]; then + echo "πŸ”’ Changing permissions for $ufw_log_path..." + sudo chown syslog:"$USER" "$ufw_log_path" + sudo chmod 640 "$ufw_log_path" +else + echo "⚠️ Warning: UFW log file not found at $ufw_log_path. Permissions not changed." +fi # Install PM2 echo "πŸ“¦ Installing PM2..." sudo npm install pm2@latest -g --silent - # Configure PM2 echo "βš™οΈ Adding PM2 to autostart..." startup_command=$(pm2 startup | grep "sudo env PATH" | sed 's/^[^s]*sudo/sudo/') @@ -270,7 +492,6 @@ echo "βš™οΈ Running a script with PM2 and saving the current state of all proce pm2 start --silent pm2 save --silent - # Final echo "🌌 Checking PM2 status..." pm2 status @@ -279,11 +500,17 @@ echo -e "\nπŸŽ‰ Installation and configuration completed! Use the 'pm2 logs' com echo -e "βš™οΈ More settings can be found in the file: $PWD/config.js" echo -e "\n====================================== Summary ======================================" -echo "πŸ–₯️ Server ID : ${server_id:-null}" -echo "πŸ”‘ API Key : $api_token" -echo "πŸ“‚ Script : $PWD" - -echo -e "\n====================================== Support ======================================" -echo "πŸ“© Email : contact@sefinek.net" -echo "πŸ”΅ Discord : https://discord.gg/RVH8UXgmzs" -echo "😺 GitHub Issues : https://github.com/sefinek/UFW-AbuseIPDB-Reporter/issues" +echo "πŸ–₯️ Server ID : ${server_id//\'/}" +if [[ ${#api_token} -gt 24 ]]; then + echo "πŸ”‘ API Key : ${api_token:0:20}...${api_token: -4}" +else + echo "πŸ”‘ API Key : ${api_token:0:8}...${api_token: -4}" +fi +echo "πŸ“‚ UFW Log File : $ufw_log_path" +echo "πŸ’Ύ Cache File : $cache_file" +echo "🌐 IP Assignment : $ip_assignment" +echo "πŸ”’ IPv6 Support : $ipv6_support" +echo "⏱️ Report Cooldown : $cooldown_hours hours" +echo "πŸ”„ Auto-Updates : $auto_update_enabled" +echo "πŸ’¬ Discord Webhooks : $discord_enabled" +echo "πŸ“‚ Script Directory : $PWD"