From a4e43b492d3a18c9af039b331410036c4de6da66 Mon Sep 17 00:00:00 2001 From: midoks Date: Wed, 16 Aug 2023 02:53:29 +0800 Subject: [PATCH] =?UTF-8?q?OP=E9=98=B2=E7=81=AB=E5=A2=99-=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E5=9C=B0=E5=8C=BA=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + plugins/op_waf/index.html | 6 +- plugins/op_waf/index.py | 140 ++++++++++++++- plugins/op_waf/info.json | 2 +- plugins/op_waf/js/op_waf.js | 243 +++++++++++++++----------- plugins/op_waf/waf/area_limit.json | 1 + plugins/op_waf/waf/lua/init.lua | 54 +++++- plugins/op_waf/waf/lua/waf_common.lua | 11 +- 8 files changed, 354 insertions(+), 105 deletions(-) create mode 100644 plugins/op_waf/waf/area_limit.json diff --git a/README.md b/README.md index 6561943c4..4df943bf5 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,10 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443: ### 版本更新 0.15.4 * OP防火墙-验证优化[cf缓存问题解决]。 +* OP防火墙-加入地区限制。 * 在aarch64架构下低于PHP70的版本[安装/扩展安装]优化。 + ### JSDelivr安装地址 - 初始安装 diff --git a/plugins/op_waf/index.html b/plugins/op_waf/index.html index 2fce86bbf..73ee75db1 100755 --- a/plugins/op_waf/index.html +++ b/plugins/op_waf/index.html @@ -256,9 +256,13 @@ + \ No newline at end of file diff --git a/plugins/op_waf/index.py b/plugins/op_waf/index.py index d07cbd756..e166342b7 100755 --- a/plugins/op_waf/index.py +++ b/plugins/op_waf/index.py @@ -1,5 +1,9 @@ # coding:utf-8 +''' +cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && bash install.sh install 0.3.2 +python3 /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf/index.py reload +''' import sys import io import os @@ -303,7 +307,7 @@ def autoMakeLuaConf(conf_reload=False, cp_reload=False): for x in conf_list: autoMakeLuaConfSingle(x, conf_reload) - import_list = ['config', 'site', 'domains'] + import_list = ['config', 'site', 'domains', 'area_limit'] for x in import_list: autoMakeLuaImportSingle(x, conf_reload) @@ -371,6 +375,29 @@ def getDefaultSite(): return mw.returnJson(True, 'OK', data) +def getCountry(): + data = ['中国大陆以外的地区(包括[中国特别行政区:港,澳,台])', '中国大陆(不包括[中国特别行政区:港,澳,台])', '中国香港', '中国澳门', '中国台湾', + '美国', '日本', '英国', '德国', '韩国', '法国', '巴西', '加拿大', '意大利', '澳大利亚', '荷兰', '俄罗斯', '印度', '瑞典', '西班牙', '墨西哥', + '比利时', '南非', '波兰', '瑞士', '阿根廷', '印度尼西亚', '埃及', '哥伦比亚', '土耳其', '越南', '挪威', '芬兰', '丹麦', '乌克兰', '奥地利', + '伊朗', '智利', '罗马尼亚', '捷克', '泰国', '沙特阿拉伯', '以色列', '新西兰', '委内瑞拉', '摩洛哥', '马来西亚', '葡萄牙', '爱尔兰', '新加坡', + '欧洲联盟', '匈牙利', '希腊', '菲律宾', '巴基斯坦', '保加利亚', '肯尼亚', '阿拉伯联合酋长国', '阿尔及利亚', '塞舌尔', '突尼斯', '秘鲁', '哈萨克斯坦', + '斯洛伐克', '斯洛文尼亚', '厄瓜多尔', '哥斯达黎加', '乌拉圭', '立陶宛', '塞尔维亚', '尼日利亚', '克罗地亚', '科威特', '巴拿马', '毛里求斯', '白俄罗斯', + '拉脱维亚', '多米尼加', '卢森堡', '爱沙尼亚', '苏丹', '格鲁吉亚', '安哥拉', '玻利维亚', '赞比亚', '孟加拉国', '巴拉圭', '波多黎各', '坦桑尼亚', + '塞浦路斯', '摩尔多瓦', '阿曼', '冰岛', '叙利亚', '卡塔尔', '波黑', '加纳', '阿塞拜疆', '马其顿', '约旦', '萨尔瓦多', '伊拉克', '亚美尼亚', '马耳他', + '危地马拉', '巴勒斯坦', '斯里兰卡', '特立尼达和多巴哥', '黎巴嫩', '尼泊尔', '纳米比亚', '巴林', '洪都拉斯', '莫桑比克', '尼加拉瓜', '卢旺达', '加蓬', + '阿尔巴尼亚', '利比亚', '吉尔吉斯坦', '柬埔寨', '古巴', '喀麦隆', '乌干达', '塞内加尔', '乌兹别克斯坦', '黑山', '关岛', '牙买加', '蒙古', '文莱', + '英属维尔京群岛', '留尼旺', '库拉索岛', '科特迪瓦', '开曼群岛', '巴巴多斯', '马达加斯加', '伯利兹', '新喀里多尼亚', '海地', '马拉维', '斐济', '巴哈马', + '博茨瓦纳', '扎伊尔', '阿富汗', '莱索托', '百慕大', '埃塞俄比亚', '美属维尔京群岛', '列支敦士登', '津巴布韦', '直布罗陀', '苏里南', '马里', '也门', + '老挝', '塔吉克斯坦', '安提瓜和巴布达', '贝宁', '法属玻利尼西亚', '圣基茨和尼维斯', '圭亚那', '布基纳法索', '马尔代夫', '泽西岛', '摩纳哥', '巴布亚新几内亚', + '刚果', '塞拉利昂', '吉布提', '斯威士兰', '缅甸', '毛里塔尼亚', '法罗群岛', '尼日尔', '安道尔', '阿鲁巴', '布隆迪', '圣马力诺', '利比里亚', + '冈比亚', '不丹', '几内亚', '圣文森特岛', '荷兰加勒比区', '圣马丁', '多哥', '格陵兰', '佛得角', '马恩岛', '索马里', '法属圭亚那', '西萨摩亚', + '土库曼斯坦', '瓜德罗普', '马里亚那群岛', '瓦努阿图', '马提尼克', '赤道几内亚', '南苏丹', '梵蒂冈', '格林纳达', '所罗门群岛', '特克斯和凯科斯群岛', '多米尼克', + '乍得', '汤加', '瑙鲁', '圣多美和普林西比', '安圭拉岛', '法属圣马丁', '图瓦卢', '库克群岛', '密克罗尼西亚联邦', '根西岛', '东帝汶', '中非', + '几内亚比绍', '帕劳', '美属萨摩亚', '厄立特里亚', '科摩罗', '圣皮埃尔和密克隆', '瓦利斯和富图纳', '英属印度洋领地', '托克劳', '马绍尔群岛', '基里巴斯', + '纽埃', '诺福克岛', '蒙特塞拉特岛', '朝鲜', '马约特', '圣卢西亚', '圣巴泰勒米岛'] + return mw.returnJson(True, 'ok', data) + + def autoMakeConfig(conf_reload=False, cp_reload=False): initDomainInfo(conf_reload) initSiteInfo(conf_reload) @@ -1288,8 +1315,111 @@ def getAreaLimit(): conf = getJsonPath('area_limit') if not os.path.exists(conf): mw.writeFile(conf, '[]') + + d = mw.readFile(conf) + data = json.loads(d) + return mw.returnJson(True, 'ok!', data) + + +def delAreaLimit(): + args = getArgs() + data = checkArgs(args, ['site', 'types', 'region']) + if not data[0]: + return data[1] + + type_list = ["refuse", "accept"] + if not args['types'] in type_list: + return mw.returnJson(False, '输入的类型错误!') + + region_l = args['region'].split(",") + site_l = args['site'].split(",") + + paramMode = {} + for i in region_l: + if not i: + continue + i = i.strip() + if not i in paramMode: + paramMode[i] = "1" + + sitesMode = {} + for i in site_l: + i = i.strip() + if not i: + continue + + if not i in sitesMode: + sitesMode[i] = "1" + + if len(paramMode) == 0: + return mw.returnJson(False, '输入的请求类型错误!') + if len(sitesMode) == 0: + return mw.returnJson(False, '输入的站点错误!') + + conf = getJsonPath('area_limit') + t_data = json.loads(mw.readFile(conf)) + + data = {"site": sitesMode, "types": args['types'], "region": paramMode} + if not data in t_data: + return mw.returnJson(False, '不存在!') + + t_data.remove(data) + mw.writeFile(conf, json.dumps(t_data)) + setConfRestartWeb() - return mw.readFile(conf) + return mw.returnJson(True, '删除成功!') + + +def addAreaLimit(): + args = getArgs() + data = checkArgs(args, ['site', 'types', 'region']) + if not data[0]: + return data[1] + + type_list = ["refuse", "accept"] + if not args['types'] in type_list: + return mw.returnJson(False, '输入的类型错误!') + + region_l = args['region'].split(",") + site_l = args['site'].split(",") + + paramMode = {} + for i in region_l: + if not i: + continue + i = i.strip() + if not i in paramMode: + paramMode[i] = "1" + + if '海外' in paramMode and '中国' in paramMode: + return mw.returnJson(False, '不允许设置【中国大陆】和【中国大陆以外地区】一同开启地区限制!') + + sitesMode = {} + for i in site_l: + i = i.strip() + if not i: + continue + + if not i in sitesMode: + sitesMode[i] = "1" + + if len(paramMode) == 0: + return mw.returnJson(False, '输入的请求类型错误!') + if len(sitesMode) == 0: + return mw.returnJson(False, '输入的站点错误!') + + conf = getJsonPath('area_limit') + t_data = json.loads(mw.readFile(conf)) + + data = {"site": sitesMode, "types": args['types'], "region": paramMode} + if data in t_data: + return mw.returnJson(False, '已存在!') + + t_data.insert(0, data) + mw.writeFile(conf, json.dumps(t_data)) + + setConfRestartWeb() + return mw.returnJson(True, '添加成功!') def cleanDropIp(): @@ -1387,6 +1517,8 @@ if __name__ == "__main__": print(getSiteConfig()) elif func == 'get_default_site': print(getDefaultSite()) + elif func == 'get_country': + print(getCountry()) elif func == 'get_site_config_byname': print(getSiteConfigByName()) elif func == 'add_site_cdn_header': @@ -1407,6 +1539,10 @@ if __name__ == "__main__": print(getWafConf()) elif func == 'get_area_limit': print(getAreaLimit()) + elif func == 'add_area_limit': + print(addAreaLimit()) + elif func == 'del_area_limit': + print(delAreaLimit()) elif func == 'clean_drop_ip': print(cleanDropIp()) elif func == 'test_run': diff --git a/plugins/op_waf/info.json b/plugins/op_waf/info.json index e2dfea921..074f6263d 100755 --- a/plugins/op_waf/info.json +++ b/plugins/op_waf/info.json @@ -24,5 +24,5 @@ "home":"https://github.com/loveshell/ngx_lua_waf", "date":"2019-04-21", "pid": "1", - "versions": ["0.3.0"] + "versions": ["0.3.2"] } \ No newline at end of file diff --git a/plugins/op_waf/js/op_waf.js b/plugins/op_waf/js/op_waf.js index 0fbf6ce34..a5b50dcf7 100755 --- a/plugins/op_waf/js/op_waf.js +++ b/plugins/op_waf/js/op_waf.js @@ -14,6 +14,19 @@ function owPost(method, args, callback){ },'json'); } +function owPostN(method, args, callback){ + $.post('/plugins/run', {name:'op_waf', func:method, args:JSON.stringify(args)}, function(data) { + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + function getRuleByName(rule_name, callback){ owPost('get_rule', {rule_name:rule_name}, function(data){ @@ -1634,48 +1647,74 @@ function wafSite(){ }); } + function wafAreaLimitRender(){ + function keyVal(obj){ + var str = []; + $.each(obj, function (index, item) { + if (item == 1) { + if (index == 'allsite') index = '所有站点'; + if (index == '海外') index = '中国大陆以外的地区(包括[港,澳,台])'; + if (index == '中国') index = '中国大陆(不包括[港,澳,台])'; + str.push(index); + } + }); + return str.toString(); + } owPost('get_area_limit', {}, function(rdata) { var rdata = $.parseJSON(rdata.data); - console.log(rdata); if (!rdata.status) { layer.msg(rdata.msg, { icon: 2, time: 2000 }); return; } - var list = ''; var rlist = rdata.data; for (var i = 0; i < rlist.length; i++) { - var docker_status = 'stop'; - var status = ''; - if (rlist[i]['State']['Status'] == 'running') { - docker_status = 'start'; - status = ''; - } - var op = ''; - op += '终端 | '; - op += '日志 | '; - op += '删除'; - - list += ''; - list += '' + rlist[i]['Name'].substring(1) + ''; - list += '' + rlist[i]['Config']['Image'] + ''; - list += '' + getFormatTime(rlist[i]['Created']) + ''; + var type = rlist[i]['types'] === 'refuse' ? '拦截' : '只放行'; + var region_str = keyVal(rlist[i]['region']); + var site_str = keyVal(rlist[i]['site']); + op += '删除'; - if (docker_status == 'start') { - list += '' + status + ''; - } else { - list += '' + status + ''; - } + list += ''; + list += '' + region_str + ''; + list += '' + site_str + ''; + list += '' + type + ''; + list += '' + op + ''; list += ''; } $('#con_list tbody').html(list); + $('.area_limit_del').click(function(){ + var data_id = $(this).data('id'); + + var site = [],region = []; + $.each(rlist[data_id]['site'], function (index, item) { + site.push(index); + }); + $.each(rlist[data_id]['region'], function (index, item) { + region.push(index); + }); + + var type = rlist[data_id]['types']; + + owPost('del_area_limit', { + site:site.toString(), + region:region.toString(), + types:type, + }, function(rdata) { + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg, function(){ + if (rdata.status){ + wafAreaLimit(); + } + },{ icon: rdata.status ? 1 : 2 }); + }); + }); }); } @@ -1699,6 +1738,9 @@ function wafAreaLimit(){ wafAreaLimitRender(); $('#create_area_limit').click(function(){ + var site_list; + var area_list; + var site_length = 0; layer.open({ type: 1, title: '添加地区限制', @@ -1709,7 +1751,7 @@ function wafAreaLimit(){
\ 类型\
\ - \ \ \ \ @@ -1718,98 +1760,103 @@ function wafAreaLimit(){
\ 站点\
\ -
\ +
\
\
\
\ 地区\ -
\ +
\
\
', success: function (layers, index) { document.getElementById('layui-layer' + index).getElementsByClassName('layui-layer-content')[0].style.overflow = 'unset'; - - var demo1 = xmSelect.render({ - el: '#demo1', + site_list = xmSelect.render({ + el: '#site_list', language: 'zn', toolbar: {show: true,}, paging: true, pageSize: 10, - data: [ - {name: '张三', value: 1}, - {name: '李四', value: 2}, - {name: '王五', value: 3}, - ] - }) - - var demo2 = xmSelect.render({ - el: '#demo2', + data: [], + }); + + owPostN('get_default_site','', function(rdata){ + var rdata = $.parseJSON(rdata.data); + var rlist = rdata.data.list; + + + var pdata = []; + for (var i = 0; i < rlist.length; i++) { + var tval = rlist[i]; + if (tval != 'unset'){ + var t = {name:rlist[i],value:rlist[i]}; + pdata.push(t); + } + } + site_length = pdata.length; + site_list.update({data:pdata}); + }); + + area_list = xmSelect.render({ + el: '#area_list', language: 'zn', toolbar: {show: true,}, - paging: true, - pageSize: 10, - data: [ - {name: '张三', value: 1}, - {name: '李四', value: 2}, - {name: '王五', value: 3}, - ] - }) - - // var robj = $('.bt-form'), - // reg_html = '', - // site_html = '', - // elName = ['multi_reg', 'multi_site']; - // $.post('/plugin?action=a&name=btwaf&s=city', function (res) { - // for (var i = 0; i < res.length; i++) { - // reg_html += '
  • ' + res[i] + '
  • '; - // } - // robj.append('
    地区
    ' + that.multi_select_view(elName[0], reg_html) + '
    '); - // that.nulti_event(elName[0], res, '地区'); - // }); - // $.post('/plugin?action=a&name=btwaf&s=reg_domains', function (res) { - // var data = []; - // for (var i = 0; i < res.length; i++) { - // data.push(res[i].name); - // site_html += '
  • ' + res[i].name + '
  • '; - // } - // site_length = data.length; - // robj.append('
    站点
    ' + that.multi_select_view(elName[1], site_html) + '
    '); - // that.nulti_event(elName[1], data, '站点'); - // }); + filterable: true, + data: [], + }); + owPostN('get_country','', function(rdata){ + var rdata = $.parseJSON(rdata.data); + var rlist = rdata.data; + + var pdata = []; + for (var i = 0; i < rlist.length; i++) { + var tval = rlist[i]; + if (tval != 'unset'){ + var t = {name:tval,value:tval}; + pdata.push(t); + } + } + + area_list.update({data:pdata}); + }); }, yes: function (indexs) { - // var region = $('.multi_reg .btn .filter-option') - // .text() - // .replace('中国大陆以外的地区(包括[中国特别行政区:港,澳,台])', '海外') - // .replace('中国大陆(不包括[中国特别行政区:港,澳,台])', '中国') - // .replace('中国香港', '香港') - // .replace('中国澳门', '澳门') - // .replace('中国台湾', '台湾'); - // var site_text = $('.multi_site .btn .filter-option').text(), - // site = ''; - // if (site_length === site.split(',').length) { - // site = 'allsite'; - // } else { - // site = site_text; - // } - // if (region.indexOf('请选择') > -1) return layer.msg('地区最少选一个!', { icon: 2 }); - // if (site.indexOf('请选择') > -1) return layer.msg('站点最少选一个!', { icon: 2 }); - // that.ajaxTask( - // 'add_reg_tions', - // { - // region: region, - // types: formData.types, - // site: site, - // }, - // function (res) { - // if (res.status) { - // layer.close(indexs); - // that.render_regional_restrictions(); - // } - // layer.msg(res.msg, { icon: res.status ? 1 : 2 }); - // } - // ); + + var reg_type = $('select[name="type"]').val(); + var site_val = site_list.getValue('value'); + var area_val = area_list.getValue('value'); + + if (area_val.length <1) return layer.msg('地区最少选一个!', { icon: 2 }); + if (site_val.length <1) return layer.msg('站点最少选一个!', { icon: 2 }); + + var site = ''; + if (site_length === site_val.length) { + site = 'allsite'; + } else { + site = site_val.join(); + } + + var area = area_val.join(); + var region = area.replace('中国大陆以外的地区(包括[中国特别行政区:港,澳,台])', '海外') + .replace('中国大陆(不包括[中国特别行政区:港,澳,台])', '中国') + .replace('中国香港', '香港') + .replace('中国澳门', '澳门') + .replace('中国台湾', '台湾'); + + owPost('add_area_limit',{ + site:site, + types:reg_type, + region:region, + }, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(indexs); + wafAreaLimit(); + } + },{ icon: rdata.status ? 1 : 2 }); + }); + }, }); }); diff --git a/plugins/op_waf/waf/area_limit.json b/plugins/op_waf/waf/area_limit.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/plugins/op_waf/waf/area_limit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/plugins/op_waf/waf/lua/init.lua b/plugins/op_waf/waf/lua/init.lua index 67a1fb4f6..8f5b509e4 100644 --- a/plugins/op_waf/waf/lua/init.lua +++ b/plugins/op_waf/waf/lua/init.lua @@ -35,6 +35,8 @@ local cookie_rules = require "rule_cookie" local url_rules = require "rule_url" local url_white_rules = require "rule_url_white" +local waf_area_limit = require "waf_area_limit" + -- local server_name = string.gsub(C:get_sn(config_domains),'_','.') local server_name = C:get_sn(config_domains) local function initParams() @@ -602,15 +604,63 @@ local function get_country() return ip_postion["country"]["names"]["zh-CN"] end +local function area_limit(overall_country, server_name, status) + + if overall_country and overall_country~="" and C:count_size(waf_area_limit)>=1 then + for k, val in pairs(waf_area_limit) do + -- C:D(tostring(k)..':'..tostring(val['site']['allsite']) ..':'.. tostring(val['site']['allsite'] == '1') ..':'.. tostring(val['site']['allsite'])) + if val['site']['allsite'] and val['site']['allsite'] == '1' and val['types'] == 'refuse' then + for rk, reg_val in pairs(val['region']) do + if rk == overall_country then + ngx.exit(403) + return true + end + end + end + + if val['site'][server_name] and val['site'][server_name] == '1' and val['types'] == 'refuse' then + for rk, reg_val in pairs(val['region']) do + if rk == overall_country then + ngx.exit(403) + return true + end + end + end + + if val['site']['allsite'] and val['site']['allsite'] == '1' and val['types'] == 'accept' then + for rk, reg_val in pairs(val['region']) do + if rk == overall_country then + return false + end + end + ngx.exit(403) + return true + end + + if val['site'][server_name] and val['site'][server_name] == '1' and val['types'] == 'accept' then + for rk, reg_val in pairs(val['region']) do + if rk == overall_country then + return false + end + end + ngx.exit(403) + return true + end + end + end + return false +end function run_app_waf() min_route() -- C:D("min_route") - + -- country limit local waf_country = get_country() - C:D(tostring(waf_country)) + if area_limit(waf_country, server_name, site_config[server_name]['open']) then return true end if site_config[server_name] and site_config[server_name]['open'] then + + -- white ip if waf_ip_white() then return true end -- C:D("waf_ip_white") diff --git a/plugins/op_waf/waf/lua/waf_common.lua b/plugins/op_waf/waf/lua/waf_common.lua index dfebf471a..97961c3b7 100644 --- a/plugins/op_waf/waf/lua/waf_common.lua +++ b/plugins/op_waf/waf/lua/waf_common.lua @@ -277,6 +277,16 @@ function _M.setParams( self, params ) self.params = params end +function _M.count_size(data) + local count=0 + if type(data)~="table" then return count end + for k,v in pairs(data) + do + count=count+1 + end + return count +end + function _M.is_min(self, ip1, ip2) n = 0 @@ -823,5 +833,4 @@ function _M.t(self) ngx.say(',,,') end - return _M