I am writing a bash script that checks every 5 minutes if there is from an IP address 100 or more invalid passwords (brute force attack) attempts.
The following script works:
blockIPs="$(cat /var/log/secure | grep "Failed password for" | grep -v "invalid" | awk {'print $11'} | sort | uniq -c | awk -v limit=100 '$1 > limit{print $2}')"
while read -r line; do
iptables -A INPUT -s $line -p tcp --dport 22 -j DROP
echo "Blocking IP address: $line"
done <<< "$blockIPs"
The problem with the script above is that after an hour I have duplicate entries in iptables. So I tried to extend my script with a check if the IP address is already blocked, if so, it should skip it.
This is the script:
blockIPs="$(cat /var/log/secure | grep "Failed password for" | grep -v "invalid" | awk {'print $11'} | sort | uniq -c | awk -v limit=100 '$1 > limit{print $2}')"
currentIPs="$(iptables-save)"
while read -r line; do
if grep -q $line $currentIPs
then
echo "IP address already blocked, skipping"
else
iptables -A INPUT -s $line -p tcp --dport 22 -j DROP
echo "Blocking IP address: $line"
fi
done <<< "$blockIPs"
But for some reasons it is not working and I am getting weird output:
grep: 2: No such file or directory
grep: 18:19:53: No such file or directory
grep: 2015: No such file or directory
Blocking IP address: 59.47.0.152
grep: #: No such file or directory
grep: Generated: No such file or directory
grep: by: No such file or directory
grep: iptables-save: No such file or directory
What is wrong with my script?
What you're basically doing is:
grep -q test this is a string that contains the word test
hoping to match a word in a string. Grep thinks each of the words is a file, and gives output like you're seeing:
grep: this: No such file or directory
grep: is: No such file or directory
grep: a: No such file or directory
grep: string: No such file or directory
To match in a literal string instead of files, send the string on stdin:
if grep -q "$line" <<< "$currentIPs"
Though you would be better off using glob matching:
if [[ "$currentIPs" = *"$line"* ]]
Note that if you've banned 1.2.3.45, 1.2.3.4 will match and therefore not get banned. You can use the above approach with *" $line "* to ensure there are spaces around it, if your input has that.
Also consider just installing fail2ban which does this automatically in a robust way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With