Skip to content

Instantly share code, notes, and snippets.

@Pandry
Last active July 3, 2024 13:07
Show Gist options
  • Save Pandry/21fc0e30abbfd0579ec69c491b99a446 to your computer and use it in GitHub Desktop.
Save Pandry/21fc0e30abbfd0579ec69c491b99a446 to your computer and use it in GitHub Desktop.
Block countries IPs via Firewalld
#!/bin/bash
##
# Name: GeoIP Firewall script
# Author: Pandry
# Version: 0.1.1
# Description: This is a simple script that will set up a GeoIP firewall blocking all the zones excecpt the specified ones
# it is possible to add the whitelisted zones @ line 47
# Additional notes: Usage of [iprange](https://github.com/firehol/iprange) is suggested
# for best performances
##
BLACKLIST_NAME="geoblacklist"
TMPDIR="/tmp/geoip"
if [ $(which yum) ]; then
echo -e "[\e[32mOK\e[39m] Detected a RHEL based environment!"
echo -e "[\e[93mDOING\e[39m] Making sure firewalld is installed..."
yum -y install firewalld > /dev/null 2> /dev/null
if [[ $? -eq 0 ]];then
echo -e "[\e[32mOK\e[39m] firewalld is installed!"
systemctl enable --now firewalld > /dev/null 2> /dev/null
else
echo -e "[\e[31mFAIL\e[39m] Couldn't install firewalld, aborting!"
exit 1
fi
elif [ $(which apt) ]; then
echo -e "[\e[32mOK\e[39m] Detected a Debian based environment!"
echo -e "[\e[93mDOING\e[39m] Making sure firewalld is installed..."
apt -y install firewalld > /dev/null 2> /dev/null
if [[ $? -eq 0 ]];then
echo -e "[\e[32mOK\e[39m] firewalld is installed!"
systemctl enable --now firewalld > /dev/null 2> /dev/null
else
echo -e "[\e[31mFAIL\e[39m] Couldn't install firewalld, aborting!"
exit 1
fi
elif [ $(which apk) ]; then
echo -e "[\e[31mFAIL\e[39m] Alpine Linux is not supported yet!"
exit 1
else
echo -e "[\e[31mFAIL\e[39m] Couldn't determine the current OS, aborting!"
exit 1
fi
#Create the blacklist (only if necessary)
#200k should be enough - $(find . -name "*.zone" | xargs wc -l) gives 184688 lines without the it zone
firewall-cmd --get-ipsets| grep "$BLACKLIST_NAME" > /dev/null 2> /dev/null
if [[ $? -ne 0 ]];then
echo -e "[\e[93mDOING\e[39m] Creating "
firewall-cmd --permanent --new-ipset="$BLACKLIST_NAME" --type=hash:net --option=family=inet --option=hashsize=4096 --option=maxelem=200000 > /dev/null 2> /dev/null
if [[ $? -eq 0 ]];then
echo -e "[\e[32mOK\e[39m] Blacklist $BLACKLIST_NAME successfully created!"
else
echo -e "[\e[31mFAIL\e[39m] Couldn't create the blacklist $BLACKLIST_NAME, aborting!"
exit 1
fi
fi
#create the folder
mkdir -p $TMPDIR
#Downloads the GeoIP database
if [[ $? -eq 0 ]];then
echo -e "[\e[93mDOING\e[39m] Downloading latest ip database... "
curl -L -o $TMPDIR/geoip.tar.gz http://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz > /dev/null 2> /dev/null
if [[ $? -eq 0 ]];then
echo -e "[\e[32mOK\e[39m] Database successfully downloaded!"
else
echo -e "[\e[31mFAIL\e[39m] Couldn't download the database, aborting!"
exit 1
fi
else
echo -e "[\e[31mFAIL\e[39m] Couldn't create the $TMPDIR directory!"
exit 1
fi
#Extract the zones in the database
tar -xzf $TMPDIR/geoip.tar.gz -C $TMPDIR
#Remove all the zones you want to allow
rm $TMPDIR/it.zone $TMPDIR/eu.zone
#Add the IPs to the blacklist
for f in $TMPDIR/*.zone; do
echo -e "[\e[93mDOING\e[39m] Adding lines from $f ..."
firewall-cmd --permanent --ipset="$BLACKLIST_NAME" --add-entries-from-file=$f > /dev/null
if [[ $? -eq 0 ]];then
echo -e "[\e[32mOK\e[39m] Added $f with no issues";
else
echo -e "[\e[31mFAIL\e[39m] Some errors verified while adding the $f zone";
fi
echo ""
done
# Drop the IPs
firewall-cmd --permanent --zone=drop --add-source="ipset:$BLACKLIST_NAME" > /dev/null
#Reload the firewall
firewall-cmd --reload
cd ~
# Remove the traces
rm -rf /tmp/geoip
@Talisker69
Copy link

@Pandry I don't run the script just added 2 countries in the geoblacklist and affect it to the drop zone like you are doing in your script. I don't think that there is any mistake in your script is just my configuration that is a little bit different. But after the years iptables will be deprecated in many linux distributions so be careful.

@Pandry
Copy link
Author

Pandry commented Mar 5, 2023

Yeah, unfortunately this script is pretty old and did not stand to the test of time.
I should look into how to use the nftables backend but I'm not even using CentOS anymore
I'll eventually give it a look, don't count on that

@Talisker69
Copy link

@Pandry
Don't worry pandry I switch to another solution. I prefer now use geoip at the http level (7) with maxmind database (it's free). The problem with nftables finally is Firewalld because the best practice is to use directly nftables (wihout ipset) so you will be obliged to disabled Firewalld so you will have new problems. So it's that why I use blacklist at the level 7 and not at the network level. I don't publish any service at this level. Anyway thanks for you script that en-light me on that technology.

@snarl817
Copy link

I am unable to use this to block countries. I HAD been using iptables with the xt_geoip addon on CentOS 7, and it works fine. BUT, I need to migrate my jump host to a more modern distro, so I installed Fedora 37. Using this script as a starting point (I just have a list of country codes to block), I create an IPSet in firewalld, but when the script attempts to activate the rules, I get a timeout from dbus. Watching the system performance, firewalld spikes the CPU as soon as the ipset is added.

I searched, and apparently this is a known issue: the daemon checks for overlap in the IPSet, and that check is VERY CPU intensive.
firewalld/firewalld#881
This was SUPPOSEDLY fixed last year, but it seems to still be broken. I cloned the current firewalld repo and built from source, but the size of the block ipset still hangs the daemon.

@snarl817
Copy link

Ok, I've done some more digging, and debugging, and the issue is nftables is TERRIBLE at managing large ipsets when using firewalld as a frontend. There are two solutions for this:

  1. Switch the FirewallBackend from nftables to iptables in /etc/firewalld/firewalld.conf
  2. Use nftables to load the the ipset outside of firewalld.

I'm having difficulty locating documentation on HOW to implement #2, so I just switched the backend to iptables. Yes, it's deprecated and will be going away "soon", but I'd imagine that they'll keep it around until libnftables gets fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment