Skip to content

Instantly share code, notes, and snippets.

@Pandry
Last active May 2, 2024 20:18
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
@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