Skrypt BASH wykorzystujący ipset do zastąpienia modułu GeoIP

Ponieważ z modułem GeoIP dla iptables występują problemy głównie w jego dostępności w wielu dystrybucjach, szukałem rozwiązania, jak blokować ruch z określonych krajów, z wielką pomocą przyszła strona http://ipdeny.com/ipblocks, która zawiera listę adresów IP podzielonych na strefy – kody krajów zgodne z ISO-3166. W skrypcie wykorzystuję ipset, który jest dostępny od kernela 2.4, a sama aplikacja do zarządzania listami, również ipset, może wymagać doinstalowania. Poniżej przedstawiam skrypt, który zastępuje mi GeoIP.

#!/bin/bash
Location="/scripts";
Countries="${Location}/Countries";
Zones="${Countries}/zones";
ZonesG="${Zones}/*";
Control=100000;


function DatabaseUpdate {
    mkdir -p 
    wget http://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz -O /scripts/Countries/all-zones.tar.gz
    cd /scripts/Countries/zones
    tar xzf /scripts/Countries/all-zones.tar.gz
}


function CreateZones {
    for Zone in ${ZonesG}; do {
        IPSetName=$(echo "${Zone}"|grep -Eo '[a-z][a-z]\.zone')
            ipset create ${IPSetName} nethash -q;

        for IPNetwork in $(cat ${Zone}); do {
            ipset add ${IPSetName} ${IPNetwork} -q;
        } done
    } done
}


function RemoveSets {
     for Zone in $(ipset -l -n | sort -u); do {
        ipset destroy ${Zone};
    } done    
}

function CheckDatabase {
    if [ "$(find ${ZonesG} -type f -mtime +7 | wc -l)" -gt 0 ]; then {
        echo "Database updating";
        DatabaseUpdate;
        RemoveSets;
        CreateZones;
    } else {
        echo "Database is up to date";
    } fi    

    if [ "$(echo $Control)" == "$(CheckSets)" ]; then { 
        echo "Sets are OK";
    } else {
        echo "Sets corrupted"; CreateZones;
    } fi; 
}


function AddFirewallRules {
    for ISOCode in ru af cn in iq; do {
        ISOCode=$(echo ${ISOCode}|tr [A-Z] [a-z]);
        iptables -A INPUT -m set --match-set ${ISOCode}.zone src -m comment --comment "Block countries" -j DROP;
    } done
}

function CheckSets {
    for ZoneFile in $(ls ${Zones} -1); do {
        if [ "$(ipset -q -l -n ${ZoneFile} | wc -L;)" == "0" ]; then {
            Control=$[${Control} -1];
        } fi
    } done
    echo ${Control};
}

function Run {
    CheckDatabase
    AddFirewallRules
}
Run