Firewallscript: Automatic detection and blocking of flooding attacks and port scans on (web) servers

Firewallscript: Automatic detection and blocking of flooding attacks and port scans on (web) servers

This article is a continuation of my previous article which also dealt with attacks on web servers. The following article shows how an attack can be detected and blocked – and that completely without paid tools.

The script does not only protect against flooding attacks but also against larger port scans due to the way it works.

However, in order not to hinder our important and genuine visitors, we additionally perform a check from which country our visitors actually come

Accordingly, attacks from Germany, for example, are intentionally not blocked in the default settings (see below). In contrast, users from more suspicious countries such as Russia are blocked faster than users from other countries.

Quick start – Ready-made firewall script from lautenbacher.io

All this is too complicated for you? You can also install the firewall script including cronjob with this script or book me via my contact form at reasonable hourly rates.

The install script also imports a CIDR blocklist and updates it daily (cronjob). The firewall script itself is executed every minute as a cronjob.

During the installation process you will be asked for your e-mail address. The email address will be used in the script to automatically send you a notification about an incident (if your server is configured to send email correctly)

The logfile will be deleted by cronjob at the beginning of the month.

Of course I do not take any warranty or liability for the following script. Please check the files yourself before each download and try the script on a testing machine before.

The script works beside Debian of course also on the Raspberry Pi and beside the Plesk Firewall.

In case of errors or problems please leave a comment.

apt-get update && apt-get install -y wget && wget -O /root/install.sh https://www.lautenbacher.io/firewallscript/install.sh && chmod 777 /root/install.sh && /root/install.sh

Tutorial

The packages iptables, netstat and geoip-bin are mandatory, they are installed with

apt-get update
apt-get install -y sudo
apt-get install -y geoip-bin
apt-get install -y iptables
apt-get install -y whois
apt-get install -y iptables-persistent
apt-get install -y net-tools #especially not installed on Raspbian

We create our test script test.sh as usual via

touch /root/test.sh
touch /root/test.log
touch /root/test.txt
chmod 777 /root/test.log
chmod 777 /root/test.txt
chmod 777 /root/test.sh
nano /root/test.sh

on. The script here mainly checks the number of established connections per IP address based on the output of netstat:

The shell script has the following content:

#!/bin/bash
echo Shellscript powered by lautenbacher.io
echo Block more than 200 Connections
netstat -ant | egrep '(:80|:443) .*:.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > test.txt
sed 's/^[ \t]*//' -i test.txt
sed '/^$/d' -i test.txt
while read a b; do
    if [[ $a > "200" ]]; then

bGF1dGVuYmFjaGVyLmlv=$(geoiplookup $b | awk -v ip="$b" '{FS=" "} {if($4 != "AT," && $4 != "US," && $4 != "DE,") {print "1";}}')

if [[ $bGF1dGVuYmFjaGVyLmlv = "1" ]]; then
echo checking ip $b
geoiplookup $b
echo add ip $b to iptables blocklist
sudo iptables -I INPUT -s $b -j DROP
#mail -s 'Alert Message regarding '$b [email protected] <<< $b' exceeded the connection limit of 200'
fi

  fi
done < test.txt

In this example all IPs with more than 200 connections which are not from Germany, Austria or the United States will be blocked automatically.

Of course this can also be turned around and we can create an additional script which will automatically block all connections from Russia, India and China as soon as there are more than 10 connections at the same time

The two scripts can of course be combined!

#!/bin/bash
echo "Shellscript powered by lautenbacher.io"
#bad countries RU CN IND IDN HKG TH VN UKR BLR VEN - limit 20
netstat -ant | egrep ':.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > testcc.txt
sed 's/^[ \t]*//' -i testcc.txt
sed '/^$/d' -i testcc.txt
while read c d; do
    if [[ $c > "20" ]]; then

bGF1dGVuYmFjaGVyLmlv=$(geoiplookup $d | awk -v ip="$d" '{FS=" "} {if($4 == "RU," || $4 == "CN," || $4 == "IND," || $4 == "IDN," || $4 == "HKG," || $4 == "TH," || $4 == "VN," || $4 == "UKR," || $4 == "BLR," || $4 == "VEN,") {print 1}}')

 if [[ $bGF1dGVuYmFjaGVyLmlv = "1" ]]; then
echo "running part 4"
bGF1dGVuYmFjaGVyLmlX=$(whois $d)
echo checking ip $d
geoiplookup $d

echo try to add ip $d to blocklist


whoisvar=0
if [[ "$bGF1dGVuYmFjaGVyLmlX" == *"CLOUDFLARE "* ]]; then
whoisvar=1
echo Cloudflare detected
fi

if [[ "$whoisvar" != 1 ]]; then
sudo iptables -I INPUT -s $d -j DROP
whois=$(whois $d)
hostvar=$(hostname)
mail -s 'Warning Message regarding '$d [email protected] <<< $d' bad country host exceeded the connection limit of 20'$whois
echo blocking $d
fi

 fi

   fi
done < testcc.txt

With our two shell scripts, we have already intelligently automated our firewall. Of course, the regular execution is still missing. The easiest way to do this is to create a cronjob with

crontab -e

and add the following content to the file:

*/1 * * * /root/test.sh >> /root/test.log 2>&1

In this example, the script inside the test.sh file is executed every minute. Accordingly, our script will check the connections for abnormalities every minute from now on and block them.

As an alternative to immediate blocking, you could also simply have attacker IPs sent to you via email:

mail -s 'Warning Message regarding '$b [email protected] <<< $b' exceeded the connection limit of 100'

This is especially a good idea to test the settings without blocking the IP immediately.

Final version - part 1: Connection flooding detection with analysis by country code and whois data

Attached is a more comprehensive combined version of the script, which also tries to exclude Cloudflare, but the whois query requires the whois package

apt-get install whois

and some time for the query. Of course, other networks can also be excluded from blocking based on the whois data.

#!/bin/bash
#powered by lautenbacher.io
#all hosts except DE AT CH US IE CA UK FR ES - limit 75
roundvar1=$(date +"%s")
datevar=$(date)
echo $datevar
echo starting round $roundvar1
echo "running part 1"
netstat -ant | egrep '(:80|:443) .*:.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > test.txt
sed 's/^[ \t]*//' -i test.txt
sed '/^$/d' -i test.txt
while read a b; do
    if [[ $a > "75" ]]; then

bGF1dGVuYmFjaGVyLmlvA=$(geoiplookup $b | awk -v ip="$b" '{FS=" "} {if($4 != "AT," && $4 != "US," && $4 != "DE," && $4 != "CH," && $4 != "IE," && $4 != "CA," && $4 != "UK," && $4 != "FR," && $4 != "ES,") {print "1";}}')
bGF1dGVuYmFjaGVyLmlvc=$(geoiplookup $b)
if [[ $bGF1dGVuYmFjaGVyLmlvA = "1" ]]; then
echo "running part 2"
echo checking ip $b
geoiplookup $b
echo try to add ip $b to blocklist

whois=$(whois $b)
whoisvar=0
#echo whoisvar 0
if [[ "$whois" == *"CLOUDFLARE "* ]]; then
whoisvar=1
echo Cloudflare detected
fi

if [[ "$whoisvar" != 1 ]]; then
hostvar=$(hostname)
mail -s 'Warning Message regarding '$b [email protected] <<< $b' exceeded the connection limit of 75'$whois
sudo iptables -I INPUT -s $b -j DROP
echo blocking $b
echo $b/32 >> /root/blockedcidr.txt
fi


fi

  fi
done < test.txt



echo "running part 3"
#bad countries RU CN IND IDN HKG TH VN UKR BLR VEN - limit 30
netstat -ant | egrep ':.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > testcc.txt
sed 's/^[ \t]*//' -i testcc.txt
sed '/^$/d' -i testcc.txt
while read c d; do
    if [[ $c > "30" ]]; then

bGF1dGVuYmFjaGVyLmlv=$(geoiplookup $d | awk -v ip="$d" '{FS=" "} {if($4 == "RU," || $4 == "CN," || $4 == "IND," || $4 == "IDN," || $4 == "HKG," || $4 == "TH," || $4 == "VN," || $4 == "UKR," || $4 == "BLR," || $4 == "VEN,") {print 1}}')

 if [[ $bGF1dGVuYmFjaGVyLmlv = "1" ]]; then
echo "running part 4"
bGF1dGVuYmFjaGVyLmlX=$(whois $d)
echo checking ip $d
geoiplookup $d

echo try to add ip $d to blocklist


whoisvar=0
if [[ "$bGF1dGVuYmFjaGVyLmlX" == *"CLOUDFLARE "* ]]; then
whoisvar=1
echo Cloudflare detected
fi

if [[ "$whoisvar" != 1 ]]; then

whois=$(whois $d)
hostvar=$(hostname)
mail -s 'Warning Message regarding '$d [email protected] <<< $d' bad country host exceeded the connection limit of 30'$whois
echo blocking $d
sudo iptables -I INPUT -s $d -j DROP
echo $d/32 >> /root/blockedcidr.txt
fi

 fi

   fi
done < testcc.txt

echo finishing $roundvar1

For clarity, we write the current date and time and the active iptables rules in the log file after each execution or output them with:

rulevar=$(sudo iptables -n --list --line-numbers | sed '/^num\|^$\|^Chain/d' | wc -l)
echo There are $rulevar rules active

You need help with your server? Just write me via my contact form.

Final version - part 2: Import of a CIDR blocklist from lautenbacher.io

Here it is recommended not to update every minute, but only every day! The script additionally checks if another rule already exists for the CIDR block and adds the rule only if no other rule exists.

0 0 * * /root/block.sh >> /root/block.log 2>&1

The code of the script under /root/block.sh is:

#!/bin/bash
wgetb=$(wget -O /root/cidr.txt https://www.lautenbacher.io/firewallscript/cidr.txt)
whois 1.1.1
while read a; do

checkvar=$(sudo iptables -S | grep -- $a)
blockit=0
if [[ "$checkvar" == *$a* ]]; then
blockit=1
echo skipping existing rule
fi


if [[ "$blockit" != 1 ]]; then

sudo iptables -I INPUT -s $a -j DROP ;
echo adding rule $a
fi

done < cidr.txt

while read a; do

IPs blocked in the past will automatically end up in the file "/root/blockedcidr.txt", so if you want to block them automatically (e.g. after a reboot) you could extend "block.sh" accordingly.

while read b; do

checkvar=$(sudo iptables -S | grep -- $b)
blockit=0
if [[ "$checkvar" == *$b* ]]; then
blockit=1
echo skipping existing rule
fi


if [[ "$blockit" != 1 ]]; then

iptables -I INPUT -s $b -j DROP ;
echo adding rule $b
fi

done < blockedcidr.txt

Appendix - Writing SSH and Telnet connections to a CIDR log

netstat -ant | egrep '(:22|:23) .*:.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sed 's/^[ \t]*//' | sed '/^$/d' | while read a b; do echo $b/32 >> /root/ssh.txt; done;

Leave a Reply

Your email address will not be published.