nginx主机黑白名单[geoip]

国家黑白名单通过 ngx_http_geoip2_module 模块实现

1.下载 libmaxminddb 并编译安装

wget https://github.com/maxmind/libmaxminddb/releases/download/1.4.3/libmaxminddb-1.4.3.tar.gz
tar xvf libmaxminddb-1.4.3.tar.gz 
cd libmaxminddb-1.4.3/
./configure
make
make check
make install
ldconfig 
sudo sh -c "echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf"
ldconfig

2.下载ngx_http_geoip2_module

wget https://github.com/leev/ngx_http_geoip2_module/archive/3.3.tar.gz
tar xvf 3.3.tar.gz

3.编译动态模块

a.需要先查看已经安装 nginx的版本

[root@devops ~]# nginx -v
nginx version: nginx/1.18.0

b.查看nginx编译的参数

[root@devops ~]# nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

c.下载源码,并使用相同参数编译,增加动态编译模块 --add-dynamic-module=../ngx_http_geoip2_module-3.3

wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar zxvf nginx-1.18.0.tar.gz
cd nginx-1.18.0
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-dynamic-module=../ngx_http_geoip2_module-3.3
make -j4  #不需要 make install

编译好的so 在  objs/ngx_http_geoip2_module.so  

cp objs/ngx_http_geoip2_module.so /usr/share/nginx/modules/

NGINX配置

#服务模块到nginx modules 目录

mkdir /usr/share/nginx/modules
cp objs/ngx_http_geoip2_module.so /usr/share/nginx/modules/
cp objs/ngx_http_geoip2_module.so /etc/nginx/modules/ngx_http_geoip2_module.so

#增加geoip2 模块

vim /etc/nginx/nginx.conf
load_module modules/ngx_http_geoip2_module.so;

在 nginx.conf http 配置里面增加

include conf.d/waf/waf.conf ;

在对应的 server 里面添加

#在 server 内添加
include conf.d/waf/status ;
include conf.d/waf/dynamic_limit ;

waf.tar.gz  需要解压到 /etc/nginx/conf.d/ 目录下

waf.tar.gz

分析一下waf内容

image.png

black_ip_list                ip 黑名单,支持ip段添加 格式: 112.2.3.0/24 1;
dynamic_limit                limit_req 和 limit_conn 配置
dynamic_variable             limit_req 和 limit_conn 拦截配置 
GeoLite2-Country.mmdb        geoip 国家库
limit_conn                    
limit_req
ngx_http_geoip2_module.so
status                       服务器黑白名单访问状态码配置 
waf.conf                     主要配置
white_country_list           国家白名单,格式: CN 1;
white_ip_limit_list          limit限制 ip白名单,增加之后,该ip访问,limit 不生效
white_ip_list                ip 白名单,格式和ip黑名单一致

waf.conf

geoip2 /etc/nginx/conf.d/waf/GeoLite2-Country.mmdb {
    #auto_reload 5m;
    $geoip2_metadata_country_build metadata build_epoch;
    $geoip2_data_country_code default=LOCAL source=$remote_addr country iso_code;
    $geoip2_data_country_name country names en;
    #$geoip2_data_country_name country names zh-CN;

    #$geoip2_data_country_name country names en;
    #$geoip2_data_city_name default=Shanghai city names en;
    #$geoip2_data_province_name subdivisions 0 names en;
    #$geoip2_data_province_isocode subdivisions 0 iso_code;
}

map $geoip2_data_country_code $allowed_country {
    default 0;
    LOCAL 1;
    include conf.d/waf/white_country_list;
}


geo $remote_addr $ipblacklist {
    default 0;
    include conf.d/waf/black_ip_list;
}

geo $remote_addr $allowed_ip {
    default 0 ;
    include conf.d/waf/white_ip_list;
}


geo $limit_white_ip_list {
    default 0 ;
    include conf.d/waf/white_ip_limit_list;
}

map $limit_white_ip_list $limit {
    0 $http_authorization ;
    1 "no_limit";
}

map $limit $auth_token {  # 首先根据token判断访问速率,如果token不存在,则根据远端ip 判断访问速率
	"" $binary_remote_addr;
	"~^Bearer(.*)(?<token>.{60}$)" $token; # 这个是根据业务处理,我们每个用户访问服务,header 里面带有token,可以根据token 判断用户访问速率
	"no_limit" "";
}

include conf.d/waf/dynamic_variable ;

这个是服务器的配置,手动操作很是繁琐,最好有个可以控制的简单界面

那开始整


页面采用vue + element-ui 

数据库使用的 postgresql

后端使用的nodejs,用Python也都一样,就是一个简单的api访问接口,实现读写文件,重载nginx服务


我先说下我的思路,服务器黑白名单,城市访问白名单,访问速率限制,访问速率白名单,这些功能的数据,先存到数据库里面,然后每次操作,都把数据从数据库读取 和要操作的数据进行处理,处理好之后,写到配置文件里面,然后通过命令检查配置是否正常,如果正常,再通过回调方法把数据写到数据库里面,不正常就回滚配置,提示操作失败


有思路之后,那就开始搞数据表结构

a.一个世界大部分国家的数据表

b.一个服务器黑白名单的数据表

c.速率访问限制配置表

城市国家数据,这个geoip 官方可以找到,

DROP TABLE IF EXISTS "public"."geoip2_data_country_info";
CREATE TABLE "public"."geoip2_data_country_info" (
  "code" varchar(4) COLLATE "pg_catalog"."default" NOT NULL,
  "cname" varchar(255) COLLATE "pg_catalog"."default",
  "ename" varchar(255) COLLATE "pg_catalog"."default",
  "isactive" bool DEFAULT false
)
;
COMMENT ON COLUMN "public"."geoip2_data_country_info"."code" IS '国家代码';
COMMENT ON COLUMN "public"."geoip2_data_country_info"."cname" IS '国家英文名称';
COMMENT ON COLUMN "public"."geoip2_data_country_info"."ename" IS '国家中文名称';
COMMENT ON COLUMN "public"."geoip2_data_country_info"."isactive" IS '是否允许该国家ip访问';

服务器黑白名单,库可以这样定义

CREATE TABLE "public"."ipbwlist" (
  "ip" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "status" bool NOT NULL DEFAULT true,
  "des" varchar(255) COLLATE "pg_catalog"."default",
  "type" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "ctime" int8 NOT NULL
)
;
COMMENT ON COLUMN "public"."ipbwlist"."ip" IS '主机ip地址';
COMMENT ON COLUMN "public"."ipbwlist"."status" IS '状态';
COMMENT ON COLUMN "public"."ipbwlist"."des" IS '备注信息';
COMMENT ON COLUMN "public"."ipbwlist"."type" IS '类型 ,white  表示白名单,  black 表示黑名单, limit 表示访问速率限制白名单';
COMMENT ON COLUMN "public"."ipbwlist"."ctime" IS '修改时间';

-- ----------------------------
-- Primary Key structure for table geoip2_data_country_info
-- ----------------------------
ALTER TABLE "public"."geoip2_data_country_info" ADD CONSTRAINT "geoip2_data_country_info_pkey" PRIMARY KEY ("code");

-- ----------------------------
-- Primary Key structure for table ipbwlist
-- ----------------------------
ALTER TABLE "public"."ipbwlist" ADD CONSTRAINT "ipbwlist_pkey" PRIMARY KEY ("ip");

访问速率配置,直接一个 key value 的配置就行

CREATE TABLE "public"."server_config" (
    config text,
    value text
);

那开始整这个后端,需要先定义一下文件和服务操作

const nginx_base_waf = '/etc/nginx/conf.d/waf/';
module.exports = {
    "black_ip_list": nginx_base_waf + 'black_ip_list',
    "white_ip_list": nginx_base_waf + 'white_ip_list',
    "white_country_list": nginx_base_waf + 'white_country_list',
    "white_ip_limit_list": nginx_base_waf + 'white_ip_limit_list',
    "limit_conn": nginx_base_waf + 'limit_conn',
    "limit_req": nginx_base_waf + 'limit_req',
    "nginx_check_cmd": "/usr/sbin/nginx -t -c /etc/nginx/nginx.conf",
    "nginx_reload_cmd": "systemctl reload nginx",
    "limit_req_tem":'limit_req_zone $auth_token zone=req_zone:100m rate=?#r/m;',
    "limit_conn_tem":'limit_conn conn_zone ?#;'
};

还需要写个调用shell命令的,写配置的方法

const fs = require('fs');

const nginxconfig = require('./../config/nginxconfig');  #这个就是上面那个定义的配置文件路径
const tools = require('./tools');  # 这个方法就是打印日志,封装的是log4js 模块
const {exec,} = require('child_process');


async function shellcmd(cmd,callback,failecallback) {

    try {
        return exec(cmd, (error, stdout, stderr) => {
            if (error) {
                tools.logerror(`exec error: ${error}`);
                return failecallback()
            }
            tools.loginfo(`stdout: ${stdout}`);
            tools.loginfo(`stderr: ${stderr}`);
            return callback();
        });
    }
    catch (e) {
        tools.logerror(`exec error: ${e}`);
        return failecallback()
    }


}

async function nginx_check(callback,failecallback) {
    tools.loginfo('exec nginx check cmd');
    return await shellcmd(nginxconfig.nginx_check_cmd,callback,failecallback);
}

async function nginx_reload(callback,failecallback) {
    tools.loginfo('exec nginx reload cmd');
    return  await shellcmd(nginxconfig.nginx_reload_cmd,callback,failecallback);
}

async function back_conf(filepath,action,cllback){
    let target = filepath+'.bak';
    if(action ==='restore'){
        let tmp = filepath;
        filepath = target;
        target = tmp;
    }
    fs.copyFile(filepath,target,function (err) {
        if (err) throw err;
       tools.loginfo(filepath+' backup success');
        return cllback()
    })
}

async function write_black_ip_list(str, callback, faileback) {
    return await write_file(nginxconfig.black_ip_list, str, callback, faileback)
}

async function write_white_ip_list(str, callback, faileback) {
    return await write_file(nginxconfig.white_ip_list, str, callback, faileback)
}

async function write_white_country_list(str, callback, faileback) {
    return await write_file(nginxconfig.white_country_list, str, callback, faileback)
}

async function write_white_ip_limit_list(str, callback, faileback) {
    return await write_file(nginxconfig.white_ip_limit_list, str, callback, faileback)
}

async function write_limit_conn(str, callback, faileback) {
    str = nginxconfig.limit_conn_tem.replace("?#",str);
    return await write_file(nginxconfig.limit_conn, str, callback, faileback)
}

async function write_limit_req(str, callback, faileback) {
    str = nginxconfig.limit_req_tem.replace("?#",str);
    return await write_file(nginxconfig.limit_req, str, callback, faileback)
}


async function write_file(filepath, str, callback, faileback) {
    try {
        fs.open(filepath, 'wx', (err,fd)=> {
            if (err) {
                if (err.code !== 'EEXIST') {
                    tools.logerror(err);
                    return faileback()
                }
            }
            return  back_conf(filepath,'backup', function () {
                return fs.writeFile(filepath, str, 'utf8',  function (err) {
                    if (err) {
                        tools.logerror(err);
                        return faileback()
                    }
                    tools.loginfo(filepath+' file save success');
                    return  nginx_check( function () {
                        return  nginx_reload(function () {
                            return callback()
                        },function () {
                            tools.logerror('nginx reload failed');
                                return faileback()
                        });
                    }, function () {
                        tools.logerror('nginx check failed');
                        return  back_conf(filepath,'restore',function () {
                            return faileback()
                        });
                    })
                });
            });


        });
    } catch (e) {
        tools.logerror(e);
        return faileback()
    }

}


module.exports = {
    write_black_ip_list,
    write_white_country_list,
    write_white_ip_list,
    write_white_ip_limit_list,
    write_limit_conn,
    write_limit_req,
};

既然核心的都完事了,那就需要处理前端传的数据,保存操作吧

大体上有三种操作

  1. 保存数据到服务器和数据库

  2. 删除数据到服务器和数据库

  3. 开启关闭 【开启:相当于保存要操作的数据到服务器,并更新数据库,关闭:相当于删除要操作的数据到服务器,并更新数据库】

根据这些操作,需要写一个公共的数据操作方法

  • nginx主机黑白名单[geoip]已关闭评论
  • 47 views
    A+
发布日期:2020年10月28日  所属分类:linux  linux 工具