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
Your code has many issues:
awk
for every line selected (awk
is not needed at all);"$BADWORDS"
)tail -F
) and so the iptables
loop never startsiptables
command appends a new rule even if the IP has been seen beforei<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.