iptables按照指定国家屏蔽(GEOIP模块的安装与使用)

iptables按照指定国家屏蔽(GEOIP模块的安装与使用)

该流程适合Centos7.*系统

一、安装iptables-addons(geoip模块)

1、下载lux源
wget http://repo.iotti.biz/CentOS/7/noarch/lux-release-7-1.noarch.rpm

2、安装lux源
rpm -ivh lux-release-7-1.noarch.rpm --force --nodeps

3、安装xtables-addons
yum -y install kmod-xtables-addons
yum -y install xtables-addons

一定要先安装kmod-xtables-addons后安装xtables-addons,后者依赖于前者

4、动态加载xt_geoip内核模块(每次重启需要重新执行)
insmod /lib/modules/3.10.0-327.36.2.el7.centos.plus.x86_64/extra/xtables-addons/xt_geoip.ko

利用insmod的优点是,整个部署流程不需要重启服务器。还有一种一劳永逸的方法是,在第一步先通过 yum -y install kernel kernel-devel 更新到最新内核,然后执行第1、2、3步,执行完成后重启服务器。这样操作完之后,就不需要运行第4步,每次重新重新加载xt_geoip内核模块了。

二、获得CSV格式的全球IP库

1、访问MAXMIND官网
https://www.maxmind.com/en/home
在这里插入图片描述

2、创建账户

注册地址:https://www.maxmind.com/en/geolite2/signup?lang=en 这个注册地址不是很好找。
在这里插入图片描述

按照引导流程创建账户,期间需要邮箱验证。

3、登陆账户并进入用户中心
在这里插入图片描述

在用户中心最左侧的导航栏,可以看到Download Files选项,进入该页面
在这里插入图片描述

4、下载CSV格式的IP库
在这里插入图片描述

在Download Files页面,选择最下方的 GeoLite2 Country: CSV Formate,点击右侧的Download ZIP下载。

5、解压下载到的压缩包,即可得到CSV格式的IP库
在这里插入图片描述

三、将官网下载的CSV格式IP库转换为iptables可识别的格式

1、创建格式转换脚本

#!/usr/bin/perl
#
#       Converter for MaxMind (GeoLite2) CSV database to binary, for xt_geoip
#       Copyright Jan Engelhardt, 2008-2011
#       Copyright Philip Prindeville, 2018
#
use Getopt::Long;
use Net::CIDR::Lite;
use Socket qw(AF_INET AF_INET6 inet_pton);
use warnings;
use Text::CSV_XS; # or trade for Text::CSV
use strict;

my $csv = Text::CSV_XS->new({
        allow_whitespace => 1,
        binary => 1,
        eol => $/,
}); # or Text::CSV
my $source_dir = ".";
my $quiet = 0;
my $target_dir = ".";

&Getopt::Long::Configure(qw(bundling));
&GetOptions(
        "D=s" => \$target_dir,
        "S=s" => \$source_dir,
        "q" => \$quiet,
        "s" => sub { $target_dir = "/usr/share/xt_geoip"; },
);

if (!-d $source_dir) {
        print STDERR "Source directory \"$source_dir\" does not exist.\n";
        exit 1;
}
if (!-d $target_dir) {
        print STDERR "Target directory \"$target_dir\" does not exist.\n";
        exit 1;
}

foreach (qw(LE BE)) {
        my $dir = "$target_dir/$_";
        if (!-e $dir && !mkdir($dir)) {
                print STDERR "Could not mkdir $dir: $!\n";
                exit 1;
        }
}

my %countryId;
my %countryName;
&loadCountries();
&dump(&collect());

sub loadCountries
{
        sub id; sub cc; sub long; sub ct; sub cn;

        %countryId = ();
        %countryName = ();

        my $file = "$source_dir/GeoLite2-Country-Locations-en.csv";
        print $file;
        open(my $fh, '<', $file) || die "Couldn't open list country names\n";

        # first line is headers
        my $row = $csv->getline($fh);

        my %header = map { ($row->[$_], $_); } (0..$#{$row});

        my %pairs = (
                country_iso_code => 'ISO Country Code',
                geoname_id => 'ID',
                country_name => 'Country Name',
                continent_code => 'Continent Code',
                continent_name => 'Continent Name',
        );

        # verify that the columns we need are present
        map { die "Table has no $pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs;

        my %remapping = (
                id => 'geoname_id',
                cc => 'country_iso_code',
                long => 'country_name',
                ct => 'continent_code',
                cn => 'continent_name',
        );

        # now create a function which returns the value of that column #
        map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping;

        while (my $row = $csv->getline($fh)) {
                if ($row->[cc] eq '' && $row->[long] eq '') {
                        $countryId{$row->[id]} = $row->[ct];
                        $countryName{$row->[ct]} = $row->[cn];
                } else {
                        $countryId{$row->[id]} = $row->[cc];
                        $countryName{$row->[cc]} = $row->[long];
                }
        }

        $countryName{A1} = 'Anonymous Proxy';
        $countryName{A2} = 'Satellite Provider';
        $countryName{O1} = 'Other Country';

        close($fh);

        # clean up the namespace
        undef &id; undef &cc; undef &long; undef &ct; undef &cn;
}

sub lookupCountry
{
        my ($id, $rid, $proxy, $sat) = @_;

        if ($proxy) {
                return 'A1';
        } elsif ($sat) {
                return 'A2';
        }
        $id ||= $rid;
        if ($id eq '') {
                return 'O1';
        }
        die "Unknown id: $id line $.\n" unless (exists $countryId{$id});
        return $countryId{$id};
}

sub collect
{
        my ($file, $fh, $row);
        my (%country, %header);

        sub net; sub id; sub rid; sub proxy; sub sat;

        my %pairs = (
                network => 'Network',
                registered_country_geoname_id => 'Registered Country ID',
                geoname_id => 'Country ID',
                is_anonymous_proxy => 'Anonymous Proxy',
                is_satellite_provider => 'Satellite',
        );

        foreach (sort keys %countryName) {
                $country{$_} = {
                        name => $countryName{$_},
                        pool_v4 => Net::CIDR::Lite->new(),
                        pool_v6 => Net::CIDR::Lite->new(),
                };
        }

        $file = "$source_dir/GeoLite2-Country-Blocks-IPv4.csv";
        open($fh, '<', $file) || die "Can't open IPv4 database\n";

        # first line is headers
        $row = $csv->getline($fh);

        %header = map { ($row->[$_], $_); } (0..$#{$row});

        # verify that the columns we need are present
        map { die "Table has no %pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs;

        my %remapping = (
                net => 'network',
                id => 'geoname_id',
                rid => 'registered_country_geoname_id',
                proxy => 'is_anonymous_proxy',
                sat => 'is_satellite_provider',
        );

        # now create a function which returns the value of that column #
        map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping;

        while ($row = $csv->getline($fh)) {
                my ($cc, $cidr);

                $cc = lookupCountry($row->[id], $row->[rid], $row->[proxy], $row->[sat]);
                $cidr = $row->[net];
                $country{$cc}->{pool_v4}->add($cidr);

                if ($. % 4096 == 0) {
                        print STDERR "\r\e[2K$. entries";
                }
        }

        print STDERR "\r\e[2K$. entries total\n";

        close($fh);

        # clean up the namespace
        undef &net; undef &id; undef &rid; undef &proxy; undef &sat;

        $file = "$source_dir/GeoLite2-Country-Blocks-IPv6.csv";
        open($fh, '<', $file) || die "Can't open IPv6 database\n";

        # first line is headers
        $row = $csv->getline($fh);

        %header = map { ($row->[$_], $_); } (0..$#{$row});

        # verify that the columns we need are present
        map { die "Table has no %pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs;

        # unlikely the IPv6 table has different columns, but just to be sure
        # create a function which returns the value of that column #
        map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping;

        while ($row = $csv->getline($fh)) {
                my ($cc, $cidr);

                $cc = lookupCountry($row->[id], $row->[rid], $row->[proxy], $row->[sat]);
                $cidr = $row->[net];
                $country{$cc}->{pool_v6}->add($cidr);

                if (!$quiet && $. % 4096 == 0) {
                        print STDERR "\r\e[2K$. entries";
                }
        }

        print STDERR "\r\e[2K$. entries total\n" unless ($quiet);

        close($fh);

        # clean up the namespace
        undef &net; undef &id; undef &rid; undef &proxy; undef &sat;

        return \%country;
}

sub dump
{
        my $country = shift @_;

        foreach my $iso_code (sort keys %{$country}) {
                &dump_one($iso_code, $country->{$iso_code});
        }
}

sub dump_one
{
        my($iso_code, $country) = @_;
        my($file, $fh_le, $fh_be);

        printf "%5u IPv6 ranges for %s %s\n",
                scalar(@{$country->{pool_v6}->list_range()}),
                $iso_code, $country->{name};

        $file = "$target_dir/LE/".uc($iso_code).".iv6";
        if (!open($fh_le, "> $file")) {
                print STDERR "Error opening $file: $!\n";
                exit 1;
        }
        $file = "$target_dir/BE/".uc($iso_code).".iv6";
        if (!open($fh_be, "> $file")) {
                print STDERR "Error opening $file: $!\n";
                exit 1;
        }
        foreach my $range (@{$country->{pool_v6}->list_range()}) {
                my ($start, $end) = split('-', $range);
                print $fh_be &ip6_pack($start), &ip6_pack($end);
                print $fh_le &ip6_swap($start), &ip6_swap($end);
        }
        close $fh_le;
        close $fh_be;

        printf "%5u IPv4 ranges for %s %s\n",
                scalar(@{$country->{pool_v4}->list_range()}),
                $iso_code, $country->{name};

        $file = "$target_dir/LE/".uc($iso_code).".iv4";
        if (!open($fh_le, "> $file")) {
                print STDERR "Error opening $file: $!\n";
                exit 1;
        }
        $file = "$target_dir/BE/".uc($iso_code).".iv4";
        if (!open($fh_be, "> $file")) {
                print STDERR "Error opening $file: $!\n";
                exit 1;
        }
        my($sc1, $sc2, $sc3, $sc4);
        my($start, $end);
        foreach my $range (@{$country->{pool_v4}->list_range()}) {
                ($start, $end) = split('-', $range);
                ($sc1, $sc2, $sc3, $sc4) = split('\.', $start);
                $start = $sc1 * 256**3 + $sc2 * 256**2 + $sc3 * 256 + $sc4;
                ($sc1, $sc2, $sc3, $sc4) = split('\.', $end);
                $end = $sc1 * 256**3 + $sc2 * 256**2 + $sc3 * 256 + $sc4;
                print $fh_le pack("VV", $start, $end);
                print $fh_be pack("NN", $start, $end);
        }
        close $fh_le;
        close $fh_be;
}

sub ip6_pack
{
        my $addr = shift @_;
        $addr =~ s{::}{:!:};
        my @addr = split(/:/, $addr);
        my @e = (0) x 8;
        foreach (@addr) {
                if ($_ eq "!") {
                        $_ = join(':', @e[0..(8-scalar(@addr))]);
                }
        }
        @addr = split(/:/, join(':', @addr));
        $_ = hex($_) foreach @addr;
        return pack("n*", @addr);
}

sub ip6_swap
{
        return pack("V*", unpack("N*", shift @_));
}

创建文件 xt_geoip_build 并讲该脚本内容写入文件。运行 chmod -x xt_geoip_build 给予文件可执行权限

2、安装xt_geoip_build脚本所需要的依赖
yum -y install perl-Text-CSV_XS
wget https://download-ib01.fedoraproject.org/pub/epel/7/aarch64/Packages/p/perl-Net-CIDR-Lite-0.21-11.el7.noarch.rpm
rpm -ivh perl-Net-CIDR-Lite-0.21-11.el7.noarch.rpm --force --nodeps

3、将csv格式ip库转换为geoip模块格式
mkdir /usr/share/xt_geoip
./xt_geoip_build -D /usr/share/xt_geoip -S csv文件路径

至此 GEOIP 模块就安装配置完成了

四、使用iptables命令屏蔽国家

1、屏蔽中国、美国和中国香港来访用户
iptables -I INPUT -m geoip --src-cc CN,US,HK -j DROP

2、屏蔽除中国大陆外的所有来访用户
iptables -I INPUT -m geoip ! --src-cc CN -j DROP

3、查看geoip模块使用说明
iptables -m geoip -h

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iceveil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值