国家黑白名单通过 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内容
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, };
既然核心的都完事了,那就需要处理前端传的数据,保存操作吧
大体上有三种操作
-
保存数据到服务器和数据库
-
删除数据到服务器和数据库
-
开启关闭 【开启:相当于保存要操作的数据到服务器,并更新数据库,关闭:相当于删除要操作的数据到服务器,并更新数据库】
根据这些操作,需要写一个公共的数据操作方法