Country Blocking with iptables and ipset
The purpose of this howto is to outline a a script which can be used to create a list of country-associated IP's which can be used to either block or allow access to your system. There are 4 part elements to this:
- Create the country list
- Set up a boot mechanism
- Create the firewall rules
Create the country list
The following script will create an ipset list of selected countries' IP addresses. Create a file, /etc/cron.monthly/country_list in in it put:
#!/bin/bash # A list of the ISO country codes can be found at http://en.wikipedia.org/wiki/ISO_3166-1 # Countries are case insensitive for this script ISO="at be ch cy cz de dk es fr gb gr ie it lu mt nl pt eu va sm mc je gg im" MAXELEM=131072 #MAXELEM=524288 if [ "`lsmod | grep ip_set`" = "" ]; then modprobe ip_set fi # Destroy country-list-temp in case it exists and is populated ipset destroy -q country-list-temp # Make sure the new lists exist ipset create country-list nethash maxelem $MAXELEM -exist ipset create country-list-temp nethash maxelem $MAXELEM -exist # Load the country list curl -s -d country=1 --data-urlencode "country_list=$ISO" -d format_template=prefix https://ip.ludost.net/cgi/process | grep -v ^# | while read -r line do ipset -A -exist country-list-temp $line done if [ $(ipset list country-list-temp | wc -l) -le 7 ]; then logger -t country-list "Update failed" echo 'Country List Update failed' | mail -s 'Country List Update failed' [email protected] ipset destroy -q country-list-temp exit fi # Make the temp list current ipset swap country-list country-list-temp # Destroy the (now old) temp list ipset destroy -q country-list-temp # add some exceptions #ipset add -exist country-list 209.90.117.194 #ipset add -exist country-list 209.90.117.196 #ipset add -exist country-list 159.203.19.178 # Create save list for loading on boot ipset save country-list > /usr/src/ipset_country-list.save sed -i 's/create/create -exist/g' /usr/src/ipset_country-list.save sed -i 's/add/add -exist/g' /usr/src/ipset_country-list.save logger -t country-list "Updated"
Notes:
- you could put it in cron.weekly if you wanted but the list should be fairly stable so monthly updates are probably OK.
- change the list of countries to suit your needs. A full list of country codes can be found here .
- change the e-mail address to suit where you want the error message sent to if the update fails.
Then make the script executable and execute the script for the first time:
chmod 0755 /etc/cron.monthly/country_list /etc/cron.monthly/country_list
It takes a couple of minutes to run. If it errors, you may need to increase the list size (MAXELEM). China and the US both produce very big lists. Oddly, increasing the number of countries blocked can reduce the list size. This can happen as it can become possible for lots of smaller IP blocks to be consolidated into fewer bigger IP blocks. ludost.net does this consolidation for you.
Boot up
In order for this to work from start up, we need to restore the last created list that the script backs up, otherwise you have to wait until the next cron.monthly run. Add the following to /etc/rc.d/rc.local:
# Load in all previously saved ipset sets if [ "`lsmod | grep ip_set`" = "" ]; then modprobe ip_set fi for file in /usr/src/ipset_*.save ; do ipset restore < $file done
And make it executable:
chmod 0744 /etc/rc.d/rc.local
Add the firewall rules
Note the firewall rules need to be personalised to your environment. Add them to a file /etc/clearos/firewall.d/20-ipset-blocks. The could be something like:
# IPv4 only for now #------------------ if [ "$FW_PROTO" != "ipv4" ]; then return 0 fi if [ "`lsmod | grep ip_set`" = "" ]; then modprobe ip_set fi # Block country addresses (exempt permitted countries) # # note the > /dev/null 2>&1 is needed for some odd reason ipset create country-list nethash -exist > /dev/null 2>&1 $IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p tcp -m multiport --dports 587,993 -j DROP $IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p udp -m multiport --dports 1194 -j DROP
This example would restrict me to only be able to pick up e-mails and connect to OpenVPN from my chosen countries as these ports are open further down the INPUT chain.
An alternative rule to block anything but TCP would be:
$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src ! -p tcp -j DROP
As an alternative to allowing OpenVPN in the normal incoming firewall then dripping OpenVPN from outside the chosen list, you could not open OpenVPN in the incoming firewall ans just use:
$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p udp -m multiport --dports 1194 -j ACCEPT
Once you have created your firewall rules, restart the firewall with a:
systemctl restart firewall