Search code examples
bashloopswhile-loopblockreadfile

Realtime monitoring, IP cannot be blocked


I would like to block IP address from a log file, IPs are collected from access.log file in apache2. The IPs are correctly collected in file ips.log, but while reading the file to ban collected IPs, the block is not done.

 #!/bin/bash

 # Store words to avoid an search for
 BADWORDS=( '/etc/passwd' 'file?' 'w00tw00t' 'fckeditor' 'ifconfig' )
 # Get number of elements in the backup_files array
 entries=${#BADWORDS[@]}

 for ((i=0; i<= entries-1; i++))
 do
 setBadWord=${BADWORDS[$i]}

 tail -F /var/log/apache2/access.log | grep --line-buffered "$setBadWord" | while read -r a; do echo "$a" | awk '{ print $1 } ' >> ips.log; done

 done # end for


while IFS= read -r ip; do
iptables -A INPUT -s "$ip" -j DROP
done < ips.log

Solution

  • Your code has many issues:

    • it runs a new copy of awk for every line selected (awk is not needed at all);
    • it tries to run the first loop multiple times (once for every element of "$BADWORDS")
    • the first loop never finishes (because of tail -F) and so the iptables loop never starts
    • the iptables command appends a new rule even if the IP has been seen before
    • it is simpler to write i<entries rather than i<=entries-1, and even simpler to just use for setBadword in "${BADWORDS[@]}"; do ...

    If you really want to permanently loop reading the logfile, with GNU utilities you can do something like:

    #!/bin/sh
    
    log=/var/log/apache2/access.log
    words=/my/list/of/badwords/one/per/line
    banned=/my/list/of/already/banned/ips
    
    tail -F "$log" |\
    grep --line-buffered -Ff "$words" |\
    while read ip junk; do
        grep -qxF $ip "$banned" || (
            iptables -A INPUT -s $ip -j DROP
            echo $ip >> "$banned"
        )
    done
    
    # we never get here because "tail -F" never finishes
    

    To just process the logfile once and then finish, you can feed grep from "$log" directly:

    grep --line-buffered -Ff "$words" "$log" | ...
    

    but it is probably less error-prone to just use fail2ban which is explicitly designed for this sort of task.