Here is a simple way to restrict access to your server from country’s that you don’t want to be able to connect to your services. On website www.ipdeny.com you can find IP lists for specific country’s. With a simple script, you can regularly update those lists so that they are up-to-date with new addresses. In my case, I needed a way to allow some services only available from specific countries. You can also change logic a little bit and blocking only specific county.
This will work on Linux server with installed iptables and ipset. Ipset will contain all addresses provided from ipdeny.com.
First, if you don’t already have it, install ipset.
[root@server ~]# dnf install ipset
Then, you’ll need to create ipset array which will contain all addresses.
ipset create allow_cc hash:net family inet hashsize 1024 maxelem 65536
Inside /root/ipset, create a script named ipset.sh and put code below in it. In variable countries, you must define all country codes that you wish to allow access from. In ROOT_DIR you can change root directory to your needs. Script will create two more directories within ROOT_DIR.
if [ ! -d "$ROOT_DIR" ]; then
mkdir -p $ROOT_DIR $TMP_DIR $IPSET_DIR;
if [ -f "$ALL_ZONES" ]; then
rm -f $ALL_ZONES
wget -O $ALL_ZONES --no-check-certificate http://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz
tar -xzvf $ALL_ZONES -C $TMP_DIR
echo -n > $IPSET_DIR/allowed-cc.zone
for cn in $countries; do
cat $TMP_DIR/$cn.zone >> $IPSET_DIR/allowed-cc.zone
for range in $(cat $IPSET_DIR/allowed-cc.zone); do
echo "Adding $range to CC...";
ipset add allow_cc $range;
Make this script executable and run it, so that it will fill addressees in ipset array.
root@server:~/ipset# chmod +x ipset.sh
URL transformed to HTTPS due to an HSTS policy
--2022-05-12 13:22:44-- https://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz
Resolving www.ipdeny.com (www.ipdeny.com)... 188.8.131.52, 2001:19f0:5:40e6:5400:2ff:fe71:c357
Connecting to www.ipdeny.com (www.ipdeny.com)|184.108.40.206|:443... connected.
. . .
You can check this list with command ipset -L. It should be propagated with addressees.
root@server:~/ipset# ipset -L | head -20
Header: family inet hashsize 32768 maxelem 65536
Size in memory: 1900312
Number of entries: 63899
. . .
In your iptables configuration, you must add a rule that will allow access to a specific port only from addresses that are stored in ipset’s allow_cc array. For example, allow SSH only from allowed country’s:
. . .
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -i lo -j REJECT –reject-with icmp-port-unreachable
-A INPUT -m state –state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEP
-A INPUT -p tcp -m set –match-set allow_cc src -m tcp –dport 22 -j ACCEPT
. . .
Create cron job that will download fresh lists with addressees and update ipset array. For example:
0 1 * * * /bin/bash /root/ipset/ipset.sh > /var/log/country-ipset.log
That should do it. You can change logic according to your needs.
Thank you Dalibor 🙂