-
-
Save Pandry/21fc0e30abbfd0579ec69c491b99a446 to your computer and use it in GitHub Desktop.
#!/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 |
Thank you @Pandry, I will give that a shot and let you know.
Hello, would you suggest how this script can be tweaked to restrict country access to only http/https and dovecot (ports 587, 993, 995), but still allow mail delivery on port 25 from anywhere (anti-spam is handled using other methods). Thanks!
Hey! How is this supposed to run? Just saved onto a .sh script an run it? No arguments? How can I add zones or remove? I was looking to only block Australia and China as its not under their Google Cloud free tier policy. I think this script has lots of potential for this specific use case.
Hi guys!
Sorry for reaching out that late, I've got stuff to do and this completely got off of my mind.
Hello, would you suggest how this script can be tweaked to restrict country access to only http/https and dovecot (ports 587, 993, 995), but still allow mail delivery on port 25 from anywhere (anti-spam is handled using other methods). Thanks!
Hi @alvin275, yes, this could be used for such a purpose, but there's some editing required!
Being more specific, the command at line 98 needs to be changed to specify the services you wish to block (appending something like --service=http --service=https --service=imap
; Keep in mind that every service not appended here will be exposed world-wide).
I haven't tried this specifically, but it should suffice
Hey! How is this supposed to run? Just saved onto a .sh script an run it? No arguments? How can I add zones or remove? I was looking to only block Australia and China as its not under their Google Cloud free tier policy. I think this script has lots of potential for this specific use case.
Hi @kiraitachi!
Yes, ideally this should be put in something like a cronjob (the GeoIP DB changes from time to time), but can as well be a one-off script.
There are no arguments as of now (I'm also not using this script anymore, just providing support from time to time), BUT you need to edit the script once to set the nations you DON'T want to block.
This will block all the IPs in the GeoIP database except the ones you 'whitelist'.
The 'whitelisting' is done by deleting the zones you want to allow traffic from (look at line 83 in this case)
Sorry again for being so late
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.
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
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
@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.
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
@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.
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.
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:
- Switch the FirewallBackend from nftables to iptables in /etc/firewalld/firewalld.conf
- 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.
Hi @chr00ted! Glad to hear you’re still using the script!
DISCLAIMER: I’m from mobile at the moment and had no time to check the actual code and firewalld documentation. The script is old and I don’t use it from a long time.
So, the script works by blacklisting a lot of IPs, but the firewall remains in allow-by-defaullt (for a couple of reason out of scope right now).
If you encountered an issue since when you changed your ISP, it sounds to me like the CIRD your ISP has assigned to you belongs to a country you’re blacklisting.
It may be either wrong data, old data or maybe the ISP you have operates on multiple countries.
Anyway, my approach would be to whitelist the CIDR your ISP has
I’d start with checking the CIDR assigned to you at the moment, and checking if I can find it in the zone files.
For instance, I use the service https://ipinfo.io/ and from the homepage I can see info on the provider I’m surfing the web from:
You can try to grep it in the zones folder:
If this is the case, one or more subnet of your AS may be incorrectly reported
To fix it, you could try to add this at the end of the script to allow the CIDR(s) of your ISP
firewall-cmd --zone=public --add-source=194.110.113.0/24 --add-source=194.110.114.0/24 # If the above works: firewall-cmd --permanent --zone=public --add-source=194.110.113.0/24 --add-source=194.110.114.0/24 firewall-cmd --reload
I would look at the ISP’s AS and whitelist all the subnets, if I were you (PLEASE check the code and ensure it works before adding it to the script)