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

Hi All,
I don't no if someone have try this script recently but for me it's not working and break firewalld on Fedora37 when using nftables (that is the default settings). I'm interested by your feedback on this distrib. But thanks a lot anyway for the script workflow.

@Pandry
Copy link
Author

Pandry commented Mar 1, 2023

Hi @Talisker69!
Unfortunately I’m not using nor maintaining the script from a long time, but I try to offer support from time to time.

break firewalld on Fedora37 when using nftables

Can you be a bit more specific? Maybe posting the errors you are encountering.
I suspect it may be an ipset-related issue (in fact i found an issue about its support )
If that’s the case, I’ve found some info on the nftables website

@Talisker69
Copy link

Talisker69 commented Mar 2, 2023

Hi @Pandry,
Please find below the trace of errors. I confirm that the problem come from the backend not from firewalld, so if your are using iptables as backend you should not have this problem. Firewalld is just the frontend. So after summit the blacklist to the drop zone the cpu rise up (1 theard at 100%), the box refuse all connexion to stop all ports, hopefully I don't loose my ssh connection and I rebooted. After the reboot the firewalld refuse to startup due to errors and reload the default config. So I remove all files in /etc/firewalld/ipsets .... and reboot again so every back to normal. By the way, i'm using selinux just for the record ...

Mar  1 19:09:03 odroid01 firewalld[818]: ERROR: INVALID_IPSET: blacklist
Mar  1 19:17:34 odroid01 audit[818]: NETFILTER_CFG table=firewalld_policy_drop:22 family=1 entries=16 op=nft_register_chain pid=818 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 19:17:34 odroid01 audit[818]: NETFILTER_CFG table=firewalld:23 family=1 entries=260 op=nft_unregister_table pid=818 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 19:17:34 odroid01 audit[818]: NETFILTER_CFG table=firewalld:24 family=1 entries=1 op=nft_register_table pid=818 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 19:36:15 odroid01 systemd[1]: Stopping firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: State 'stop-sigterm' timed out. Killing.
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: Killing process 818 (firewalld) with signal SIGKILL.
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: Killing process 914 (gmain) with signal SIGKILL.
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: Main process exited, code=killed, status=9/KILL
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: Failed with result 'timeout'.
Mar  1 19:37:46 odroid01 systemd[1]: Stopped firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 19:37:46 odroid01 systemd[1]: firewalld.service: Consumed 20min 11.543s CPU time.
Mar  1 19:37:46 odroid01 audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=failed'
Mar  1 19:38:14 odroid01 systemd[1]: Starting firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 19:38:14 odroid01 systemd[1]: Started firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 19:38:14 odroid01 audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Mar  1 19:38:15 odroid01 audit[815]: NETFILTER_CFG table=firewalld:2 family=1 entries=1 op=nft_register_table pid=815 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 19:38:27 odroid01 NetworkManager[824]: <warn>  [1677695907.6278] firewalld: [d13620f7af927370,change:"enp1s0"]: complete: request failed (Timeout was reached)
Mar  1 19:38:27 odroid01 NetworkManager[824]: <warn>  [1677695907.7908] firewalld: [73161c0c3b87df87,change:"enp2s0"]: complete: request failed (Timeout was reached)
Mar  1 19:52:13 odroid01 systemd[1]: Stopping firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 19:52:13 odroid01 systemd[1]: firewalld.service: Deactivated successfully.
Mar  1 19:52:13 odroid01 systemd[1]: Stopped firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 19:52:13 odroid01 audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Mar  1 19:52:13 odroid01 systemd[1]: firewalld.service: Consumed 13min 55.506s CPU time.
Mar  1 20:03:40 odroid01 systemd[1]: Starting firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 20:03:40 odroid01 systemd[1]: Started firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 20:03:40 odroid01 audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Mar  1 20:03:41 odroid01 firewalld[3540]: ERROR: Failed to load user configuration. Falling back to full stock configuration.
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:3 family=1 entries=1 op=nft_register_table pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:3 family=1 entries=1 op=nft_unregister_table pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:4 family=1 entries=234 op=nft_register_chain pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 20:03:41 odroid01 firewalld[3540]: ERROR: INVALID_IPSET: geoblacklist
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:5 family=1 entries=6 op=nft_register_rule pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:6 family=1 entries=394 op=nft_register_obj pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 20:03:41 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:6 family=1 entries=103 op=nft_register_chain pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:06:28 odroid01 firewalld[3540]: ERROR: INVALID_IPSET: geoblacklist
Mar  1 21:25:44 odroid01 systemd[1]: Stopping firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 21:25:44 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:7 family=1 entries=394 op=nft_unregister_obj pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:44 odroid01 audit[3540]: NETFILTER_CFG table=firewalld:7 family=1 entries=258 op=nft_unregister_table pid=3540 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:44 odroid01 systemd[1]: firewalld.service: Deactivated successfully.
Mar  1 21:25:44 odroid01 systemd[1]: Stopped firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 21:25:44 odroid01 audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Mar  1 21:25:52 odroid01 systemd[1]: Starting firewalld.service - firewalld - dynamic firewall daemon...
Mar  1 21:25:52 odroid01 systemd[1]: Started firewalld.service - firewalld - dynamic firewall daemon.
Mar  1 21:25:52 odroid01 audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=firewalld comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Mar  1 21:25:52 odroid01 audit[5530]: NETFILTER_CFG table=firewalld:8 family=1 entries=1 op=nft_register_table pid=5530 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:52 odroid01 firewalld[5530]: ERROR: Failed to load user configuration. Falling back to full stock configuration.
Mar  1 21:25:52 odroid01 audit[5530]: NETFILTER_CFG table=firewalld:9 family=1 entries=1 op=nft_register_table pid=5530 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:52 odroid01 audit[5530]: NETFILTER_CFG table=firewalld:9 family=1 entries=1 op=nft_unregister_table pid=5530 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:52 odroid01 audit[5530]: NETFILTER_CFG table=firewalld:10 family=1 entries=234 op=nft_register_chain pid=5530 subj=system_u:system_r:firewalld_t:s0 comm="firewalld"
Mar  1 21:25:52 odroid01 firewalld[5530]: ERROR: INVALID_IPSET: geoblacklist

@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