#!/bin/bash # Script: ip_block_analyzer.sh # Descripción: Analiza bloqueos del firewall CSF 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 # Función para ejecutar comandos con sudo si es necesario ejecutar_comando() { local comando="$1" if [ "$EUID" -eq 0 ]; then # Si ya somos root, ejecutar directamente $comando else # Si no somos root, usar sudo sudo $comando fi } # Función para leer archivos con sudo si es necesario leer_archivo() { local archivo="$1" if [ -r "$archivo" ]; then cat "$archivo" else ejecutar_comando "cat $archivo" fi } # Función modificada 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" ]; then # Usar nuestra función de lectura if leer_archivo "$archivo_estado" 2>/dev/null | grep -q "$IP"; 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 < <(leer_archivo "$archivo_estado" 2>/dev/null | grep "$IP") BLOCK_FOUND=true encontrado_en_estado=true fi 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 modificada para buscar en archivos de denegación buscar_archivos_denegacion_csf() { echo -e "${AMARILLO}Buscando archivos de denegación de CSF...${NC}" # Verificar lista de denegación permanente if leer_archivo "$CSF_DENY" 2>/dev/null | grep -q "$IP"; then echo -e "${ROJO}✓ Encontrado en lista de denegación permanente de CSF: $CSF_DENY${NC}" leer_archivo "$CSF_DENY" 2>/dev/null | grep "$IP" | while read -r linea; do echo -e " ${CYAN}Entrada: $linea${NC}" done BLOCK_FOUND=true fi # Verificar lista de denegación temporal if leer_archivo "$CSF_TEMP_DENY" 2>/dev/null | grep -q "$IP"; then echo -e "${ROJO}✓ Encontrado en lista de denegación temporal de CSF: $CSF_TEMP_DENY${NC}" leer_archivo "$CSF_TEMP_DENY" 2>/dev/null | grep "$IP" | while read -r linea; do echo -e " ${CYAN}Entrada: $linea${NC}" done BLOCK_FOUND=true fi } # Función modificada para opciones de desbloqueo opciones_desbloqueo() { echo echo -e "${AMARILLO}Opciones de Desbloqueo:${NC}" echo "1) Desbloqueo permanente (remover de todas las listas de CSF)" echo "2) Desbloqueo temporal (permitir por 1 hora)" echo "3) Salir sin desbloquear" read -p "Seleccione opción [1-3]: " opcion case $opcion in 1) echo -e "${AMARILLO}Removiendo bloqueo permanente para $IP...${NC}" ejecutar_comando "csf -dr $IP" if [ $? -eq 0 ]; then echo -e "${VERDE}✓ IP $IP desbloqueada permanentemente${NC}" else echo -e "${ROJO}✗ Error al desbloquear $IP${NC}" fi ;; 2) echo -e "${AMARILLO}Agregando permiso temporal por 1 hora para $IP...${NC}" ejecutar_comando "csf -ta $IP 1h" if [ $? -eq 0 ]; then echo -e "${VERDE}✓ IP $IP permitida temporalmente por 1 hora${NC}" else echo -e "${ROJO}✗ Error al agregar permiso temporal para $IP${NC}" fi ;; 3) echo -e "${AZUL}Saliendo sin cambios.${NC}" ;; *) echo -e "${ROJO}Selección inválida. Saliendo.${NC}" ;; esac } # 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 # 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 Bloqueo de IP: $IP ===${NC}" echo # ============================================= # FUNCIONES PARA BUSCAR EN LOGS ESPECÍFICOS # ============================================= # Función para buscar en logs de cPanel buscar_logs_cpanel() { local ip="$1" local log_cpanel="/usr/local/cpanel/logs/access_log" local eventos="" echo -e " ${CYAN}Buscando en logs de cPanel...${NC}" if [ -f "$log_cpanel" ]; then eventos=$(grep "$ip" "$log_cpanel" 2>/dev/null | tail -n 10) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de cPanel encontrados:${NC}" echo # Mostrar eventos más relevantes (últimos 5) echo "$eventos" | tail -n 5 | while IFS= read -r linea; do if [ -n "$linea" ]; then echo -e " ${CYAN} - $linea${NC}" fi done # Resumen de intentos de login fallidos echo echo -e " ${AMARILLO}Resumen de intentos de login:${NC}" grep "$ip" "$log_cpanel" 2>/dev/null | grep -i "login" | grep -o "user=[^ ]*" | sort | uniq -c | while read -r resumen; do echo -e " ${CYAN} - $resumen intentos${NC}" done else echo -e " ${AMARILLO}No se encontraron eventos de cPanel para $ip${NC}" fi else echo -e " ${AMARILLO}Archivo de log no encontrado: $log_cpanel${NC}" fi echo } # Función para buscar en logs de FTP buscar_logs_ftp() { local ip="$1" local log_ftp="/var/log/messages" local eventos="" echo -e " ${CYAN}Buscando en logs de FTP...${NC}" if [ -f "$log_ftp" ]; then eventos=$(grep "$ip" "$log_ftp" 2>/dev/null | grep -i "ftpd\|proftpd\|pure-ftpd" | tail -n 5) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de FTP encontrados:${NC}" echo while IFS= read -r linea; do if [ -n "$linea" ]; then echo -e " ${CYAN} - $linea${NC}" fi done <<< "$eventos" else echo -e " ${AMARILLO}No se encontraron eventos de FTP para $ip${NC}" fi else echo -e " ${AMARILLO}Archivo de log no encontrado: $log_ftp${NC}" # Buscar en ubicaciones alternativas local logs_alternativos=("/var/log/secure" "/var/log/auth.log") for log_alt in "${logs_alternativos[@]}"; do if [ -f "$log_alt" ]; then echo -e " ${CYAN}Verificando ubicación alternativa: $log_alt${NC}" grep "$ip" "$log_alt" | grep -i "ftp" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done fi done fi echo } # Función para buscar en logs de Exim buscar_logs_exim() { local ip="$1" local log_exim="/var/log/exim_mainlog" local eventos="" echo -e " ${CYAN}Buscando en logs de Exim...${NC}" if [ -f "$log_exim" ]; then eventos=$(grep "$ip" "$log_exim" 2>/dev/null | tail -n 10) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de Exim encontrados:${NC}" echo # Mostrar eventos más relevantes (últimos 5) echo "$eventos" | tail -n 5 | while IFS= read -r linea; do if [ -n "$linea" ]; then echo -e " ${CYAN} - $linea${NC}" fi done # Resumen de tipos de errores echo echo -e " ${AMARILLO}Resumen de actividad Exim:${NC}" grep "$ip" "$log_exim" 2>/dev/null | grep -o "=[A-Z]* " | sort | uniq -c | while read -r resumen; do echo -e " ${CYAN} - $resumen${NC}" done else echo -e " ${AMARILLO}No se encontraron eventos de Exim para $ip${NC}" fi else echo -e " ${AMARILLO}Archivo de log no encontrado: $log_exim${NC}" fi echo } # Función para buscar en logs de SSH buscar_logs_ssh() { local ip="$1" local log_ssh="/var/log/secure" local eventos="" echo -e " ${CYAN}Buscando en logs de SSH...${NC}" if [ -f "$log_ssh" ]; then eventos=$(grep "$ip" "$log_ssh" 2>/dev/null | grep -i "sshd.*Failed" | tail -n 5) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de SSH encontrados:${NC}" echo while IFS= read -r linea; do if [ -n "$linea" ]; then # Extraer información relevante 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/.*sshd//i') if [ -n "$timestamp" ]; then echo -e " ${CYAN}[$timestamp] $mensaje${NC}" else echo -e " ${CYAN}$linea${NC}" fi fi done <<< "$eventos" else echo -e " ${AMARILLO}No se encontraron eventos de SSH para $ip${NC}" echo -e " ${CYAN}Búsqueda alternativa en $log_ssh:${NC}" grep "$ip" "$log_ssh" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done fi else echo -e " ${AMARILLO}Archivo de log no encontrado: $log_ssh${NC}" # Buscar en ubicaciones alternativas local log_ssh_alt="/var/log/auth.log" if [ -f "$log_ssh_alt" ]; then echo -e " ${CYAN}Verificando ubicación alternativa: $log_ssh_alt${NC}" grep "$ip" "$log_ssh_alt" | grep -i "ssh" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done fi 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}" # Buscar eventos específicos de mod_security para esta IP 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 # Extraer información relevante y mostrar de forma legible 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 buscar en logs de Dovecot buscar_logs_dovecot() { local ip="$1" local log_dovecot="/var/log/maillog" local eventos="" echo -e " ${CYAN}Buscando en logs de Dovecot...${NC}" if [ -f "$log_dovecot" ]; then # Buscar eventos específicos de Dovecot para esta IP eventos=$(grep "$ip" "$log_dovecot" 2>/dev/null | grep -i "dovecot.*auth.*fail\|dovecot.*Login failed\|imap.*authentication failed\|pop3.*authentication failed" | tail -n 5) if [ -n "$eventos" ]; then echo -e " ${ROJO}✓ Eventos de autenticación Dovecot encontrados:${NC}" echo while IFS= read -r linea; do if [ -n "$linea" ]; then # Extraer información relevante y mostrar de forma legible 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/.*dovecot://i' | sed 's/.*imap://i' | sed 's/.*pop3://i') if [ -n "$timestamp" ]; then echo -e " ${CYAN}[$timestamp] $mensaje${NC}" else echo -e " ${CYAN}$linea${NC}" fi fi done <<< "$eventos" # Mostrar resumen de usuarios afectados echo echo -e " ${AMARILLO}Resumen de usuarios con intentos fallidos:${NC}" grep "$ip" "$log_dovecot" 2>/dev/null | grep -i "auth.*fail" | grep -o "user=.*," | sort | uniq -c | while read -r resumen; do echo -e " ${CYAN} - $resumen intentos${NC}" done else echo -e " ${AMARILLO}No se encontraron eventos de autenticación Dovecot para $ip${NC}" echo -e " ${CYAN}Búsqueda alternativa en $log_dovecot:${NC}" grep "$ip" "$log_dovecot" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done fi else echo -e " ${AMARILLO}Archivo de log no encontrado: $log_dovecot${NC}" # Buscar en ubicaciones alternativas local logs_alternativos=("/var/log/mail.log" "/var/log/dovecot.log") for log_alt in "${logs_alternativos[@]}"; do if [ -f "$log_alt" ]; then echo -e " ${CYAN}Verificando ubicación alternativa: $log_alt${NC}" grep "$ip" "$log_alt" | grep -i "dovecot\|imap\|pop3" | tail -n 3 | while read -r evento; do echo -e " ${CYAN} - $evento${NC}" done fi done fi echo } # Función para analizar bloqueos permanentes analizar_permblock() { local ip="$1" local razon="$2" echo -e " ${CYAN}Analizando bloqueo permanente por exceso de bloques temporales...${NC}" # Extraer el servicio del log de LFD local servicio=$(grep "PERMBLOCK.*$ip" /var/log/lfd.log 2>/dev/null | grep -o "\[LF_[A-Z]*\]" | tail -1) if [ -n "$servicio" ]; then echo -e " ${CYAN}Servicio identificado: $servicio${NC}" # Mostrar los últimos 5 bloques temporales relacionados echo -e " ${CYAN}Últimos bloques temporales para $ip:${NC}" grep "$ip" /var/log/lfd.log 2>/dev/null | grep -E "LF_[A-Z]+" | tail -5 | while read -r linea; do echo -e " ${CYAN} - $linea${NC}" done # Según el servicio, buscar en logs específicos case "$servicio" in "[LF_POP3D]"|"[LF_IMAPD]") buscar_logs_dovecot "$ip" ;; "[LF_SSHD]") buscar_logs_ssh "$ip" ;; "[LF_MODSEC]") buscar_logs_modsecurity "$ip" ;; "[LF_FTPD]") buscar_logs_ftp "$ip" ;; "[LF_EXIM]") buscar_logs_exim "$ip" ;; "[LF_CPANEL]") buscar_logs_cpanel "$ip" ;; *) echo -e " ${AMARILLO}No se tiene una búsqueda específica para $servicio${NC}" ;; esac else echo -e " ${AMARILLO}No se pudo identificar el servicio específico del bloqueo.${NC}" echo -e " ${CYAN}Revisando logs generales...${NC}" # Buscar en logs comunes como fallback buscar_logs_dovecot "$ip" buscar_logs_ssh "$ip" buscar_logs_modsecurity "$ip" fi } # ============================================= # FUNCIONES PRINCIPALES DE ANÁLISIS # ============================================= # 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}" echo -e " ${AMARILLO}Buscando eventos recientes relacionados...${NC}" # Determinar el tipo de bloqueo y buscar en los logs correspondientes razon_lower=$(echo "$razon" | tr '[:upper:]' '[:lower:]') case "$razon_lower" in *"cpanel"*) buscar_logs_cpanel "$IP" ;; *"mod_security"* | *"modsec"*) buscar_logs_modsecurity "$IP" ;; *"dovecot"* | *"imap"* | *"pop3"*) buscar_logs_dovecot "$IP" ;; *"exim"* | *"smtp"*) buscar_logs_exim "$IP" ;; *"ssh"*) buscar_logs_ssh "$IP" ;; *"ftp"*) buscar_logs_ftp "$IP" ;; *"permblock"*) analizar_permblock "$IP" "$razon" ;; *) echo -e " ${CYAN}No se identificó un servicio específico para búsqueda detallada.${NC}" ;; esac else echo -e " ${CYAN}Datos crudos: $linea${NC}" 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") # Omitir archivo dnscache como solicitó el usuario 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}" # Verificar lista de denegación permanente 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 # Verificar lista de denegación temporal 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 } # ============================================= # EJECUCIÓN PRINCIPAL # ============================================= # Ejecución principal buscar_archivos_estado_csf buscar_archivos_denegacion_csf buscar_logs_lfd 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 pudo haber expirado" exit 0 fi # Mostrar opciones de desbloqueo si se encontraron bloques opciones_desbloqueo echo echo -e "${AZUL}=== Análisis completado ===${NC}"