Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash check if grep matches string

Tags:

grep

bash

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?

like image 798
Sinan Avatar asked Oct 22 '25 03:10

Sinan


1 Answers

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.

like image 72
that other guy Avatar answered Oct 23 '25 18:10

that other guy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!