#!/bin/bash # Script: ip_block_analyzer.sh # Descripción: Analiza bloqueos del firewall CSF, cPHulk y provee opciones para desbloquear # Uso: ./ip_block_analyzer.sh # Códigos de color para la salida ROJO='\033[0;31m' VERDE='\033[0;32m' AMARILLO='\033[1;33m' AZUL='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Sin Color # Verificar si se proporcionó la dirección IP como argumento if [ $# -eq 0 ]; then echo -e "${ROJO}Error: No se proporcionó dirección IP.${NC}" echo "Uso: $0 " exit 1 fi IP="$1" CSF_DENY="/etc/csf/csf.deny" CSF_TEMP_DENY="/etc/csf/csf.tempban" CSF_STATE_DIR="/var/lib/csf/" LFD_LOG="/var/log/lfd.log" BLOCK_FOUND=false CPHULK_BLOCKED=false # Variable para rastrear el estado en cPanel # Validar formato de la IP if ! [[ $IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo -e "${ROJO}Error: Formato de dirección IP inválido.${NC}" exit 1 fi echo -e "${AZUL}=== Analizando IP: $IP ===${NC}" echo # ----------------------------------------------------- # NUEVO: Función para obtener Geolocalización # ----------------------------------------------------- obtener_geolocalizacion() { echo -e "${AMARILLO}Obteniendo origen geográfico de la IP...${NC}" if command -v curl &> /dev/null; then # ip-api.com permite consultas gratuitas sin API Key (límite razonable) local geo_info=$(curl -s --max-time 5 "http://ip-api.com/line/$IP?fields=status,country,city,isp") local status=$(echo "$geo_info" | sed -n '1p') if [ "$status" == "success" ]; then local pais=$(echo "$geo_info" | sed -n '2p') local ciudad=$(echo "$geo_info" | sed -n '3p') local isp=$(echo "$geo_info" | sed -n '4p') echo -e " ${CYAN}País: ${VERDE}$pais${NC}" echo -e " ${CYAN}Ciudad: ${VERDE}$ciudad${NC}" echo -e " ${CYAN}ISP: ${VERDE}$isp${NC}" else echo -e " ${ROJO}No se pudo obtener información geográfica (IP privada o no enrutable).${NC}" fi else echo -e " ${AMARILLO}Comando 'curl' no disponible para consultar geolocalización.${NC}" fi echo } # ----------------------------------------------------- # NUEVO: Función para verificar cPHulk # ----------------------------------------------------- buscar_bloqueos_cphulk() { echo -e "${AMARILLO}Buscando en bloqueos de cPHulkd...${NC}" local cphulk_script="/usr/local/cpanel/scripts/cphulkd_login_dbsetup" if [ -f "$cphulk_script" ]; then # Listar bloqueos activos y filtrar por la IP local bans=$("$cphulk_script" --list-bans 2>/dev/null | grep "$IP") if [ -n "$bans" ]; then echo -e "${ROJO}✓ Encontrado en bloqueos activos de cPHulk:${NC}" echo -e " ${CYAN}$bans${NC}" BLOCK_FOUND=true CPHULK_BLOCKED=true else echo -e " ${VERDE}No se encontraron bloqueos en cPHulk para esta IP.${NC}" fi else echo -e " ${AMARILLO}Script de cPHulk no encontrado (¿el servidor tiene cPanel instalado?).${NC}" fi echo } # Función mejorada para buscar en logs de mod_security buscar_logs_modsecurity() { local ip="$1" declare -a logs_modsec=( "/usr/local/apache/logs/error_log" "/var/log/apache2/error_log" "/var/log/httpd/error_log" "/usr/local/cpanel/logs/error_log" ) local encontrado=0 local eventos="" echo -e " ${CYAN}Buscando en logs de mod_security...${NC}" for log_modsec in "${logs_modsec[@]}"; do if [ -f "$log_modsec" ]; then echo -e " ${VERDE}Verificando archivo: $log_modsec${NC}" eventos=$(grep "$ip" "$log_modsec" 2>/dev/null | grep -i "mod_security\|ModSecurity" | tail -n 5) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de ModSecurity encontrados en $log_modsec:${NC}" echo while IFS= read -r linea; do if [ -n "$linea" ]; then local timestamp=$(echo "$linea" | grep -o '[A-Z][a-z][a-z] [0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]' | head -1) local mensaje=$(echo "$linea" | sed 's/.*ModSecurity:/ModSecurity:/' | cut -d']' -f2-) if [ -n "$timestamp" ]; then echo -e " ${CYAN}[$timestamp] $mensaje${NC}" else echo -e " ${CYAN}$linea${NC}" fi fi done <<< "$eventos" encontrado=1 break else echo -e " ${AMARILLO} No se encontraron eventos de ModSecurity en $log_modsec${NC}" fi else echo -e " ${AMARILLO} Archivo no encontrado: $log_modsec${NC}" fi done if [ $encontrado -eq 0 ]; then echo -e " ${ROJO}✗ No se encontraron eventos de ModSecurity en ninguna ubicación conocida${NC}" echo -e " ${AMARILLO}Posibles causas:" echo -e " - Los logs han sido rotados o limpiados" echo -e " - ModSecurity está logging en una ubicación personalizada" echo -e " - El bloqueo fue muy breve y no generó logs detallados${NC}" fi echo } # Función para extraer y mostrar la razón de csf.tempip mostrar_razon_tempip() { local archivo="$1" local linea="$2" IFS='|' read -ra campos <<< "$linea" if [ ${#campos[@]} -ge 4 ]; then local timestamp="${campos[2]}" local razon="${campos[3]}" local fecha_legible=$(date -d "@$timestamp" 2>/dev/null || echo "Fecha desconocida") echo -e " ${CYAN}Timestamp: $fecha_legible ($timestamp)${NC}" echo -e " ${CYAN}Razón: $razon${NC}" # CORRECCIÓN: Canalizamos la razón a la función unificada de tu script buscar_eventos_recientes "$razon" else echo -e " ${CYAN}Datos crudos: $linea${NC}" fi echo } # Función para buscar eventos recientes en logs específicos buscar_eventos_recientes() { local razon="$1" echo -e " ${AMARILLO}Buscando eventos recientes relacionados...${NC}" if [[ "$razon" == *"(cpanel)"* ]] || [[ "$razon" == *"cPanel"* ]]; then local archivo_log="/usr/local/cpanel/logs/access_log" local patron="$IP" local servicio="cPanel" elif [[ "$razon" == *"dovecot"* ]] || [[ "$razon" == *"imap"* ]] || [[ "$razon" == *"pop3"* ]]; then local archivo_log="/var/log/maillog" local patron="dovecot.*$IP.*authentication failed" local servicio="Dovecot (Email)" elif [[ "$razon" == *"exim"* ]] || [[ "$razon" == *"smtp"* ]]; then local archivo_log="/var/log/exim_mainlog" local patron="eximsyntax.*$IP" local servicio="Exim (SMTP)" elif [[ "$razon" == *"ssh"* ]] || [[ "$razon" == *"sshd"* ]]; then local archivo_log="/var/log/auth.log" local patron="sshd.*$IP.*Failed" local servicio="SSH" elif [[ "$razon" == *"ftp"* ]]; then local archivo_log="/var/log/messages" local patron="ftpd.*$IP.*fail" local servicio="FTP" else # Si es mod_security, llamamos a la función dedicada if [[ "$razon" == *"mod_security"* ]] || [[ "$razon" == *"modsec"* ]]; then buscar_logs_modsecurity "$IP" else echo -e " ${CYAN}No se identificó un servicio logueable para búsqueda general.${NC}" fi return fi if [ -f "$archivo_log" ]; then echo -e " ${CYAN}Últimos eventos de $servicio para $IP:${NC}" grep "$IP" "$archivo_log" | tail -n 5 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done else echo -e " ${AMARILLO}Archivo de log no encontrado: $archivo_log${NC}" local logs_alternativos=("/var/log/auth.log" "/var/log/secure" "/var/log/messages" "/var/log/syslog") for log_alt in "${logs_alternativos[@]}"; do if [ -f "$log_alt" ] && grep -q "$IP" "$log_alt" 2>/dev/null; then echo -e " ${CYAN}Eventos en $log_alt:${NC}" grep "$IP" "$log_alt" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done break fi done fi echo } # Función para buscar en archivos de estado de CSF buscar_archivos_estado_csf() { echo -e "${AMARILLO}Buscando en archivos de estado de CSF en $CSF_STATE_DIR...${NC}" local encontrado_en_estado=false for archivo_estado in "$CSF_STATE_DIR"csf.*; do if [ -f "$archivo_estado" ] && grep -q "$IP" "$archivo_estado" 2>/dev/null; then local nombre_archivo=$(basename "$archivo_estado") if [[ "$nombre_archivo" == "csf.dnscache" ]]; then echo -e "${VERDE}↳ Encontrado en $nombre_archivo (caché DNS, puede ignorarse)${NC}" continue fi echo -e "${ROJO}✓ Encontrado en estado de CSF: $nombre_archivo${NC}" while IFS= read -r linea; do if [[ "$linea" == *"$IP"* ]]; then if [[ "$nombre_archivo" == "csf.tempip" ]]; then mostrar_razon_tempip "$archivo_estado" "$linea" else echo -e " ${CYAN}Entrada: $linea${NC}" fi fi done < <(grep "$IP" "$archivo_estado") BLOCK_FOUND=true encontrado_en_estado=true fi done if [ "$encontrado_en_estado" = false ]; then echo -e "${VERDE}No se encontraron entradas en archivos de estado de CSF.${NC}" fi } # Función para buscar en archivos de denegación de CSF buscar_archivos_denegacion_csf() { echo -e "${AMARILLO}Buscando archivos de denegación de CSF...${NC}" if grep -q "$IP" "$CSF_DENY" 2>/dev/null; then echo -e "${ROJO}✓ Encontrado en lista de denegación permanente de CSF: $CSF_DENY${NC}" grep "$IP" "$CSF_DENY" | while read -r linea; do echo -e " ${CYAN}Entrada: $linea${NC}" done BLOCK_FOUND=true fi if grep -q "$IP" "$CSF_TEMP_DENY" 2>/dev/null; then echo -e "${ROJO}✓ Encontrado en lista de denegación temporal de CSF: $CSF_TEMP_DENY${NC}" grep "$IP" "$CSF_TEMP_DENY" | while read -r linea; do echo -e " ${CYAN}Entrada: $linea${NC}" done BLOCK_FOUND=true fi } # Función para buscar en logs de LFD buscar_logs_lfd() { echo -e "${AMARILLO}Buscando en logs de LFD...${NC}" if [ -f "$LFD_LOG" ]; then if grep -q "$IP" "$LFD_LOG" 2>/dev/null; then echo -e "${ROJO}✓ Encontrado en log de LFD: $LFD_LOG${NC}" grep "$IP" "$LFD_LOG" | tail -n 3 | while read -r linea; do echo -e " ${CYAN}Entrada de log: $linea${NC}" done BLOCK_FOUND=true else echo -e "${VERDE}No se encontraron entradas recientes en log de LFD.${NC}" fi else echo -e "${AMARILLO}Archivo de log LFD no encontrado en $LFD_LOG${NC}" fi } # Función para ofrecer opciones de desbloqueo opciones_desbloqueo() { echo echo -e "${AMARILLO}Opciones de Desbloqueo:${NC}" echo "1) Desbloqueo permanente (remover de todas las listas de CSF y cPHulk)" echo "2) Desbloqueo temporal (permitir por 1 hora en CSF)" echo "3) Salir sin desbloquear" read -p "Seleccione opción [1-3]: " opcion case $opcion in 1) echo -e "${AMARILLO}Removiendo bloqueo permanente en CSF para $IP...${NC}" csf -dr "$IP" 2>/dev/null if [ $? -eq 0 ]; then echo -e "${VERDE}✓ IP $IP desbloqueada permanentemente en CSF${NC}" else echo -e "${ROJO}✗ Error al desbloquear en CSF - puede requerir privilegios de root${NC}" fi # Limpiar bloqueo en cPHulk si corresponde if [ "$CPHULK_BLOCKED" = true ]; then echo -e "${AMARILLO}Limpiando historial de bloqueos en cPHulk para $IP...${NC}" if command -v whmapi1 &> /dev/null; then whmapi1 flush_cphulk_login_history_for_ips ip="$IP" 2>/dev/null > /dev/null echo -e "${VERDE}✓ IP $IP purgada de la base de datos de cPHulk${NC}" else echo -e "${ROJO}✗ Comando whmapi1 no encontrado para desbloquear cPHulk${NC}" fi fi ;; 2) echo -e "${AMARILLO}Agregando permiso temporal por 1 hora en CSF para $IP...${NC}" csf -ta "$IP" 1h 2>/dev/null if [ $? -eq 0 ]; then echo -e "${VERDE}✓ IP $IP permitida temporalmente por 1 hora${NC}" else echo -e "${ROJO}✗ Error al agregar permiso temporal en CSF - puede requerir privilegios de root${NC}" fi ;; 3) echo -e "${AZUL}Saliendo sin cambios.${NC}" ;; *) echo -e "${ROJO}Selección inválida. Saliendo.${NC}" ;; esac } # Ejecución principal obtener_geolocalizacion buscar_archivos_estado_csf buscar_archivos_denegacion_csf buscar_logs_lfd buscar_bloqueos_cphulk if [ "$BLOCK_FOUND" = false ]; then echo -e "${VERDE}No se encontraron bloqueos activos para IP: $IP${NC}" echo "Nota: La IP pudo haber sido desbloqueada automáticamente o el bloqueo expiró." exit 0 fi # Mostrar opciones de desbloqueo si se encontraron bloques opciones_desbloqueo echo echo -e "${AZUL}=== Análisis completado ===${NC}"