Originally published October 4, 2019 @ 11:58 am

I’ve been using my mod of this handy script to block countries with iptables. One issue with the script is that it is adding rules one-by-one using the iptables -A syntax. This is the proper way to add rules, but it does take a long while. And here’s how you can make it orders of magnitude faster.

Here’s the basic process in the original script:

for each country_code
do
  download zone file
  get a list of all IPs
  for each IP in the list
  do
    add a logging rule to iptables
    add a block rule to iptables
  done
done

Here’s what I have in my revised version:

save current iptables config
copy saved config to a temp file
remove (but remember) the COMMIT footer at the end of the config file

for each country_code
do
  download zone file
  get a list of all IPs
  for each IP in the list
  do
    echo logging rule to temp file
    echo block rule to temp file
  done
done

move temp file to iptables config file
reload iptables

The iptables command is called after all the rules are already in the config file, and not for every single rule, of which there may be tens of thousands.

Here’s the actual code for this:

function func_add_rules() {
  iptables-save > /etc/sysconfig/iptables
  tmpfile3=$(mktemp)
  /bin/cp -p /etc/sysconfig/iptables ${tmpfile3}
  footer="$(sed -n '/^COMMIT/{:a;n;/*$/b;p;ba}' ${tmpfile3})"
  sed -i "/^COMMIT/,/*$/d" ${tmpfile3}
  for country_code in af cn ua jp
  do
    wget -O /root/${country_code}.zone ${download_url}/${country_code}.zone
    log_msg="${country_code} CountryDrop"
    banned_ips=$(grep -Ev "^#|^$" /root/${country_code}.zone)
    for ban_ip in ${banned_ips}
    do
      echo "-A ${chain_name} -s ${ban_ip} -j LOG --log-prefix ${log_msg}" >> ${tmpfile3}
      echo "-A ${chain_name} -s ${ban_ip} -j DROP" >> ${tmpfile3}
    done
  done
  echo COMMIT >> ${tmpfile3}
  echo "${footer}" >> ${tmpfile3}
  awk '/^COMMIT$/ { delete x; }; !x[$0]++' ${tmpfile3} > /etc/sysconfig/iptables
  iptables-restore < /etc/sysconfig/iptables
  /sbin/service iptables reload
  /sbin/service iptables save
}